Fix crash in SpeechRecognizerImpl introduced in AudioParams refactor.
[chromium-blink-merge.git] / chromeos / audio / cras_audio_handler.cc
blob8ee6009f35c69e6ee1ec3070716b5bf75654fd30
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/audio/cras_audio_handler.h"
7 #include <algorithm>
8 #include <cmath>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "chromeos/audio/audio_devices_pref_handler.h"
14 #include "chromeos/audio/audio_devices_pref_handler_stub.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
17 using std::max;
18 using std::min;
20 namespace chromeos {
22 namespace {
24 // Default value for unmuting, as a percent in the range [0, 100].
25 // Used when sound is unmuted, but volume was less than kMuteThresholdPercent.
26 const int kDefaultUnmuteVolumePercent = 4;
28 // Volume value which should be considered as muted in range [0, 100].
29 const int kMuteThresholdPercent = 1;
31 // 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)
49 return true;
51 return false;
54 } // namespace
56 CrasAudioHandler::AudioObserver::AudioObserver() {
59 CrasAudioHandler::AudioObserver::~AudioObserver() {
62 void CrasAudioHandler::AudioObserver::OnOutputNodeVolumeChanged(
63 uint64_t /* node_id */,
64 int /* volume */) {
67 void CrasAudioHandler::AudioObserver::OnInputNodeGainChanged(
68 uint64_t /* node_id */,
69 int /* gain */) {
72 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged(
73 bool /* mute_on */,
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() {
88 // static
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);
95 // static
96 void CrasAudioHandler::InitializeForTesting() {
97 CHECK(!g_cras_audio_handler);
98 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
101 // static
102 void CrasAudioHandler::Shutdown() {
103 CHECK(g_cras_audio_handler);
104 delete g_cras_audio_handler;
105 g_cras_audio_handler = NULL;
108 // static
109 bool CrasAudioHandler::IsInitialized() {
110 return g_cras_audio_handler != NULL;
113 // static
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);
138 if (!device)
139 return false;
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);
154 if (!device)
155 return false;
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_;
161 return false;
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_;
175 } else {
176 const AudioDevice* device = GetDeviceFromId(device_id);
177 return static_cast<int>(audio_pref_handler_->GetOutputVolumeValue(device));
181 int CrasAudioHandler::GetInputGainPercent() {
182 return input_gain_;
185 int CrasAudioHandler::GetInputGainPercentForDevice(uint64_t device_id) {
186 if (device_id == active_input_node_id_) {
187 return input_gain_;
188 } else {
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)
212 return false;
213 *device = *active_device;
214 return true;
217 void CrasAudioHandler::SetKeyboardMicActive(bool active) {
218 const AudioDevice* keyboard_mic = GetKeyboardMic();
219 if (!keyboard_mic)
220 return;
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);
224 if (active)
225 AddActiveNode(keyboard_mic->id, true);
226 else
227 RemoveActiveNodeInternal(keyboard_mic->id, true);
230 void CrasAudioHandler::AddActiveNode(uint64_t node_id, bool notify) {
231 const AudioDevice* device = GetDeviceFromId(node_id);
232 if (!device) {
233 VLOG(1) << "AddActiveInputNode: Cannot find device id="
234 << "0x" << std::hex << node_id;
235 return;
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);
242 return;
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
256 // or output nodes.
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]);
263 if (device) {
264 if (device->is_input)
265 request_input_change = true;
266 else
267 request_output_change = true;
269 // If the new active device is already active, keep it as active.
270 if (device->active)
271 continue;
273 nodes_to_activate.push_back(new_active_ids[i]);
274 if (device->is_input)
275 make_input_change = true;
276 else
277 make_output_change = true;
281 // Remove all existing active devices that are not in the |new_active_ids|
282 // list.
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.
288 if (device.active) {
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();
315 ++it) {
316 const AudioDevice& device = it->second;
317 if (!device.is_input && device.type == AUDIO_TYPE_INTERNAL_SPEAKER) {
318 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->SwapLeftRight(
319 device.id, swap);
320 break;
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();
337 it++) {
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();
349 it++) {
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))
362 return;
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();
367 it++) {
368 const AudioDevice& device = it->second;
369 if (!device.is_input && device.active) {
370 audio_pref_handler_->SetMuteValue(device, output_mute_on_);
374 FOR_EACH_OBSERVER(
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);
396 if (notify)
397 NotifyActiveNodeChanged(false);
400 void CrasAudioHandler::SetActiveInputNode(uint64_t node_id, bool notify) {
401 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
402 SetActiveInputNode(node_id);
403 if (notify)
404 NotifyActiveNodeChanged(true);
407 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64_t device_id,
408 int value) {
409 const AudioDevice* device = GetDeviceFromId(device_id);
410 if (!device)
411 return;
413 if (device->is_input)
414 SetInputNodeGainPercent(device_id, value);
415 else
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);
422 return;
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);
427 return;
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() {
437 log_errors_ = true;
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_))
449 return;
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),
465 output_volume_(0),
466 input_gain_(0),
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),
472 log_errors_(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())
478 return;
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())
484 return;
485 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
486 audio_pref_handler_->AddAudioPrefObserver(this);
487 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
488 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
489 AddObserver(this);
491 InitializeAudioState();
494 CrasAudioHandler::~CrasAudioHandler() {
495 hdmi_rediscover_timer_.Stop();
496 if (!chromeos::DBusThreadManager::IsInitialized() ||
497 !chromeos::DBusThreadManager::Get() ||
498 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
499 return;
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.
512 LogErrors();
513 InitializeAudioState();
516 void CrasAudioHandler::NodesChanged() {
517 // Refresh audio nodes data.
518 GetNodes();
521 void CrasAudioHandler::ActiveOutputNodeChanged(uint64_t node_id) {
522 if (active_output_node_id_ == node_id)
523 return;
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)
537 return;
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() {
550 ApplyAudioPolicy();
553 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
554 // Enable logging after cras server is started, which will be after
555 // EmitLoginPromptVisible.
556 LogErrors();
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())
562 return NULL;
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);
573 return NULL;
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_);
579 if (!device) {
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_;
583 return;
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_);
595 if (!device) {
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_;
599 return;
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;
606 } else {
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);
618 if (!device) {
619 VLOG(1) << "Can't set up audio state for unknown device id ="
620 << "0x" << std::hex << node_id;
621 return;
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() {
639 ApplyAudioPolicy();
640 GetNodes();
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;
649 } else {
650 // Restore the mute state.
651 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
652 if (device)
653 SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
656 // Policy for audio input is handled by kAudioCaptureAllowed in the Chrome
657 // media system.
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)
669 return;
671 volume_percent = min(max(volume_percent, 0), 100);
672 if (volume_percent <= kMuteThresholdPercent)
673 volume_percent = 0;
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_)
688 return false;
690 output_mute_on_ = mute_on;
691 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
692 SetOutputUserMute(mute_on);
693 return true;
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,
702 int gain_percent) {
703 const AudioDevice* device = GetDeviceFromId(node_id);
704 if (!device || !device->is_input)
705 return;
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,
738 // do nothing.
739 if (new_active_device.active &&
740 new_active_device.id == *current_active_node_id) {
741 return false;
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;
757 return 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_))
770 return;
771 SetupAudioInputState();
772 SetActiveInputNode(active_input_node_id_, notify);
773 } else {
774 if (!ChangeActiveDevice(device, &active_output_node_id_))
775 return;
776 SetupAudioOutputState();
777 SetActiveOutputNode(active_output_node_id_, notify);
781 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
782 bool is_input,
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)
789 ++num_old_devices;
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) {
797 ++num_new_devices;
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);
814 if (!device_found)
815 return NEW_DEVICE;
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;
826 return OLD_DEVICE;
829 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input) {
830 if (is_input)
831 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
832 else
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;
844 else
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_ &&
870 device.is_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_ &&
875 !device.is_input &&
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;
883 } else {
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);
911 } else {
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);
931 } else {
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,
942 bool success) {
943 if (!success) {
944 LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data";
945 return;
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);
960 if (!device) {
961 VLOG(1) << "AddActiveInputNode: Cannot find device id="
962 << "0x" << std::hex << node_id;
963 return;
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);
974 if (notify)
975 NotifyActiveNodeChanged(true);
976 } else {
977 DCHECK(node_id != active_output_node_id_);
978 chromeos::DBusThreadManager::Get()
979 ->GetCrasAudioClient()
980 ->AddActiveOutputNode(node_id);
981 if (notify)
982 NotifyActiveNodeChanged(false);
986 void CrasAudioHandler::RemoveActiveNodeInternal(uint64_t node_id, bool notify) {
987 const AudioDevice* device = GetDeviceFromId(node_id);
988 if (!device) {
989 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
990 << "0x" << std::hex << node_id;
991 return;
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);
1001 if (notify)
1002 NotifyActiveNodeChanged(true);
1003 } else {
1004 if (node_id == active_output_node_id_)
1005 active_output_node_id_ = 0;
1006 chromeos::DBusThreadManager::Get()
1007 ->GetCrasAudioClient()
1008 ->RemoveActiveOutputNode(node_id);
1009 if (notify)
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.
1023 FOR_EACH_OBSERVER(
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