SupervisedUserSyncService cleanup: Implement SigninManagerBase::Observer
[chromium-blink-merge.git] / chromeos / audio / cras_audio_handler.cc
blob5cf17eebf937c0698d30bd8d772927efc0d092ff
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 static CrasAudioHandler* g_cras_audio_handler = NULL;
33 bool IsSameAudioDevice(const AudioDevice& a, const AudioDevice& b) {
34 return a.id == b.id && a.is_input == b.is_input && a.type == b.type
35 && a.device_name == b.device_name;
38 bool IsInNodeList(uint64_t node_id,
39 const CrasAudioHandler::NodeIdList& id_list) {
40 return std::find(id_list.begin(), id_list.end(), node_id) != id_list.end();
43 bool IsNodeInTheList(uint64_t node_id, const AudioNodeList& node_list) {
44 for (size_t i = 0; i < node_list.size(); ++i) {
45 if (node_id == node_list[i].id)
46 return true;
48 return false;
51 } // namespace
53 CrasAudioHandler::AudioObserver::AudioObserver() {
56 CrasAudioHandler::AudioObserver::~AudioObserver() {
59 void CrasAudioHandler::AudioObserver::OnOutputNodeVolumeChanged(
60 uint64_t /* node_id */,
61 int /* volume */) {
64 void CrasAudioHandler::AudioObserver::OnInputNodeGainChanged(
65 uint64_t /* node_id */,
66 int /* gain */) {
69 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
72 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
75 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
78 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
81 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
84 // static
85 void CrasAudioHandler::Initialize(
86 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) {
87 CHECK(!g_cras_audio_handler);
88 g_cras_audio_handler = new CrasAudioHandler(audio_pref_handler);
91 // static
92 void CrasAudioHandler::InitializeForTesting() {
93 CHECK(!g_cras_audio_handler);
94 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
97 // static
98 void CrasAudioHandler::Shutdown() {
99 CHECK(g_cras_audio_handler);
100 delete g_cras_audio_handler;
101 g_cras_audio_handler = NULL;
104 // static
105 bool CrasAudioHandler::IsInitialized() {
106 return g_cras_audio_handler != NULL;
109 // static
110 CrasAudioHandler* CrasAudioHandler::Get() {
111 CHECK(g_cras_audio_handler)
112 << "CrasAudioHandler::Get() called before Initialize().";
113 return g_cras_audio_handler;
116 void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
117 observers_.AddObserver(observer);
120 void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
121 observers_.RemoveObserver(observer);
124 bool CrasAudioHandler::HasKeyboardMic() {
125 return GetKeyboardMic() != NULL;
128 bool CrasAudioHandler::IsOutputMuted() {
129 return output_mute_on_;
132 bool CrasAudioHandler::IsOutputMutedForDevice(uint64_t device_id) {
133 const AudioDevice* device = GetDeviceFromId(device_id);
134 if (!device)
135 return false;
136 DCHECK(!device->is_input);
137 return audio_pref_handler_->GetMuteValue(*device);
140 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
141 return output_volume_ <= kMuteThresholdPercent;
144 bool CrasAudioHandler::IsInputMuted() {
145 return input_mute_on_;
148 bool CrasAudioHandler::IsInputMutedForDevice(uint64_t device_id) {
149 const AudioDevice* device = GetDeviceFromId(device_id);
150 if (!device)
151 return false;
152 DCHECK(device->is_input);
153 // We don't record input mute state for each device in the prefs,
154 // for any non-active input device, we assume mute is off.
155 if (device->id == active_input_node_id_)
156 return input_mute_on_;
157 return false;
160 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
161 return kMuteThresholdPercent;
164 int CrasAudioHandler::GetOutputVolumePercent() {
165 return output_volume_;
168 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64_t device_id) {
169 if (device_id == active_output_node_id_) {
170 return output_volume_;
171 } else {
172 const AudioDevice* device = GetDeviceFromId(device_id);
173 return static_cast<int>(audio_pref_handler_->GetOutputVolumeValue(device));
177 int CrasAudioHandler::GetInputGainPercent() {
178 return input_gain_;
181 int CrasAudioHandler::GetInputGainPercentForDevice(uint64_t device_id) {
182 if (device_id == active_input_node_id_) {
183 return input_gain_;
184 } else {
185 const AudioDevice* device = GetDeviceFromId(device_id);
186 return static_cast<int>(audio_pref_handler_->GetInputGainValue(device));
190 uint64_t CrasAudioHandler::GetPrimaryActiveOutputNode() const {
191 return active_output_node_id_;
194 uint64_t CrasAudioHandler::GetPrimaryActiveInputNode() const {
195 return active_input_node_id_;
198 void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
199 device_list->clear();
200 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
201 it != audio_devices_.end(); ++it)
202 device_list->push_back(it->second);
205 bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice* device) const {
206 const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_);
207 if (!active_device || !device)
208 return false;
209 *device = *active_device;
210 return true;
213 void CrasAudioHandler::SetKeyboardMicActive(bool active) {
214 const AudioDevice* keyboard_mic = GetKeyboardMic();
215 if (!keyboard_mic)
216 return;
217 // Keyboard mic is invisible to chromeos users. It is always added or removed
218 // as additional active node.
219 DCHECK(active_input_node_id_ && active_input_node_id_ != keyboard_mic->id);
220 if (active)
221 AddActiveNode(keyboard_mic->id, true);
222 else
223 RemoveActiveNodeInternal(keyboard_mic->id, true);
226 void CrasAudioHandler::AddActiveNode(uint64_t node_id, bool notify) {
227 const AudioDevice* device = GetDeviceFromId(node_id);
228 if (!device) {
229 VLOG(1) << "AddActiveInputNode: Cannot find device id="
230 << "0x" << std::hex << node_id;
231 return;
234 // If there is no primary active device, set |node_id| to primary active node.
235 if ((device->is_input && !active_input_node_id_) ||
236 (!device->is_input && !active_output_node_id_)) {
237 SwitchToDevice(*device, notify);
238 return;
241 AddAdditionalActiveNode(node_id, notify);
244 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList& new_active_ids) {
245 // Flags for whether there are input or output nodes passed in from
246 // |new_active_ids|. If there are no input nodes passed in, we will not
247 // make any change for input nodes; same for the output nodes.
248 bool request_input_change = false;
249 bool request_output_change = false;
251 // Flags for whether we will actually change active status of input
252 // or output nodes.
253 bool make_input_change = false;
254 bool make_output_change = false;
256 NodeIdList nodes_to_activate;
257 for (size_t i = 0; i < new_active_ids.size(); ++i) {
258 const AudioDevice* device = GetDeviceFromId(new_active_ids[i]);
259 if (device) {
260 if (device->is_input)
261 request_input_change = true;
262 else
263 request_output_change = true;
265 // If the new active device is already active, keep it as active.
266 if (device->active)
267 continue;
269 nodes_to_activate.push_back(new_active_ids[i]);
270 if (device->is_input)
271 make_input_change = true;
272 else
273 make_output_change = true;
277 // Remove all existing active devices that are not in the |new_active_ids|
278 // list.
279 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
280 it != audio_devices_.end(); ++it) {
281 AudioDevice device = it->second;
282 // Remove the existing active input or output nodes that are not in the new
283 // active node list if there are new input or output nodes specified.
284 if (device.active) {
285 if ((device.is_input && request_input_change &&
286 !IsInNodeList(device.id, new_active_ids))) {
287 make_input_change = true;
288 RemoveActiveNodeInternal(device.id, false); // no notification.
289 } else if (!device.is_input && request_output_change &&
290 !IsInNodeList(device.id, new_active_ids)) {
291 make_output_change = true;
292 RemoveActiveNodeInternal(device.id, false); // no notification.
297 // Adds the new active devices.
298 for (size_t i = 0; i < nodes_to_activate.size(); ++i)
299 AddActiveNode(nodes_to_activate[i], false); // no notification.
301 // Notify the active nodes change now.
302 if (make_input_change)
303 NotifyActiveNodeChanged(true);
304 if (make_output_change)
305 NotifyActiveNodeChanged(false);
308 void CrasAudioHandler::SwapInternalSpeakerLeftRightChannel(bool swap) {
309 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
310 it != audio_devices_.end();
311 ++it) {
312 const AudioDevice& device = it->second;
313 if (!device.is_input && device.type == AUDIO_TYPE_INTERNAL_SPEAKER) {
314 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->SwapLeftRight(
315 device.id, swap);
316 break;
321 bool CrasAudioHandler::has_alternative_input() const {
322 return has_alternative_input_;
325 bool CrasAudioHandler::has_alternative_output() const {
326 return has_alternative_output_;
329 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
330 // Set all active devices to the same volume.
331 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
332 it != audio_devices_.end();
333 it++) {
334 const AudioDevice& device = it->second;
335 if (!device.is_input && device.active)
336 SetOutputNodeVolumePercent(device.id, volume_percent);
340 // TODO: Rename the 'Percent' to something more meaningful.
341 void CrasAudioHandler::SetInputGainPercent(int gain_percent) {
342 // TODO(jennyz): Should we set all input devices' gain to the same level?
343 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
344 it != audio_devices_.end();
345 it++) {
346 const AudioDevice& device = it->second;
347 if (device.is_input && device.active)
348 SetInputNodeGainPercent(active_input_node_id_, gain_percent);
352 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
353 SetOutputVolumePercent(output_volume_ + adjust_by_percent);
356 void CrasAudioHandler::SetOutputMute(bool mute_on) {
357 if (!SetOutputMuteInternal(mute_on))
358 return;
360 // Save the mute state for all active output audio devices.
361 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
362 it != audio_devices_.end();
363 it++) {
364 const AudioDevice& device = it->second;
365 if (!device.is_input && device.active) {
366 audio_pref_handler_->SetMuteValue(device, output_mute_on_);
370 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged());
373 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
374 if (output_volume_ <= kMuteThresholdPercent) {
375 // Avoid the situation when sound has been unmuted, but the volume
376 // is set to a very low value, so user still can't hear any sound.
377 SetOutputVolumePercent(kDefaultUnmuteVolumePercent);
381 void CrasAudioHandler::SetInputMute(bool mute_on) {
382 SetInputMuteInternal(mute_on);
383 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
386 void CrasAudioHandler::SetActiveOutputNode(uint64_t node_id, bool notify) {
387 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
388 SetActiveOutputNode(node_id);
389 if (notify)
390 NotifyActiveNodeChanged(false);
393 void CrasAudioHandler::SetActiveInputNode(uint64_t node_id, bool notify) {
394 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
395 SetActiveInputNode(node_id);
396 if (notify)
397 NotifyActiveNodeChanged(true);
400 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64_t device_id,
401 int value) {
402 const AudioDevice* device = GetDeviceFromId(device_id);
403 if (!device)
404 return;
406 if (device->is_input)
407 SetInputNodeGainPercent(device_id, value);
408 else
409 SetOutputNodeVolumePercent(device_id, value);
412 void CrasAudioHandler::SetMuteForDevice(uint64_t device_id, bool mute_on) {
413 if (device_id == active_output_node_id_) {
414 SetOutputMute(mute_on);
415 return;
416 } else if (device_id == active_input_node_id_) {
417 VLOG(1) << "SetMuteForDevice sets active input device id="
418 << "0x" << std::hex << device_id << " mute=" << mute_on;
419 SetInputMute(mute_on);
420 return;
423 const AudioDevice* device = GetDeviceFromId(device_id);
424 // Input device's mute state is not recorded in the pref. crbug.com/365050.
425 if (device && !device->is_input)
426 audio_pref_handler_->SetMuteValue(*device, mute_on);
429 void CrasAudioHandler::LogErrors() {
430 log_errors_ = true;
433 CrasAudioHandler::CrasAudioHandler(
434 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
435 : audio_pref_handler_(audio_pref_handler),
436 output_mute_on_(false),
437 input_mute_on_(false),
438 output_volume_(0),
439 input_gain_(0),
440 active_output_node_id_(0),
441 active_input_node_id_(0),
442 has_alternative_input_(false),
443 has_alternative_output_(false),
444 output_mute_locked_(false),
445 log_errors_(false),
446 weak_ptr_factory_(this) {
447 if (!audio_pref_handler.get())
448 return;
449 // If the DBusThreadManager or the CrasAudioClient aren't available, there
450 // isn't much we can do. This should only happen when running tests.
451 if (!chromeos::DBusThreadManager::IsInitialized() ||
452 !chromeos::DBusThreadManager::Get() ||
453 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
454 return;
455 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
456 audio_pref_handler_->AddAudioPrefObserver(this);
457 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
458 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
459 AddObserver(this);
461 InitializeAudioState();
464 CrasAudioHandler::~CrasAudioHandler() {
465 if (!chromeos::DBusThreadManager::IsInitialized() ||
466 !chromeos::DBusThreadManager::Get() ||
467 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
468 return;
469 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
470 RemoveObserver(this);
471 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
472 RemoveObserver(this);
473 if (audio_pref_handler_.get())
474 audio_pref_handler_->RemoveAudioPrefObserver(this);
475 audio_pref_handler_ = NULL;
478 void CrasAudioHandler::AudioClientRestarted() {
479 // Make sure the logging is enabled in case cras server
480 // restarts after crashing.
481 LogErrors();
482 InitializeAudioState();
485 void CrasAudioHandler::NodesChanged() {
486 // Refresh audio nodes data.
487 GetNodes();
490 void CrasAudioHandler::ActiveOutputNodeChanged(uint64_t node_id) {
491 if (active_output_node_id_ == node_id)
492 return;
494 // Active audio output device should always be changed by chrome.
495 // During system boot, cras may change active input to unknown device 0x1,
496 // we don't need to log it, since it is not an valid device.
497 if (GetDeviceFromId(node_id)) {
498 LOG_IF(WARNING, log_errors_)
499 << "Active output node changed unexpectedly by system node_id="
500 << "0x" << std::hex << node_id;
504 void CrasAudioHandler::ActiveInputNodeChanged(uint64_t node_id) {
505 if (active_input_node_id_ == node_id)
506 return;
508 // Active audio input device should always be changed by chrome.
509 // During system boot, cras may change active input to unknown device 0x2,
510 // we don't need to log it, since it is not an valid device.
511 if (GetDeviceFromId(node_id)) {
512 LOG_IF(WARNING, log_errors_)
513 << "Active input node changed unexpectedly by system node_id="
514 << "0x" << std::hex << node_id;
518 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
519 ApplyAudioPolicy();
522 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
523 // Enable logging after cras server is started, which will be after
524 // EmitLoginPromptVisible.
525 LogErrors();
528 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64_t device_id) const {
529 AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
530 if (it == audio_devices_.end())
531 return NULL;
533 return &(it->second);
536 const AudioDevice* CrasAudioHandler::GetKeyboardMic() const {
537 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
538 it != audio_devices_.end(); it++) {
539 if (it->second.is_input && it->second.type == AUDIO_TYPE_KEYBOARD_MIC)
540 return &(it->second);
542 return NULL;
545 void CrasAudioHandler::SetupAudioInputState() {
546 // Set the initial audio state to the ones read from audio prefs.
547 const AudioDevice* device = GetDeviceFromId(active_input_node_id_);
548 if (!device) {
549 LOG_IF(ERROR, log_errors_)
550 << "Can't set up audio state for unknown input device id ="
551 << "0x" << std::hex << active_input_node_id_;
552 return;
554 input_gain_ = audio_pref_handler_->GetInputGainValue(device);
555 VLOG(1) << "SetupAudioInputState for active device id="
556 << "0x" << std::hex << device->id << " mute=" << input_mute_on_;
557 SetInputMuteInternal(input_mute_on_);
558 // TODO(rkc,jennyz): Set input gain once we decide on how to store
559 // the gain values since the range and step are both device specific.
562 void CrasAudioHandler::SetupAudioOutputState() {
563 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
564 if (!device) {
565 LOG_IF(ERROR, log_errors_)
566 << "Can't set up audio state for unknown output device id ="
567 << "0x" << std::hex << active_output_node_id_;
568 return;
570 DCHECK(!device->is_input);
571 output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
572 output_volume_ = audio_pref_handler_->GetOutputVolumeValue(device);
574 SetOutputMuteInternal(output_mute_on_);
575 SetOutputNodeVolume(active_output_node_id_, output_volume_);
578 // This sets up the state of an additional active node.
579 void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64_t node_id) {
580 const AudioDevice* device = GetDeviceFromId(node_id);
581 if (!device) {
582 VLOG(1) << "Can't set up audio state for unknown device id ="
583 << "0x" << std::hex << node_id;
584 return;
587 DCHECK(node_id != active_output_node_id_ && node_id != active_input_node_id_);
589 // Note: The mute state is a system wide state, we don't set mute per device,
590 // but just keep the mute state consistent for the active node in prefs.
591 // The output volume should be set to the same value for all active output
592 // devices. For input devices, we don't restore their gain value so far.
593 // TODO(jennyz): crbug.com/417418, track the status for the decison if
594 // we should persist input gain value in prefs.
595 if (!device->is_input) {
596 audio_pref_handler_->SetMuteValue(*device, IsOutputMuted());
597 SetOutputNodeVolumePercent(node_id, GetOutputVolumePercent());
601 void CrasAudioHandler::InitializeAudioState() {
602 ApplyAudioPolicy();
603 GetNodes();
606 void CrasAudioHandler::ApplyAudioPolicy() {
607 output_mute_locked_ = false;
608 if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
609 // Mute the device, but do not update the preference.
610 SetOutputMuteInternal(true);
611 output_mute_locked_ = true;
612 } else {
613 // Restore the mute state.
614 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
615 if (device)
616 SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
619 // Policy for audio input is handled by kAudioCaptureAllowed in the Chrome
620 // media system.
623 void CrasAudioHandler::SetOutputNodeVolume(uint64_t node_id, int volume) {
624 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
625 SetOutputNodeVolume(node_id, volume);
628 void CrasAudioHandler::SetOutputNodeVolumePercent(uint64_t node_id,
629 int volume_percent) {
630 const AudioDevice* device = this->GetDeviceFromId(node_id);
631 if (!device || device->is_input)
632 return;
634 volume_percent = min(max(volume_percent, 0), 100);
635 if (volume_percent <= kMuteThresholdPercent)
636 volume_percent = 0;
637 if (node_id == active_output_node_id_)
638 output_volume_ = volume_percent;
640 audio_pref_handler_->SetVolumeGainValue(*device, volume_percent);
642 if (device->active) {
643 SetOutputNodeVolume(node_id, volume_percent);
644 FOR_EACH_OBSERVER(AudioObserver, observers_,
645 OnOutputNodeVolumeChanged(node_id, volume_percent));
649 bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
650 if (output_mute_locked_)
651 return false;
653 output_mute_on_ = mute_on;
654 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
655 SetOutputUserMute(mute_on);
656 return true;
659 void CrasAudioHandler::SetInputNodeGain(uint64_t node_id, int gain) {
660 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
661 SetInputNodeGain(node_id, gain);
664 void CrasAudioHandler::SetInputNodeGainPercent(uint64_t node_id,
665 int gain_percent) {
666 const AudioDevice* device = GetDeviceFromId(node_id);
667 if (!device || !device->is_input)
668 return;
670 // NOTE: We do not sanitize input gain values since the range is completely
671 // dependent on the device.
672 if (active_input_node_id_ == node_id)
673 input_gain_ = gain_percent;
675 audio_pref_handler_->SetVolumeGainValue(*device, gain_percent);
677 if (device->active) {
678 SetInputNodeGain(node_id, gain_percent);
679 FOR_EACH_OBSERVER(AudioObserver, observers_,
680 OnInputNodeGainChanged(node_id, gain_percent));
684 void CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
685 input_mute_on_ = mute_on;
686 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
687 SetInputMute(mute_on);
690 void CrasAudioHandler::GetNodes() {
691 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
692 base::Bind(&CrasAudioHandler::HandleGetNodes,
693 weak_ptr_factory_.GetWeakPtr()),
694 base::Bind(&CrasAudioHandler::HandleGetNodesError,
695 weak_ptr_factory_.GetWeakPtr()));
698 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device,
699 uint64_t* current_active_node_id) {
700 // If the device we want to switch to is already the current active device,
701 // do nothing.
702 if (new_active_device.active &&
703 new_active_device.id == *current_active_node_id) {
704 return false;
707 // Reset all other input or output devices' active status. The active audio
708 // device from the previous user session can be remembered by cras, but not
709 // in chrome. see crbug.com/273271.
710 for (AudioDeviceMap::iterator it = audio_devices_.begin();
711 it != audio_devices_.end(); ++it) {
712 if (it->second.is_input == new_active_device.is_input &&
713 it->second.id != new_active_device.id)
714 it->second.active = false;
717 // Set the current active input/output device to the new_active_device.
718 *current_active_node_id = new_active_device.id;
719 audio_devices_[*current_active_node_id].active = true;
720 return true;
723 bool CrasAudioHandler::NonActiveDeviceUnplugged(size_t old_devices_size,
724 size_t new_devices_size,
725 uint64_t current_active_node) {
726 return (new_devices_size < old_devices_size &&
727 GetDeviceFromId(current_active_node));
730 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device, bool notify) {
731 if (device.is_input) {
732 if (!ChangeActiveDevice(device, &active_input_node_id_))
733 return;
734 SetupAudioInputState();
735 SetActiveInputNode(active_input_node_id_, notify);
736 } else {
737 if (!ChangeActiveDevice(device, &active_output_node_id_))
738 return;
739 SetupAudioOutputState();
740 SetActiveOutputNode(active_output_node_id_, notify);
744 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
745 bool is_input,
746 AudioNodeList* new_discovered) {
747 size_t num_old_devices = 0;
748 size_t num_new_devices = 0;
749 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
750 it != audio_devices_.end(); ++it) {
751 if (is_input == it->second.is_input)
752 ++num_old_devices;
755 bool new_or_changed_device = false;
756 new_discovered->clear();
757 for (AudioNodeList::const_iterator it = new_nodes.begin();
758 it != new_nodes.end(); ++it) {
759 if (is_input == it->is_input) {
760 ++num_new_devices;
761 // Look to see if the new device not in the old device list.
762 AudioDevice device(*it);
763 DeviceStatus status = CheckDeviceStatus(device);
764 if (status == NEW_DEVICE)
765 new_discovered->push_back(*it);
766 if (status == NEW_DEVICE || status == CHANGED_DEVICE) {
767 new_or_changed_device = true;
771 return new_or_changed_device || (num_old_devices != num_new_devices);
774 CrasAudioHandler::DeviceStatus CrasAudioHandler::CheckDeviceStatus(
775 const AudioDevice& device) {
776 const AudioDevice* device_found = GetDeviceFromId(device.id);
777 if (!device_found)
778 return NEW_DEVICE;
780 if (!IsSameAudioDevice(device, *device_found)) {
781 LOG(WARNING) << "Different Audio devices with same id:"
782 << " new device: " << device.ToString()
783 << " old device: " << device_found->ToString();
784 return CHANGED_DEVICE;
785 } else if (device.active != device_found->active) {
786 return CHANGED_DEVICE;
789 return OLD_DEVICE;
792 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input) {
793 if (is_input)
794 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
795 else
796 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
799 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
800 const AudioNodeList& nodes) {
801 size_t old_output_device_size = 0;
802 size_t old_input_device_size = 0;
803 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
804 it != audio_devices_.end(); ++it) {
805 if (it->second.is_input)
806 ++old_input_device_size;
807 else
808 ++old_output_device_size;
811 AudioNodeList hotplug_output_nodes;
812 AudioNodeList hotplug_input_nodes;
813 bool output_devices_changed =
814 HasDeviceChange(nodes, false, &hotplug_output_nodes);
815 bool input_devices_changed =
816 HasDeviceChange(nodes, true, &hotplug_input_nodes);
817 audio_devices_.clear();
818 has_alternative_input_ = false;
819 has_alternative_output_ = false;
821 while (!input_devices_pq_.empty())
822 input_devices_pq_.pop();
823 while (!output_devices_pq_.empty())
824 output_devices_pq_.pop();
826 size_t new_output_device_size = 0;
827 size_t new_input_device_size = 0;
828 for (size_t i = 0; i < nodes.size(); ++i) {
829 AudioDevice device(nodes[i]);
830 audio_devices_[device.id] = device;
832 if (!has_alternative_input_ &&
833 device.is_input &&
834 device.type != AUDIO_TYPE_INTERNAL_MIC &&
835 device.type != AUDIO_TYPE_KEYBOARD_MIC) {
836 has_alternative_input_ = true;
837 } else if (!has_alternative_output_ &&
838 !device.is_input &&
839 device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
840 has_alternative_output_ = true;
843 if (device.is_input) {
844 input_devices_pq_.push(device);
845 ++new_input_device_size;
846 } else {
847 output_devices_pq_.push(device);
848 ++new_output_device_size;
852 // If the previous active device is removed from the new node list,
853 // reset active_output_node_id_.
854 if (!GetDeviceFromId(active_output_node_id_))
855 active_output_node_id_ = 0;
856 if (!GetDeviceFromId(active_input_node_id_))
857 active_input_node_id_ = 0;
859 // If audio nodes change is caused by unplugging some non-active audio
860 // devices, the previously set active audio device will stay active.
861 // Otherwise, switch to a new active audio device according to their priority.
862 if (input_devices_changed &&
863 !NonActiveDeviceUnplugged(old_input_device_size,
864 new_input_device_size,
865 active_input_node_id_)) {
866 // Some devices like chromeboxes don't have the internal audio input. In
867 // that case the active input node id should be reset.
868 if (input_devices_pq_.empty()) {
869 active_input_node_id_ = 0;
870 NotifyActiveNodeChanged(true);
871 } else {
872 // If user has hot plugged a new node, we should change to the active
873 // device to the new node if it has the highest priority; otherwise,
874 // we should keep the existing active node chosen by user.
875 // For all other cases, we will choose the node with highest priority.
876 if (!active_input_node_id_ || hotplug_input_nodes.empty() ||
877 IsNodeInTheList(input_devices_pq_.top().id, hotplug_input_nodes)) {
878 SwitchToDevice(input_devices_pq_.top(), true);
882 if (output_devices_changed &&
883 !NonActiveDeviceUnplugged(old_output_device_size,
884 new_output_device_size,
885 active_output_node_id_)) {
886 // This is really unlikely to happen because all ChromeOS devices have the
887 // internal audio output.
888 if (output_devices_pq_.empty()) {
889 active_output_node_id_ = 0;
890 NotifyActiveNodeChanged(false);
891 } else {
892 // ditto input node case.
893 if (!active_output_node_id_ || hotplug_output_nodes.empty() ||
894 IsNodeInTheList(output_devices_pq_.top().id, hotplug_output_nodes)) {
895 SwitchToDevice(output_devices_pq_.top(), true);
901 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
902 bool success) {
903 if (!success) {
904 LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data";
905 return;
908 UpdateDevicesAndSwitchActive(node_list);
909 FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
912 void CrasAudioHandler::HandleGetNodesError(const std::string& error_name,
913 const std::string& error_msg) {
914 LOG_IF(ERROR, log_errors_) << "Failed to call GetNodes: "
915 << error_name << ": " << error_msg;
918 void CrasAudioHandler::AddAdditionalActiveNode(uint64_t node_id, bool notify) {
919 const AudioDevice* device = GetDeviceFromId(node_id);
920 if (!device) {
921 VLOG(1) << "AddActiveInputNode: Cannot find device id="
922 << "0x" << std::hex << node_id;
923 return;
926 audio_devices_[node_id].active = true;
927 SetupAdditionalActiveAudioNodeState(node_id);
929 if (device->is_input) {
930 DCHECK(node_id != active_input_node_id_);
931 chromeos::DBusThreadManager::Get()
932 ->GetCrasAudioClient()
933 ->AddActiveInputNode(node_id);
934 if (notify)
935 NotifyActiveNodeChanged(true);
936 } else {
937 DCHECK(node_id != active_output_node_id_);
938 chromeos::DBusThreadManager::Get()
939 ->GetCrasAudioClient()
940 ->AddActiveOutputNode(node_id);
941 if (notify)
942 NotifyActiveNodeChanged(false);
946 void CrasAudioHandler::RemoveActiveNodeInternal(uint64_t node_id, bool notify) {
947 const AudioDevice* device = GetDeviceFromId(node_id);
948 if (!device) {
949 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
950 << "0x" << std::hex << node_id;
951 return;
954 audio_devices_[node_id].active = false;
955 if (device->is_input) {
956 if (node_id == active_input_node_id_)
957 active_input_node_id_ = 0;
958 chromeos::DBusThreadManager::Get()
959 ->GetCrasAudioClient()
960 ->RemoveActiveInputNode(node_id);
961 if (notify)
962 NotifyActiveNodeChanged(true);
963 } else {
964 if (node_id == active_output_node_id_)
965 active_output_node_id_ = 0;
966 chromeos::DBusThreadManager::Get()
967 ->GetCrasAudioClient()
968 ->RemoveActiveOutputNode(node_id);
969 if (notify)
970 NotifyActiveNodeChanged(false);
974 } // namespace chromeos