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(bool /* mute_on */) {
75 void CrasAudioHandler::AudioObserver::OnInputMuteChanged(bool /* mute_on */) {
78 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
81 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
84 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
88 void CrasAudioHandler::Initialize(
89 scoped_refptr
<AudioDevicesPrefHandler
> audio_pref_handler
) {
90 CHECK(!g_cras_audio_handler
);
91 g_cras_audio_handler
= new CrasAudioHandler(audio_pref_handler
);
95 void CrasAudioHandler::InitializeForTesting() {
96 CHECK(!g_cras_audio_handler
);
97 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
101 void CrasAudioHandler::Shutdown() {
102 CHECK(g_cras_audio_handler
);
103 delete g_cras_audio_handler
;
104 g_cras_audio_handler
= NULL
;
108 bool CrasAudioHandler::IsInitialized() {
109 return g_cras_audio_handler
!= NULL
;
113 CrasAudioHandler
* CrasAudioHandler::Get() {
114 CHECK(g_cras_audio_handler
)
115 << "CrasAudioHandler::Get() called before Initialize().";
116 return g_cras_audio_handler
;
119 void CrasAudioHandler::AddAudioObserver(AudioObserver
* observer
) {
120 observers_
.AddObserver(observer
);
123 void CrasAudioHandler::RemoveAudioObserver(AudioObserver
* observer
) {
124 observers_
.RemoveObserver(observer
);
127 bool CrasAudioHandler::HasKeyboardMic() {
128 return GetKeyboardMic() != NULL
;
131 bool CrasAudioHandler::IsOutputMuted() {
132 return output_mute_on_
;
135 bool CrasAudioHandler::IsOutputMutedForDevice(uint64_t device_id
) {
136 const AudioDevice
* device
= GetDeviceFromId(device_id
);
139 DCHECK(!device
->is_input
);
140 return audio_pref_handler_
->GetMuteValue(*device
);
143 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
144 return output_volume_
<= kMuteThresholdPercent
;
147 bool CrasAudioHandler::IsInputMuted() {
148 return input_mute_on_
;
151 bool CrasAudioHandler::IsInputMutedForDevice(uint64_t device_id
) {
152 const AudioDevice
* device
= GetDeviceFromId(device_id
);
155 DCHECK(device
->is_input
);
156 // We don't record input mute state for each device in the prefs,
157 // for any non-active input device, we assume mute is off.
158 if (device
->id
== active_input_node_id_
)
159 return input_mute_on_
;
163 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
164 return kMuteThresholdPercent
;
167 int CrasAudioHandler::GetOutputVolumePercent() {
168 return output_volume_
;
171 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64_t device_id
) {
172 if (device_id
== active_output_node_id_
) {
173 return output_volume_
;
175 const AudioDevice
* device
= GetDeviceFromId(device_id
);
176 return static_cast<int>(audio_pref_handler_
->GetOutputVolumeValue(device
));
180 int CrasAudioHandler::GetInputGainPercent() {
184 int CrasAudioHandler::GetInputGainPercentForDevice(uint64_t device_id
) {
185 if (device_id
== active_input_node_id_
) {
188 const AudioDevice
* device
= GetDeviceFromId(device_id
);
189 return static_cast<int>(audio_pref_handler_
->GetInputGainValue(device
));
193 uint64_t CrasAudioHandler::GetPrimaryActiveOutputNode() const {
194 return active_output_node_id_
;
197 uint64_t CrasAudioHandler::GetPrimaryActiveInputNode() const {
198 return active_input_node_id_
;
201 void CrasAudioHandler::GetAudioDevices(AudioDeviceList
* device_list
) const {
202 device_list
->clear();
203 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
204 it
!= audio_devices_
.end(); ++it
)
205 device_list
->push_back(it
->second
);
208 bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice
* device
) const {
209 const AudioDevice
* active_device
= GetDeviceFromId(active_output_node_id_
);
210 if (!active_device
|| !device
)
212 *device
= *active_device
;
216 void CrasAudioHandler::SetKeyboardMicActive(bool active
) {
217 const AudioDevice
* keyboard_mic
= GetKeyboardMic();
220 // Keyboard mic is invisible to chromeos users. It is always added or removed
221 // as additional active node.
222 DCHECK(active_input_node_id_
&& active_input_node_id_
!= keyboard_mic
->id
);
224 AddActiveNode(keyboard_mic
->id
, true);
226 RemoveActiveNodeInternal(keyboard_mic
->id
, true);
229 void CrasAudioHandler::AddActiveNode(uint64_t node_id
, bool notify
) {
230 const AudioDevice
* device
= GetDeviceFromId(node_id
);
232 VLOG(1) << "AddActiveInputNode: Cannot find device id="
233 << "0x" << std::hex
<< node_id
;
237 // If there is no primary active device, set |node_id| to primary active node.
238 if ((device
->is_input
&& !active_input_node_id_
) ||
239 (!device
->is_input
&& !active_output_node_id_
)) {
240 SwitchToDevice(*device
, notify
);
244 AddAdditionalActiveNode(node_id
, notify
);
247 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList
& new_active_ids
) {
248 // Flags for whether there are input or output nodes passed in from
249 // |new_active_ids|. If there are no input nodes passed in, we will not
250 // make any change for input nodes; same for the output nodes.
251 bool request_input_change
= false;
252 bool request_output_change
= false;
254 // Flags for whether we will actually change active status of input
256 bool make_input_change
= false;
257 bool make_output_change
= false;
259 NodeIdList nodes_to_activate
;
260 for (size_t i
= 0; i
< new_active_ids
.size(); ++i
) {
261 const AudioDevice
* device
= GetDeviceFromId(new_active_ids
[i
]);
263 if (device
->is_input
)
264 request_input_change
= true;
266 request_output_change
= true;
268 // If the new active device is already active, keep it as active.
272 nodes_to_activate
.push_back(new_active_ids
[i
]);
273 if (device
->is_input
)
274 make_input_change
= true;
276 make_output_change
= true;
280 // Remove all existing active devices that are not in the |new_active_ids|
282 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
283 it
!= audio_devices_
.end(); ++it
) {
284 AudioDevice device
= it
->second
;
285 // Remove the existing active input or output nodes that are not in the new
286 // active node list if there are new input or output nodes specified.
288 if ((device
.is_input
&& request_input_change
&&
289 !IsInNodeList(device
.id
, new_active_ids
))) {
290 make_input_change
= true;
291 RemoveActiveNodeInternal(device
.id
, false); // no notification.
292 } else if (!device
.is_input
&& request_output_change
&&
293 !IsInNodeList(device
.id
, new_active_ids
)) {
294 make_output_change
= true;
295 RemoveActiveNodeInternal(device
.id
, false); // no notification.
300 // Adds the new active devices.
301 for (size_t i
= 0; i
< nodes_to_activate
.size(); ++i
)
302 AddActiveNode(nodes_to_activate
[i
], false); // no notification.
304 // Notify the active nodes change now.
305 if (make_input_change
)
306 NotifyActiveNodeChanged(true);
307 if (make_output_change
)
308 NotifyActiveNodeChanged(false);
311 void CrasAudioHandler::SwapInternalSpeakerLeftRightChannel(bool swap
) {
312 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
313 it
!= audio_devices_
.end();
315 const AudioDevice
& device
= it
->second
;
316 if (!device
.is_input
&& device
.type
== AUDIO_TYPE_INTERNAL_SPEAKER
) {
317 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->SwapLeftRight(
324 bool CrasAudioHandler::has_alternative_input() const {
325 return has_alternative_input_
;
328 bool CrasAudioHandler::has_alternative_output() const {
329 return has_alternative_output_
;
332 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent
) {
333 // Set all active devices to the same volume.
334 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
335 it
!= audio_devices_
.end();
337 const AudioDevice
& device
= it
->second
;
338 if (!device
.is_input
&& device
.active
)
339 SetOutputNodeVolumePercent(device
.id
, volume_percent
);
343 // TODO: Rename the 'Percent' to something more meaningful.
344 void CrasAudioHandler::SetInputGainPercent(int gain_percent
) {
345 // TODO(jennyz): Should we set all input devices' gain to the same level?
346 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
347 it
!= audio_devices_
.end();
349 const AudioDevice
& device
= it
->second
;
350 if (device
.is_input
&& device
.active
)
351 SetInputNodeGainPercent(active_input_node_id_
, gain_percent
);
355 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent
) {
356 SetOutputVolumePercent(output_volume_
+ adjust_by_percent
);
359 void CrasAudioHandler::SetOutputMute(bool mute_on
) {
360 if (!SetOutputMuteInternal(mute_on
))
363 // Save the mute state for all active output audio devices.
364 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
365 it
!= audio_devices_
.end();
367 const AudioDevice
& device
= it
->second
;
368 if (!device
.is_input
&& device
.active
) {
369 audio_pref_handler_
->SetMuteValue(device
, output_mute_on_
);
373 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
374 OnOutputMuteChanged(output_mute_on_
));
377 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
378 if (output_volume_
<= kMuteThresholdPercent
) {
379 // Avoid the situation when sound has been unmuted, but the volume
380 // is set to a very low value, so user still can't hear any sound.
381 SetOutputVolumePercent(kDefaultUnmuteVolumePercent
);
385 void CrasAudioHandler::SetInputMute(bool mute_on
) {
386 SetInputMuteInternal(mute_on
);
387 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
388 OnInputMuteChanged(input_mute_on_
));
391 void CrasAudioHandler::SetActiveOutputNode(uint64_t node_id
, bool notify
) {
392 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
393 SetActiveOutputNode(node_id
);
395 NotifyActiveNodeChanged(false);
398 void CrasAudioHandler::SetActiveInputNode(uint64_t node_id
, bool notify
) {
399 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
400 SetActiveInputNode(node_id
);
402 NotifyActiveNodeChanged(true);
405 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64_t device_id
,
407 const AudioDevice
* device
= GetDeviceFromId(device_id
);
411 if (device
->is_input
)
412 SetInputNodeGainPercent(device_id
, value
);
414 SetOutputNodeVolumePercent(device_id
, value
);
417 void CrasAudioHandler::SetMuteForDevice(uint64_t device_id
, bool mute_on
) {
418 if (device_id
== active_output_node_id_
) {
419 SetOutputMute(mute_on
);
421 } else if (device_id
== active_input_node_id_
) {
422 VLOG(1) << "SetMuteForDevice sets active input device id="
423 << "0x" << std::hex
<< device_id
<< " mute=" << mute_on
;
424 SetInputMute(mute_on
);
428 const AudioDevice
* device
= GetDeviceFromId(device_id
);
429 // Input device's mute state is not recorded in the pref. crbug.com/365050.
430 if (device
&& !device
->is_input
)
431 audio_pref_handler_
->SetMuteValue(*device
, mute_on
);
434 void CrasAudioHandler::LogErrors() {
438 // If the HDMI device is the active output device, when the device enters/exits
439 // docking mode, or HDMI display changes resolution, or chromeos device
440 // suspends/resumes, cras will lose the HDMI output node for a short period of
441 // time, then rediscover it. This hotplug behavior will cause the audio output
442 // be leaked to the alternatvie active audio output during HDMI re-discovering
443 // period. See crbug.com/503667.
444 void CrasAudioHandler::SetActiveHDMIOutoutRediscoveringIfNecessary(
445 bool force_rediscovering
) {
446 if (!GetDeviceFromId(active_output_node_id_
))
449 // Marks the start of the HDMI re-discovering grace period, during which we
450 // will mute the audio output to prevent it to be be leaked to the
451 // alternative output device.
452 if ((hdmi_rediscovering_
&& force_rediscovering
) ||
453 (!hdmi_rediscovering_
&& IsHDMIPrimaryOutputDevice())) {
454 StartHDMIRediscoverGracePeriod();
458 CrasAudioHandler::CrasAudioHandler(
459 scoped_refptr
<AudioDevicesPrefHandler
> audio_pref_handler
)
460 : audio_pref_handler_(audio_pref_handler
),
461 output_mute_on_(false),
462 input_mute_on_(false),
465 active_output_node_id_(0),
466 active_input_node_id_(0),
467 has_alternative_input_(false),
468 has_alternative_output_(false),
469 output_mute_locked_(false),
471 hdmi_rediscover_grace_period_duration_in_ms_(
472 kHDMIRediscoverGracePeriodDurationInMs
),
473 hdmi_rediscovering_(false),
474 weak_ptr_factory_(this) {
475 if (!audio_pref_handler
.get())
477 // If the DBusThreadManager or the CrasAudioClient aren't available, there
478 // isn't much we can do. This should only happen when running tests.
479 if (!chromeos::DBusThreadManager::IsInitialized() ||
480 !chromeos::DBusThreadManager::Get() ||
481 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
483 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
484 audio_pref_handler_
->AddAudioPrefObserver(this);
485 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
486 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
489 InitializeAudioState();
492 CrasAudioHandler::~CrasAudioHandler() {
493 hdmi_rediscover_timer_
.Stop();
494 if (!chromeos::DBusThreadManager::IsInitialized() ||
495 !chromeos::DBusThreadManager::Get() ||
496 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
498 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
499 RemoveObserver(this);
500 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
501 RemoveObserver(this);
502 if (audio_pref_handler_
.get())
503 audio_pref_handler_
->RemoveAudioPrefObserver(this);
504 audio_pref_handler_
= NULL
;
507 void CrasAudioHandler::AudioClientRestarted() {
508 // Make sure the logging is enabled in case cras server
509 // restarts after crashing.
511 InitializeAudioState();
514 void CrasAudioHandler::NodesChanged() {
515 // Refresh audio nodes data.
519 void CrasAudioHandler::ActiveOutputNodeChanged(uint64_t node_id
) {
520 if (active_output_node_id_
== node_id
)
523 // Active audio output device should always be changed by chrome.
524 // During system boot, cras may change active input to unknown device 0x1,
525 // we don't need to log it, since it is not an valid device.
526 if (GetDeviceFromId(node_id
)) {
527 LOG_IF(WARNING
, log_errors_
)
528 << "Active output node changed unexpectedly by system node_id="
529 << "0x" << std::hex
<< node_id
;
533 void CrasAudioHandler::ActiveInputNodeChanged(uint64_t node_id
) {
534 if (active_input_node_id_
== node_id
)
537 // Active audio input device should always be changed by chrome.
538 // During system boot, cras may change active input to unknown device 0x2,
539 // we don't need to log it, since it is not an valid device.
540 if (GetDeviceFromId(node_id
)) {
541 LOG_IF(WARNING
, log_errors_
)
542 << "Active input node changed unexpectedly by system node_id="
543 << "0x" << std::hex
<< node_id
;
547 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
551 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
552 // Enable logging after cras server is started, which will be after
553 // EmitLoginPromptVisible.
557 const AudioDevice
* CrasAudioHandler::GetDeviceFromId(uint64_t device_id
) const {
558 AudioDeviceMap::const_iterator it
= audio_devices_
.find(device_id
);
559 if (it
== audio_devices_
.end())
562 return &(it
->second
);
565 const AudioDevice
* CrasAudioHandler::GetKeyboardMic() const {
566 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
567 it
!= audio_devices_
.end(); it
++) {
568 if (it
->second
.is_input
&& it
->second
.type
== AUDIO_TYPE_KEYBOARD_MIC
)
569 return &(it
->second
);
574 void CrasAudioHandler::SetupAudioInputState() {
575 // Set the initial audio state to the ones read from audio prefs.
576 const AudioDevice
* device
= GetDeviceFromId(active_input_node_id_
);
578 LOG_IF(ERROR
, log_errors_
)
579 << "Can't set up audio state for unknown input device id ="
580 << "0x" << std::hex
<< active_input_node_id_
;
583 input_gain_
= audio_pref_handler_
->GetInputGainValue(device
);
584 VLOG(1) << "SetupAudioInputState for active device id="
585 << "0x" << std::hex
<< device
->id
<< " mute=" << input_mute_on_
;
586 SetInputMuteInternal(input_mute_on_
);
587 // TODO(rkc,jennyz): Set input gain once we decide on how to store
588 // the gain values since the range and step are both device specific.
591 void CrasAudioHandler::SetupAudioOutputState() {
592 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
594 LOG_IF(ERROR
, log_errors_
)
595 << "Can't set up audio state for unknown output device id ="
596 << "0x" << std::hex
<< active_output_node_id_
;
599 DCHECK(!device
->is_input
);
600 // Mute the output during HDMI re-discovering grace period.
601 if (hdmi_rediscovering_
&& !IsHDMIPrimaryOutputDevice()) {
602 VLOG(1) << "Mute the output during HDMI re-discovering grace period";
603 output_mute_on_
= true;
605 output_mute_on_
= audio_pref_handler_
->GetMuteValue(*device
);
607 output_volume_
= audio_pref_handler_
->GetOutputVolumeValue(device
);
609 SetOutputMuteInternal(output_mute_on_
);
610 SetOutputNodeVolume(active_output_node_id_
, output_volume_
);
613 // This sets up the state of an additional active node.
614 void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64_t node_id
) {
615 const AudioDevice
* device
= GetDeviceFromId(node_id
);
617 VLOG(1) << "Can't set up audio state for unknown device id ="
618 << "0x" << std::hex
<< node_id
;
622 DCHECK(node_id
!= active_output_node_id_
&& node_id
!= active_input_node_id_
);
624 // Note: The mute state is a system wide state, we don't set mute per device,
625 // but just keep the mute state consistent for the active node in prefs.
626 // The output volume should be set to the same value for all active output
627 // devices. For input devices, we don't restore their gain value so far.
628 // TODO(jennyz): crbug.com/417418, track the status for the decison if
629 // we should persist input gain value in prefs.
630 if (!device
->is_input
) {
631 audio_pref_handler_
->SetMuteValue(*device
, IsOutputMuted());
632 SetOutputNodeVolumePercent(node_id
, GetOutputVolumePercent());
636 void CrasAudioHandler::InitializeAudioState() {
641 void CrasAudioHandler::ApplyAudioPolicy() {
642 output_mute_locked_
= false;
643 if (!audio_pref_handler_
->GetAudioOutputAllowedValue()) {
644 // Mute the device, but do not update the preference.
645 SetOutputMuteInternal(true);
646 output_mute_locked_
= true;
648 // Restore the mute state.
649 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
651 SetOutputMuteInternal(audio_pref_handler_
->GetMuteValue(*device
));
654 // Policy for audio input is handled by kAudioCaptureAllowed in the Chrome
658 void CrasAudioHandler::SetOutputNodeVolume(uint64_t node_id
, int volume
) {
659 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
660 SetOutputNodeVolume(node_id
, volume
);
663 void CrasAudioHandler::SetOutputNodeVolumePercent(uint64_t node_id
,
664 int volume_percent
) {
665 const AudioDevice
* device
= this->GetDeviceFromId(node_id
);
666 if (!device
|| device
->is_input
)
669 volume_percent
= min(max(volume_percent
, 0), 100);
670 if (volume_percent
<= kMuteThresholdPercent
)
672 if (node_id
== active_output_node_id_
)
673 output_volume_
= volume_percent
;
675 audio_pref_handler_
->SetVolumeGainValue(*device
, volume_percent
);
677 if (device
->active
) {
678 SetOutputNodeVolume(node_id
, volume_percent
);
679 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
680 OnOutputNodeVolumeChanged(node_id
, volume_percent
));
684 bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on
) {
685 if (output_mute_locked_
)
688 output_mute_on_
= mute_on
;
689 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
690 SetOutputUserMute(mute_on
);
694 void CrasAudioHandler::SetInputNodeGain(uint64_t node_id
, int gain
) {
695 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
696 SetInputNodeGain(node_id
, gain
);
699 void CrasAudioHandler::SetInputNodeGainPercent(uint64_t node_id
,
701 const AudioDevice
* device
= GetDeviceFromId(node_id
);
702 if (!device
|| !device
->is_input
)
705 // NOTE: We do not sanitize input gain values since the range is completely
706 // dependent on the device.
707 if (active_input_node_id_
== node_id
)
708 input_gain_
= gain_percent
;
710 audio_pref_handler_
->SetVolumeGainValue(*device
, gain_percent
);
712 if (device
->active
) {
713 SetInputNodeGain(node_id
, gain_percent
);
714 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
715 OnInputNodeGainChanged(node_id
, gain_percent
));
719 void CrasAudioHandler::SetInputMuteInternal(bool mute_on
) {
720 input_mute_on_
= mute_on
;
721 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
722 SetInputMute(mute_on
);
725 void CrasAudioHandler::GetNodes() {
726 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
727 base::Bind(&CrasAudioHandler::HandleGetNodes
,
728 weak_ptr_factory_
.GetWeakPtr()),
729 base::Bind(&CrasAudioHandler::HandleGetNodesError
,
730 weak_ptr_factory_
.GetWeakPtr()));
733 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice
& new_active_device
,
734 uint64_t* current_active_node_id
) {
735 // If the device we want to switch to is already the current active device,
737 if (new_active_device
.active
&&
738 new_active_device
.id
== *current_active_node_id
) {
742 // Reset all other input or output devices' active status. The active audio
743 // device from the previous user session can be remembered by cras, but not
744 // in chrome. see crbug.com/273271.
745 for (AudioDeviceMap::iterator it
= audio_devices_
.begin();
746 it
!= audio_devices_
.end(); ++it
) {
747 if (it
->second
.is_input
== new_active_device
.is_input
&&
748 it
->second
.id
!= new_active_device
.id
)
749 it
->second
.active
= false;
752 // Set the current active input/output device to the new_active_device.
753 *current_active_node_id
= new_active_device
.id
;
754 audio_devices_
[*current_active_node_id
].active
= true;
758 bool CrasAudioHandler::NonActiveDeviceUnplugged(size_t old_devices_size
,
759 size_t new_devices_size
,
760 uint64_t current_active_node
) {
761 return (new_devices_size
< old_devices_size
&&
762 GetDeviceFromId(current_active_node
));
765 void CrasAudioHandler::SwitchToDevice(const AudioDevice
& device
, bool notify
) {
766 if (device
.is_input
) {
767 if (!ChangeActiveDevice(device
, &active_input_node_id_
))
769 SetupAudioInputState();
770 SetActiveInputNode(active_input_node_id_
, notify
);
772 if (!ChangeActiveDevice(device
, &active_output_node_id_
))
774 SetupAudioOutputState();
775 SetActiveOutputNode(active_output_node_id_
, notify
);
779 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList
& new_nodes
,
781 AudioNodeList
* new_discovered
) {
782 size_t num_old_devices
= 0;
783 size_t num_new_devices
= 0;
784 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
785 it
!= audio_devices_
.end(); ++it
) {
786 if (is_input
== it
->second
.is_input
)
790 bool new_or_changed_device
= false;
791 new_discovered
->clear();
792 for (AudioNodeList::const_iterator it
= new_nodes
.begin();
793 it
!= new_nodes
.end(); ++it
) {
794 if (is_input
== it
->is_input
) {
796 // Look to see if the new device not in the old device list.
797 AudioDevice
device(*it
);
798 DeviceStatus status
= CheckDeviceStatus(device
);
799 if (status
== NEW_DEVICE
)
800 new_discovered
->push_back(*it
);
801 if (status
== NEW_DEVICE
|| status
== CHANGED_DEVICE
) {
802 new_or_changed_device
= true;
806 return new_or_changed_device
|| (num_old_devices
!= num_new_devices
);
809 CrasAudioHandler::DeviceStatus
CrasAudioHandler::CheckDeviceStatus(
810 const AudioDevice
& device
) {
811 const AudioDevice
* device_found
= GetDeviceFromId(device
.id
);
815 if (!IsSameAudioDevice(device
, *device_found
)) {
816 LOG(WARNING
) << "Different Audio devices with same id:"
817 << " new device: " << device
.ToString()
818 << " old device: " << device_found
->ToString();
819 return CHANGED_DEVICE
;
820 } else if (device
.active
!= device_found
->active
) {
821 return CHANGED_DEVICE
;
827 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input
) {
829 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnActiveInputNodeChanged());
831 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnActiveOutputNodeChanged());
834 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
835 const AudioNodeList
& nodes
) {
836 size_t old_output_device_size
= 0;
837 size_t old_input_device_size
= 0;
838 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
839 it
!= audio_devices_
.end(); ++it
) {
840 if (it
->second
.is_input
)
841 ++old_input_device_size
;
843 ++old_output_device_size
;
846 AudioNodeList hotplug_output_nodes
;
847 AudioNodeList hotplug_input_nodes
;
848 bool output_devices_changed
=
849 HasDeviceChange(nodes
, false, &hotplug_output_nodes
);
850 bool input_devices_changed
=
851 HasDeviceChange(nodes
, true, &hotplug_input_nodes
);
852 audio_devices_
.clear();
853 has_alternative_input_
= false;
854 has_alternative_output_
= false;
856 while (!input_devices_pq_
.empty())
857 input_devices_pq_
.pop();
858 while (!output_devices_pq_
.empty())
859 output_devices_pq_
.pop();
861 size_t new_output_device_size
= 0;
862 size_t new_input_device_size
= 0;
863 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
864 AudioDevice
device(nodes
[i
]);
865 audio_devices_
[device
.id
] = device
;
867 if (!has_alternative_input_
&&
869 device
.type
!= AUDIO_TYPE_INTERNAL_MIC
&&
870 device
.type
!= AUDIO_TYPE_KEYBOARD_MIC
) {
871 has_alternative_input_
= true;
872 } else if (!has_alternative_output_
&&
874 device
.type
!= AUDIO_TYPE_INTERNAL_SPEAKER
) {
875 has_alternative_output_
= true;
878 if (device
.is_input
) {
879 input_devices_pq_
.push(device
);
880 ++new_input_device_size
;
882 output_devices_pq_
.push(device
);
883 ++new_output_device_size
;
887 // If the previous active device is removed from the new node list,
888 // or changed to inactive by cras, reset active_output_node_id_.
889 // See crbug.com/478968.
890 const AudioDevice
* active_output
= GetDeviceFromId(active_output_node_id_
);
891 if (!active_output
|| !active_output
->active
)
892 active_output_node_id_
= 0;
893 const AudioDevice
* active_input
= GetDeviceFromId(active_input_node_id_
);
894 if (!active_input
|| !active_input
->active
)
895 active_input_node_id_
= 0;
897 // If audio nodes change is caused by unplugging some non-active audio
898 // devices, the previously set active audio device will stay active.
899 // Otherwise, switch to a new active audio device according to their priority.
900 if (input_devices_changed
&&
901 !NonActiveDeviceUnplugged(old_input_device_size
,
902 new_input_device_size
,
903 active_input_node_id_
)) {
904 // Some devices like chromeboxes don't have the internal audio input. In
905 // that case the active input node id should be reset.
906 if (input_devices_pq_
.empty()) {
907 active_input_node_id_
= 0;
908 NotifyActiveNodeChanged(true);
910 // If user has hot plugged a new node, we should change to the active
911 // device to the new node if it has the highest priority; otherwise,
912 // we should keep the existing active node chosen by user.
913 // For all other cases, we will choose the node with highest priority.
914 if (!active_input_node_id_
|| hotplug_input_nodes
.empty() ||
915 IsNodeInTheList(input_devices_pq_
.top().id
, hotplug_input_nodes
)) {
916 SwitchToDevice(input_devices_pq_
.top(), true);
920 if (output_devices_changed
&&
921 !NonActiveDeviceUnplugged(old_output_device_size
,
922 new_output_device_size
,
923 active_output_node_id_
)) {
924 // This is really unlikely to happen because all ChromeOS devices have the
925 // internal audio output.
926 if (output_devices_pq_
.empty()) {
927 active_output_node_id_
= 0;
928 NotifyActiveNodeChanged(false);
930 // ditto input node case.
931 if (!active_output_node_id_
|| hotplug_output_nodes
.empty() ||
932 IsNodeInTheList(output_devices_pq_
.top().id
, hotplug_output_nodes
)) {
933 SwitchToDevice(output_devices_pq_
.top(), true);
939 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList
& node_list
,
942 LOG_IF(ERROR
, log_errors_
) << "Failed to retrieve audio nodes data";
946 UpdateDevicesAndSwitchActive(node_list
);
947 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnAudioNodesChanged());
950 void CrasAudioHandler::HandleGetNodesError(const std::string
& error_name
,
951 const std::string
& error_msg
) {
952 LOG_IF(ERROR
, log_errors_
) << "Failed to call GetNodes: "
953 << error_name
<< ": " << error_msg
;
956 void CrasAudioHandler::AddAdditionalActiveNode(uint64_t node_id
, bool notify
) {
957 const AudioDevice
* device
= GetDeviceFromId(node_id
);
959 VLOG(1) << "AddActiveInputNode: Cannot find device id="
960 << "0x" << std::hex
<< node_id
;
964 audio_devices_
[node_id
].active
= true;
965 SetupAdditionalActiveAudioNodeState(node_id
);
967 if (device
->is_input
) {
968 DCHECK(node_id
!= active_input_node_id_
);
969 chromeos::DBusThreadManager::Get()
970 ->GetCrasAudioClient()
971 ->AddActiveInputNode(node_id
);
973 NotifyActiveNodeChanged(true);
975 DCHECK(node_id
!= active_output_node_id_
);
976 chromeos::DBusThreadManager::Get()
977 ->GetCrasAudioClient()
978 ->AddActiveOutputNode(node_id
);
980 NotifyActiveNodeChanged(false);
984 void CrasAudioHandler::RemoveActiveNodeInternal(uint64_t node_id
, bool notify
) {
985 const AudioDevice
* device
= GetDeviceFromId(node_id
);
987 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
988 << "0x" << std::hex
<< node_id
;
992 audio_devices_
[node_id
].active
= false;
993 if (device
->is_input
) {
994 if (node_id
== active_input_node_id_
)
995 active_input_node_id_
= 0;
996 chromeos::DBusThreadManager::Get()
997 ->GetCrasAudioClient()
998 ->RemoveActiveInputNode(node_id
);
1000 NotifyActiveNodeChanged(true);
1002 if (node_id
== active_output_node_id_
)
1003 active_output_node_id_
= 0;
1004 chromeos::DBusThreadManager::Get()
1005 ->GetCrasAudioClient()
1006 ->RemoveActiveOutputNode(node_id
);
1008 NotifyActiveNodeChanged(false);
1012 void CrasAudioHandler::UpdateAudioAfterHDMIRediscoverGracePeriod() {
1013 VLOG(1) << "HDMI output re-discover grace period ends.";
1014 hdmi_rediscovering_
= false;
1015 if (!IsOutputMutedForDevice(active_output_node_id_
)) {
1016 // Unmute the audio output after the HDMI transition period.
1017 VLOG(1) << "Unmute output after HDMI rediscovring grace period.";
1018 SetOutputMuteInternal(false);
1022 bool CrasAudioHandler::IsHDMIPrimaryOutputDevice() const {
1023 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
1024 return (device
&& device
->type
== chromeos::AUDIO_TYPE_HDMI
);
1027 void CrasAudioHandler::StartHDMIRediscoverGracePeriod() {
1028 VLOG(1) << "Start HDMI rediscovering grace period.";
1029 hdmi_rediscovering_
= true;
1030 hdmi_rediscover_timer_
.Stop();
1031 hdmi_rediscover_timer_
.Start(
1032 FROM_HERE
, base::TimeDelta::FromMilliseconds(
1033 hdmi_rediscover_grace_period_duration_in_ms_
),
1034 this, &CrasAudioHandler::UpdateAudioAfterHDMIRediscoverGracePeriod
);
1037 void CrasAudioHandler::SetHDMIRediscoverGracePeriodForTesting(
1038 int duration_in_ms
) {
1039 hdmi_rediscover_grace_period_duration_in_ms_
= duration_in_ms
;
1042 } // namespace chromeos