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