Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chromeos / audio / cras_audio_handler.cc
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.
4
5 #include "chromeos/audio/cras_audio_handler.h"
6
7 #include <algorithm>
8 #include <cmath>
9
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"
16
17 using std::max;
18 using std::min;
19
20 namespace chromeos {
21
22 namespace {
23
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;
27
28 // Volume value which should be considered as muted in range [0, 100].
29 const int kMuteThresholdPercent = 1;
30
31 static CrasAudioHandler* g_cras_audio_handler = NULL;
32
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;
36 }
37
38 }  // namespace
39
40 CrasAudioHandler::AudioObserver::AudioObserver() {
41 }
42
43 CrasAudioHandler::AudioObserver::~AudioObserver() {
44 }
45
46 void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
47 }
48
49 void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
50 }
51
52 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
53 }
54
55 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
56 }
57
58 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
59 }
60
61 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
62 }
63
64 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
65 }
66
67 // static
68 void CrasAudioHandler::Initialize(
69     scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) {
70   CHECK(!g_cras_audio_handler);
71   g_cras_audio_handler = new CrasAudioHandler(audio_pref_handler);
72 }
73
74 // static
75 void CrasAudioHandler::InitializeForTesting() {
76   CHECK(!g_cras_audio_handler);
77   CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
78 }
79
80 // static
81 void CrasAudioHandler::Shutdown() {
82   CHECK(g_cras_audio_handler);
83   delete g_cras_audio_handler;
84   g_cras_audio_handler = NULL;
85 }
86
87 // static
88 bool CrasAudioHandler::IsInitialized() {
89   return g_cras_audio_handler != NULL;
90 }
91
92 // static
93 CrasAudioHandler* CrasAudioHandler::Get() {
94   CHECK(g_cras_audio_handler)
95       << "CrasAudioHandler::Get() called before Initialize().";
96   return g_cras_audio_handler;
97 }
98
99 void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
100   observers_.AddObserver(observer);
101 }
102
103 void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
104   observers_.RemoveObserver(observer);
105 }
106
107 bool CrasAudioHandler::HasKeyboardMic() {
108   return GetKeyboardMic() != NULL;
109 }
110
111 bool CrasAudioHandler::IsOutputMuted() {
112   return output_mute_on_;
113 }
114
115 bool CrasAudioHandler::IsOutputMutedForDevice(uint64 device_id) {
116   const AudioDevice* device = GetDeviceFromId(device_id);
117   if (!device)
118     return false;
119   DCHECK(!device->is_input);
120   return audio_pref_handler_->GetMuteValue(*device);
121 }
122
123 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
124   return output_volume_ <= kMuteThresholdPercent;
125 }
126
127 bool CrasAudioHandler::IsInputMuted() {
128   return input_mute_on_;
129 }
130
131 bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id) {
132   const AudioDevice* device = GetDeviceFromId(device_id);
133   if (!device)
134     return false;
135   DCHECK(device->is_input);
136   // We don't record input mute state for each device in the prefs,
137   // for any non-active input device, we assume mute is off.
138   if (device->id == active_input_node_id_)
139     return input_mute_on_;
140   return false;
141 }
142
143 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
144   return kMuteThresholdPercent;
145 }
146
147 int CrasAudioHandler::GetOutputVolumePercent() {
148   return output_volume_;
149 }
150
151 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64 device_id) {
152   if (device_id == active_output_node_id_) {
153     return output_volume_;
154   } else {
155     const AudioDevice* device = GetDeviceFromId(device_id);
156     return static_cast<int>(audio_pref_handler_->GetOutputVolumeValue(device));
157   }
158 }
159
160 int CrasAudioHandler::GetInputGainPercent() {
161   return input_gain_;
162 }
163
164 int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id) {
165   if (device_id == active_input_node_id_) {
166     return input_gain_;
167   } else {
168     const AudioDevice* device = GetDeviceFromId(device_id);
169     return static_cast<int>(audio_pref_handler_->GetInputGainValue(device));
170   }
171 }
172
173 uint64 CrasAudioHandler::GetActiveOutputNode() const {
174   return active_output_node_id_;
175 }
176
177 uint64 CrasAudioHandler::GetActiveInputNode() const {
178   return active_input_node_id_;
179 }
180
181 void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
182   device_list->clear();
183   for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
184        it != audio_devices_.end(); ++it)
185     device_list->push_back(it->second);
186 }
187
188 bool CrasAudioHandler::GetActiveOutputDevice(AudioDevice* device) const {
189   const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_);
190   if (!active_device || !device)
191     return false;
192   *device = *active_device;
193   return true;
194 }
195
196 void CrasAudioHandler::SetKeyboardMicActive(bool active) {
197   const AudioDevice* keyboard_mic = GetKeyboardMic();
198   if (!keyboard_mic)
199     return;
200   if (active) {
201     chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
202         AddActiveInputNode(keyboard_mic->id);
203   } else {
204     chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
205         RemoveActiveInputNode(keyboard_mic->id);
206   }
207 }
208
209 bool CrasAudioHandler::has_alternative_input() const {
210   return has_alternative_input_;
211 }
212
213 bool CrasAudioHandler::has_alternative_output() const {
214   return has_alternative_output_;
215 }
216
217 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
218   volume_percent = min(max(volume_percent, 0), 100);
219   if (volume_percent <= kMuteThresholdPercent)
220     volume_percent = 0;
221   output_volume_ = volume_percent;
222
223   if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_))
224     audio_pref_handler_->SetVolumeGainValue(*device, output_volume_);
225
226   SetOutputNodeVolume(active_output_node_id_, output_volume_);
227   FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged());
228 }
229
230 // TODO: Rename the 'Percent' to something more meaningful.
231 void CrasAudioHandler::SetInputGainPercent(int gain_percent) {
232   // NOTE: We do not sanitize input gain values since the range is completely
233   // dependent on the device.
234   input_gain_ = gain_percent;
235
236   if (const AudioDevice* device = GetDeviceFromId(active_input_node_id_))
237     audio_pref_handler_->SetVolumeGainValue(*device, input_gain_);
238
239   SetInputNodeGain(active_input_node_id_, input_gain_);
240   FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged());
241 }
242
243 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
244   SetOutputVolumePercent(output_volume_ + adjust_by_percent);
245 }
246
247 void CrasAudioHandler::SetOutputMute(bool mute_on) {
248   if (!SetOutputMuteInternal(mute_on))
249     return;
250
251   if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_)) {
252     DCHECK(!device->is_input);
253     audio_pref_handler_->SetMuteValue(*device, output_mute_on_);
254   }
255
256   FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged());
257 }
258
259 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
260   if (output_volume_ <= kMuteThresholdPercent) {
261     // Avoid the situation when sound has been unmuted, but the volume
262     // is set to a very low value, so user still can't hear any sound.
263     SetOutputVolumePercent(kDefaultUnmuteVolumePercent);
264   }
265 }
266
267 void CrasAudioHandler::SetInputMute(bool mute_on) {
268   if (!SetInputMuteInternal(mute_on))
269     return;
270
271   FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
272 }
273
274 void CrasAudioHandler::SetActiveOutputNode(uint64 node_id) {
275   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
276       SetActiveOutputNode(node_id);
277   FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
278 }
279
280 void CrasAudioHandler::SetActiveInputNode(uint64 node_id) {
281   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
282       SetActiveInputNode(node_id);
283   FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
284 }
285
286 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id,
287                                                      int value) {
288   if (device_id == active_output_node_id_) {
289     SetOutputVolumePercent(value);
290     return;
291   } else if (device_id == active_input_node_id_) {
292     SetInputGainPercent(value);
293     return;
294   }
295
296   if (const AudioDevice* device = GetDeviceFromId(device_id)) {
297     if (!device->is_input) {
298       value = min(max(value, 0), 100);
299       if (value <= kMuteThresholdPercent)
300         value = 0;
301     }
302     audio_pref_handler_->SetVolumeGainValue(*device, value);
303   }
304 }
305
306 void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) {
307   if (device_id == active_output_node_id_) {
308     SetOutputMute(mute_on);
309     return;
310   } else if (device_id == active_input_node_id_) {
311     VLOG(1) << "SetMuteForDevice sets active input device id="
312             << "0x" << std::hex << device_id << " mute=" << mute_on;
313     SetInputMute(mute_on);
314     return;
315   }
316
317   const AudioDevice* device = GetDeviceFromId(device_id);
318   // Input device's mute state is not recorded in the pref. crbug.com/365050.
319   if (device && !device->is_input)
320     audio_pref_handler_->SetMuteValue(*device, mute_on);
321 }
322
323 void CrasAudioHandler::LogErrors() {
324   log_errors_ = true;
325 }
326
327 CrasAudioHandler::CrasAudioHandler(
328     scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
329     : audio_pref_handler_(audio_pref_handler),
330       weak_ptr_factory_(this),
331       output_mute_on_(false),
332       input_mute_on_(false),
333       output_volume_(0),
334       input_gain_(0),
335       active_output_node_id_(0),
336       active_input_node_id_(0),
337       has_alternative_input_(false),
338       has_alternative_output_(false),
339       output_mute_locked_(false),
340       input_mute_locked_(false),
341       log_errors_(false) {
342   if (!audio_pref_handler.get())
343     return;
344   // If the DBusThreadManager or the CrasAudioClient aren't available, there
345   // isn't much we can do. This should only happen when running tests.
346   if (!chromeos::DBusThreadManager::IsInitialized() ||
347       !chromeos::DBusThreadManager::Get() ||
348       !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
349     return;
350   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
351   audio_pref_handler_->AddAudioPrefObserver(this);
352   if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
353     chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
354         AddObserver(this);
355   }
356   InitializeAudioState();
357 }
358
359 CrasAudioHandler::~CrasAudioHandler() {
360   if (!chromeos::DBusThreadManager::IsInitialized() ||
361       !chromeos::DBusThreadManager::Get() ||
362       !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
363     return;
364   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
365       RemoveObserver(this);
366   chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
367       RemoveObserver(this);
368   if (audio_pref_handler_.get())
369     audio_pref_handler_->RemoveAudioPrefObserver(this);
370   audio_pref_handler_ = NULL;
371 }
372
373 void CrasAudioHandler::AudioClientRestarted() {
374   // Make sure the logging is enabled in case cras server
375   // restarts after crashing.
376   LogErrors();
377   InitializeAudioState();
378 }
379
380 void CrasAudioHandler::NodesChanged() {
381   // Refresh audio nodes data.
382   GetNodes();
383 }
384
385 void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) {
386   if (active_output_node_id_ == node_id)
387     return;
388
389   // Active audio output device should always be changed by chrome.
390   // During system boot, cras may change active input to unknown device 0x1,
391   // we don't need to log it, since it is not an valid device.
392   if (GetDeviceFromId(node_id)) {
393     LOG_IF(WARNING, log_errors_)
394         << "Active output node changed unexpectedly by system node_id="
395         << "0x" << std::hex << node_id;
396   }
397 }
398
399 void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) {
400   if (active_input_node_id_ == node_id)
401     return;
402
403   // Active audio input device should always be changed by chrome.
404   // During system boot, cras may change active input to unknown device 0x2,
405   // we don't need to log it, since it is not an valid device.
406   if (GetDeviceFromId(node_id)) {
407     LOG_IF(WARNING, log_errors_)
408         << "Active input node changed unexpectedly by system node_id="
409         << "0x" << std::hex << node_id;
410   }
411 }
412
413 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
414   ApplyAudioPolicy();
415 }
416
417 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
418   // Enable logging after cras server is started, which will be after
419   // EmitLoginPromptVisible.
420   LogErrors();
421 }
422
423 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64 device_id) const {
424   AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
425   if (it == audio_devices_.end())
426     return NULL;
427
428   return &(it->second);
429 }
430
431 const AudioDevice* CrasAudioHandler::GetKeyboardMic() const {
432   for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
433        it != audio_devices_.end(); it++) {
434     if (it->second.is_input && it->second.type == AUDIO_TYPE_KEYBOARD_MIC)
435       return &(it->second);
436   }
437   return NULL;
438 }
439
440 void CrasAudioHandler::SetupAudioInputState() {
441   // Set the initial audio state to the ones read from audio prefs.
442   const AudioDevice* device = GetDeviceFromId(active_input_node_id_);
443   if (!device) {
444     LOG_IF(ERROR, log_errors_)
445         << "Can't set up audio state for unknown input device id ="
446         << "0x" << std::hex << active_input_node_id_;
447     return;
448   }
449   input_gain_ = audio_pref_handler_->GetInputGainValue(device);
450   VLOG(1) << "SetupAudioInputState for active device id="
451           << "0x" << std::hex << device->id << " mute=" << input_mute_on_;
452   SetInputMuteInternal(input_mute_on_);
453   // TODO(rkc,jennyz): Set input gain once we decide on how to store
454   // the gain values since the range and step are both device specific.
455 }
456
457 void CrasAudioHandler::SetupAudioOutputState() {
458   const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
459   if (!device) {
460     LOG_IF(ERROR, log_errors_)
461         << "Can't set up audio state for unknown output device id ="
462         << "0x" << std::hex << active_output_node_id_;
463     return;
464   }
465   DCHECK(!device->is_input);
466   output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
467   output_volume_ = audio_pref_handler_->GetOutputVolumeValue(device);
468
469   SetOutputMuteInternal(output_mute_on_);
470   SetOutputNodeVolume(active_output_node_id_, output_volume_);
471 }
472
473 void CrasAudioHandler::InitializeAudioState() {
474   ApplyAudioPolicy();
475   GetNodes();
476 }
477
478 void CrasAudioHandler::ApplyAudioPolicy() {
479   output_mute_locked_ = false;
480   if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
481     // Mute the device, but do not update the preference.
482     SetOutputMuteInternal(true);
483     output_mute_locked_ = true;
484   } else {
485     // Restore the mute state.
486     const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
487     if (device)
488       SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
489   }
490
491   input_mute_locked_ = false;
492   if (audio_pref_handler_->GetAudioCaptureAllowedValue()) {
493     VLOG(1) << "Audio input allowed by policy, sets input id="
494             << "0x" << std::hex << active_input_node_id_ << " mute=false";
495     SetInputMuteInternal(false);
496   } else {
497     VLOG(0) << "Audio input NOT allowed by policy, sets input id="
498             << "0x" << std::hex << active_input_node_id_ << " mute=true";
499     SetInputMuteInternal(true);
500     input_mute_locked_ = true;
501   }
502 }
503
504 void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
505   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
506       SetOutputNodeVolume(node_id, volume);
507 }
508
509 bool  CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
510   if (output_mute_locked_)
511     return false;
512
513   output_mute_on_ = mute_on;
514   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
515       SetOutputUserMute(mute_on);
516   return true;
517 }
518
519 void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
520   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
521       SetInputNodeGain(node_id, gain);
522 }
523
524 bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
525   if (input_mute_locked_)
526     return false;
527
528   VLOG(1) << "SetInputMuteInternal sets active input device id="
529           << "0x" << std::hex << active_input_node_id_ << " mute=" << mute_on;
530   input_mute_on_ = mute_on;
531   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
532       SetInputMute(mute_on);
533   return true;
534 }
535
536 void CrasAudioHandler::GetNodes() {
537   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
538       base::Bind(&CrasAudioHandler::HandleGetNodes,
539                  weak_ptr_factory_.GetWeakPtr()),
540       base::Bind(&CrasAudioHandler::HandleGetNodesError,
541                  weak_ptr_factory_.GetWeakPtr()));
542 }
543
544 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device,
545                                           uint64* current_active_node_id) {
546   // If the device we want to switch to is already the current active device,
547   // do nothing.
548   if (new_active_device.active &&
549       new_active_device.id == *current_active_node_id) {
550     return false;
551   }
552
553   // Reset all other input or output devices' active status. The active audio
554   // device from the previous user session can be remembered by cras, but not
555   // in chrome. see crbug.com/273271.
556   for (AudioDeviceMap::iterator it = audio_devices_.begin();
557        it != audio_devices_.end(); ++it) {
558     if (it->second.is_input == new_active_device.is_input &&
559         it->second.id != new_active_device.id)
560       it->second.active = false;
561   }
562
563   // Set the current active input/output device to the new_active_device.
564   *current_active_node_id = new_active_device.id;
565   audio_devices_[*current_active_node_id].active = true;
566   return true;
567 }
568
569 bool CrasAudioHandler::NonActiveDeviceUnplugged(
570     size_t old_devices_size,
571     size_t new_devices_size,
572     uint64 current_active_node) {
573   return (new_devices_size < old_devices_size &&
574           GetDeviceFromId(current_active_node));
575 }
576
577 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device) {
578   if (device.is_input) {
579     if (!ChangeActiveDevice(device, &active_input_node_id_))
580       return;
581     SetupAudioInputState();
582     SetActiveInputNode(active_input_node_id_);
583   } else {
584     if (!ChangeActiveDevice(device, &active_output_node_id_))
585       return;
586     SetupAudioOutputState();
587     SetActiveOutputNode(active_output_node_id_);
588   }
589 }
590
591 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
592                                        bool is_input) {
593   size_t num_old_devices = 0;
594   size_t num_new_devices = 0;
595   for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
596        it != audio_devices_.end(); ++it) {
597     if (is_input == it->second.is_input)
598       ++num_old_devices;
599   }
600
601   for (AudioNodeList::const_iterator it = new_nodes.begin();
602        it != new_nodes.end(); ++it) {
603     if (is_input == it->is_input) {
604       ++num_new_devices;
605       // Look to see if the new device not in the old device list.
606       AudioDevice device(*it);
607       if (FoundNewDevice(device))
608         return true;
609     }
610   }
611   return num_old_devices != num_new_devices;
612 }
613
614 bool CrasAudioHandler::FoundNewDevice(const AudioDevice& device) {
615   const AudioDevice* device_found = GetDeviceFromId(device.id);
616   if (!device_found)
617     return true;
618
619   if (!IsSameAudioDevice(device, *device_found)) {
620     LOG(WARNING) << "Different Audio devices with same id:"
621         << " new device: " << device.ToString()
622         << " old device: " << device_found->ToString();
623     return true;
624   }
625   return false;
626 }
627
628 // Sanitize the audio node data. When a device is plugged in or unplugged, there
629 // should be only one NodesChanged signal from cras. However, we've observed
630 // the case that multiple NodesChanged signals being sent from cras. After the
631 // first NodesChanged being processed, chrome sets the active node properly.
632 // However, the NodesChanged received after the first one, can return stale
633 // nodes data in GetNodes call, the staled nodes data does not reflect the
634 // latest active node state. Since active audio node should only be set by
635 // chrome, the inconsistent data from cras could be the result of stale data
636 // described above and sanitized.
637 AudioDevice CrasAudioHandler::GetSanitizedAudioDevice(const AudioNode& node) {
638   AudioDevice device(node);
639   if (device.is_input) {
640     if (device.active && device.id != active_input_node_id_) {
641       LOG(WARNING) << "Stale audio device data, should not be active: "
642           << " device = " << device.ToString()
643           << " current active input node id = 0x" << std::hex
644           << active_input_node_id_;
645       device.active = false;
646     } else if (device.id == active_input_node_id_ && !device.active) {
647       LOG(WARNING) << "Stale audio device data, should be active:"
648           << " device = " << device.ToString()
649           << " current active input node id = 0x" << std::hex
650           << active_input_node_id_;
651       device.active = true;
652     }
653   } else {
654     if (device.active && device.id != active_output_node_id_) {
655       LOG(WARNING) << "Stale audio device data, should not be active: "
656           << " device = " << device.ToString()
657           << " current active output node id = 0x" << std::hex
658           << active_output_node_id_;
659       device.active = false;
660     } else if (device.id == active_output_node_id_ && !device.active) {
661       LOG(WARNING) << "Stale audio device data, should be active:"
662           << " device = " << device.ToString()
663           << " current active output node id = 0x" << std::hex
664           << active_output_node_id_;
665       device.active = true;
666     }
667   }
668   return device;
669 }
670
671 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
672     const AudioNodeList& nodes) {
673   size_t old_audio_devices_size = audio_devices_.size();
674   bool output_devices_changed = HasDeviceChange(nodes, false);
675   bool input_devices_changed = HasDeviceChange(nodes, true);
676   audio_devices_.clear();
677   has_alternative_input_ = false;
678   has_alternative_output_ = false;
679
680   while (!input_devices_pq_.empty())
681     input_devices_pq_.pop();
682   while (!output_devices_pq_.empty())
683     output_devices_pq_.pop();
684
685   for (size_t i = 0; i < nodes.size(); ++i) {
686     AudioDevice device = GetSanitizedAudioDevice(nodes[i]);
687     audio_devices_[device.id] = device;
688
689     if (!has_alternative_input_ &&
690         device.is_input &&
691         device.type != AUDIO_TYPE_INTERNAL_MIC &&
692         device.type != AUDIO_TYPE_KEYBOARD_MIC) {
693       has_alternative_input_ = true;
694     } else if (!has_alternative_output_ &&
695                !device.is_input &&
696                device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
697       has_alternative_output_ = true;
698     }
699
700     if (device.is_input)
701       input_devices_pq_.push(device);
702     else
703       output_devices_pq_.push(device);
704   }
705
706   // If audio nodes change is caused by unplugging some non-active audio
707   // devices, the previously set active audio device will stay active.
708   // Otherwise, switch to a new active audio device according to their priority.
709   if (input_devices_changed &&
710       !NonActiveDeviceUnplugged(old_audio_devices_size,
711                                 audio_devices_.size(),
712                                 active_input_node_id_) &&
713       !input_devices_pq_.empty())
714     SwitchToDevice(input_devices_pq_.top());
715   if (output_devices_changed &&
716       !NonActiveDeviceUnplugged(old_audio_devices_size,
717                                 audio_devices_.size(),
718                                 active_output_node_id_) &&
719       !output_devices_pq_.empty()) {
720     SwitchToDevice(output_devices_pq_.top());
721   }
722 }
723
724 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
725                                       bool success) {
726   if (!success) {
727     LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data";
728     return;
729   }
730
731   UpdateDevicesAndSwitchActive(node_list);
732   FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
733 }
734
735 void CrasAudioHandler::HandleGetNodesError(const std::string& error_name,
736                                            const std::string& error_msg) {
737   LOG_IF(ERROR, log_errors_) << "Failed to call GetNodes: "
738       << error_name  << ": " << error_msg;
739 }
740 }  // namespace chromeos