• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsSameAudioDevice(const AudioDevice & a,const AudioDevice & b)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 
AudioObserver()40 CrasAudioHandler::AudioObserver::AudioObserver() {
41 }
42 
~AudioObserver()43 CrasAudioHandler::AudioObserver::~AudioObserver() {
44 }
45 
OnOutputVolumeChanged()46 void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
47 }
48 
OnInputGainChanged()49 void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
50 }
51 
OnOutputMuteChanged()52 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
53 }
54 
OnInputMuteChanged()55 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
56 }
57 
OnAudioNodesChanged()58 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
59 }
60 
OnActiveOutputNodeChanged()61 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
62 }
63 
OnActiveInputNodeChanged()64 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
65 }
66 
67 // static
Initialize(scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)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
InitializeForTesting()75 void CrasAudioHandler::InitializeForTesting() {
76   CHECK(!g_cras_audio_handler);
77   CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
78 }
79 
80 // static
Shutdown()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
IsInitialized()88 bool CrasAudioHandler::IsInitialized() {
89   return g_cras_audio_handler != NULL;
90 }
91 
92 // static
Get()93 CrasAudioHandler* CrasAudioHandler::Get() {
94   CHECK(g_cras_audio_handler)
95       << "CrasAudioHandler::Get() called before Initialize().";
96   return g_cras_audio_handler;
97 }
98 
AddAudioObserver(AudioObserver * observer)99 void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
100   observers_.AddObserver(observer);
101 }
102 
RemoveAudioObserver(AudioObserver * observer)103 void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
104   observers_.RemoveObserver(observer);
105 }
106 
IsOutputMuted()107 bool CrasAudioHandler::IsOutputMuted() {
108   return output_mute_on_;
109 }
110 
IsOutputMutedForDevice(uint64 device_id)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 
IsOutputVolumeBelowDefaultMuteLvel()119 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLvel() {
120   return output_volume_ <= kMuteThresholdPercent;
121 }
122 
IsInputMuted()123 bool CrasAudioHandler::IsInputMuted() {
124   return input_mute_on_;
125 }
126 
IsInputMutedForDevice(uint64 device_id)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 
GetOutputDefaultVolumeMuteThreshold()139 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
140   return kMuteThresholdPercent;
141 }
142 
GetOutputVolumePercent()143 int CrasAudioHandler::GetOutputVolumePercent() {
144   return output_volume_;
145 }
146 
GetOutputVolumePercentForDevice(uint64 device_id)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 
GetInputGainPercent()156 int CrasAudioHandler::GetInputGainPercent() {
157   return input_gain_;
158 }
159 
GetInputGainPercentForDevice(uint64 device_id)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 
GetActiveOutputNode() const169 uint64 CrasAudioHandler::GetActiveOutputNode() const {
170   return active_output_node_id_;
171 }
172 
GetActiveInputNode() const173 uint64 CrasAudioHandler::GetActiveInputNode() const {
174   return active_input_node_id_;
175 }
176 
GetAudioDevices(AudioDeviceList * device_list) const177 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 
GetActiveOutputDevice(AudioDevice * device) const184 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 
has_alternative_input() const192 bool CrasAudioHandler::has_alternative_input() const {
193   return has_alternative_input_;
194 }
195 
has_alternative_output() const196 bool CrasAudioHandler::has_alternative_output() const {
197   return has_alternative_output_;
198 }
199 
SetOutputVolumePercent(int volume_percent)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.
SetInputGainPercent(int gain_percent)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 
AdjustOutputVolumeByPercent(int adjust_by_percent)226 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
227   SetOutputVolumePercent(output_volume_ + adjust_by_percent);
228 }
229 
SetOutputMute(bool mute_on)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 
AdjustOutputVolumeToAudibleLevel()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 
SetInputMute(bool mute_on)250 void CrasAudioHandler::SetInputMute(bool mute_on) {
251   if (!SetInputMuteInternal(mute_on))
252     return;
253 
254   FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
255 }
256 
SetActiveOutputNode(uint64 node_id)257 void CrasAudioHandler::SetActiveOutputNode(uint64 node_id) {
258   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
259       SetActiveOutputNode(node_id);
260   FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
261 }
262 
SetActiveInputNode(uint64 node_id)263 void CrasAudioHandler::SetActiveInputNode(uint64 node_id) {
264   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
265       SetActiveInputNode(node_id);
266   FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
267 }
268 
SetVolumeGainPercentForDevice(uint64 device_id,int value)269 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id,
270                                                      int value) {
271   if (device_id == active_output_node_id_) {
272     SetOutputVolumePercent(value);
273     return;
274   } else if (device_id == active_input_node_id_) {
275     SetInputGainPercent(value);
276     return;
277   }
278 
279   if (const AudioDevice* device = GetDeviceFromId(device_id)) {
280     if (!device->is_input) {
281       value = min(max(value, 0), 100);
282       if (value <= kMuteThresholdPercent)
283         value = 0;
284     }
285     audio_pref_handler_->SetVolumeGainValue(*device, value);
286   }
287 }
288 
SetMuteForDevice(uint64 device_id,bool mute_on)289 void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) {
290   if (device_id == active_output_node_id_) {
291     SetOutputMute(mute_on);
292     return;
293   } else if (device_id == active_input_node_id_) {
294     VLOG(1) << "SetMuteForDevice sets active input device id="
295             << "0x" << std::hex << device_id << " mute=" << mute_on;
296     SetInputMute(mute_on);
297     return;
298   }
299 
300   const AudioDevice* device = GetDeviceFromId(device_id);
301   // Input device's mute state is not recorded in the pref. crbug.com/365050.
302   if (device && !device->is_input)
303     audio_pref_handler_->SetMuteValue(*device, mute_on);
304 }
305 
LogErrors()306 void CrasAudioHandler::LogErrors() {
307   log_errors_ = true;
308 }
309 
CrasAudioHandler(scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)310 CrasAudioHandler::CrasAudioHandler(
311     scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
312     : audio_pref_handler_(audio_pref_handler),
313       weak_ptr_factory_(this),
314       output_mute_on_(false),
315       input_mute_on_(false),
316       output_volume_(0),
317       input_gain_(0),
318       active_output_node_id_(0),
319       active_input_node_id_(0),
320       has_alternative_input_(false),
321       has_alternative_output_(false),
322       output_mute_locked_(false),
323       input_mute_locked_(false),
324       log_errors_(false) {
325   if (!audio_pref_handler.get())
326     return;
327   // If the DBusThreadManager or the CrasAudioClient aren't available, there
328   // isn't much we can do. This should only happen when running tests.
329   if (!chromeos::DBusThreadManager::IsInitialized() ||
330       !chromeos::DBusThreadManager::Get() ||
331       !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
332     return;
333   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
334   audio_pref_handler_->AddAudioPrefObserver(this);
335   if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
336     chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
337         AddObserver(this);
338   }
339   InitializeAudioState();
340 }
341 
~CrasAudioHandler()342 CrasAudioHandler::~CrasAudioHandler() {
343   if (!chromeos::DBusThreadManager::IsInitialized() ||
344       !chromeos::DBusThreadManager::Get() ||
345       !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
346     return;
347   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
348       RemoveObserver(this);
349   chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
350       RemoveObserver(this);
351   if (audio_pref_handler_.get())
352     audio_pref_handler_->RemoveAudioPrefObserver(this);
353   audio_pref_handler_ = NULL;
354 }
355 
AudioClientRestarted()356 void CrasAudioHandler::AudioClientRestarted() {
357   // Make sure the logging is enabled in case cras server
358   // restarts after crashing.
359   LogErrors();
360   InitializeAudioState();
361 }
362 
NodesChanged()363 void CrasAudioHandler::NodesChanged() {
364   // Refresh audio nodes data.
365   GetNodes();
366 }
367 
ActiveOutputNodeChanged(uint64 node_id)368 void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) {
369   if (active_output_node_id_ == node_id)
370     return;
371 
372   // Active audio output device should always be changed by chrome.
373   // During system boot, cras may change active input to unknown device 0x1,
374   // we don't need to log it, since it is not an valid device.
375   if (GetDeviceFromId(node_id)) {
376     LOG_IF(WARNING, log_errors_)
377         << "Active output node changed unexpectedly by system node_id="
378         << "0x" << std::hex << node_id;
379   }
380 }
381 
ActiveInputNodeChanged(uint64 node_id)382 void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) {
383   if (active_input_node_id_ == node_id)
384     return;
385 
386   // Active audio input device should always be changed by chrome.
387   // During system boot, cras may change active input to unknown device 0x2,
388   // we don't need to log it, since it is not an valid device.
389   if (GetDeviceFromId(node_id)) {
390     LOG_IF(WARNING, log_errors_)
391         << "Active input node changed unexpectedly by system node_id="
392         << "0x" << std::hex << node_id;
393   }
394 }
395 
OnAudioPolicyPrefChanged()396 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
397   ApplyAudioPolicy();
398 }
399 
EmitLoginPromptVisibleCalled()400 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
401   // Enable logging after cras server is started, which will be after
402   // EmitLoginPromptVisible.
403   LogErrors();
404 }
405 
GetDeviceFromId(uint64 device_id) const406 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64 device_id) const {
407   AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
408   if (it == audio_devices_.end())
409     return NULL;
410 
411   return &(it->second);
412 }
413 
SetupAudioInputState()414 void CrasAudioHandler::SetupAudioInputState() {
415   // Set the initial audio state to the ones read from audio prefs.
416   const AudioDevice* device = GetDeviceFromId(active_input_node_id_);
417   if (!device) {
418     LOG_IF(ERROR, log_errors_)
419         << "Can't set up audio state for unknown input device id ="
420         << "0x" << std::hex << active_input_node_id_;
421     return;
422   }
423   input_gain_ = audio_pref_handler_->GetInputGainValue(device);
424   VLOG(1) << "SetupAudioInputState for active device id="
425           << "0x" << std::hex << device->id << " mute=" << input_mute_on_;
426   SetInputMuteInternal(input_mute_on_);
427   // TODO(rkc,jennyz): Set input gain once we decide on how to store
428   // the gain values since the range and step are both device specific.
429 }
430 
SetupAudioOutputState()431 void CrasAudioHandler::SetupAudioOutputState() {
432   const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
433   if (!device) {
434     LOG_IF(ERROR, log_errors_)
435         << "Can't set up audio state for unknown output device id ="
436         << "0x" << std::hex << active_output_node_id_;
437     return;
438   }
439   DCHECK(!device->is_input);
440   output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
441   output_volume_ = audio_pref_handler_->GetOutputVolumeValue(device);
442 
443   SetOutputMuteInternal(output_mute_on_);
444   SetOutputNodeVolume(active_output_node_id_, output_volume_);
445 }
446 
InitializeAudioState()447 void CrasAudioHandler::InitializeAudioState() {
448   ApplyAudioPolicy();
449   GetNodes();
450 }
451 
ApplyAudioPolicy()452 void CrasAudioHandler::ApplyAudioPolicy() {
453   output_mute_locked_ = false;
454   if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
455     // Mute the device, but do not update the preference.
456     SetOutputMuteInternal(true);
457     output_mute_locked_ = true;
458   } else {
459     // Restore the mute state.
460     const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
461     if (device)
462       SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
463   }
464 
465   input_mute_locked_ = false;
466   if (audio_pref_handler_->GetAudioCaptureAllowedValue()) {
467     VLOG(1) << "Audio input allowed by policy, sets input id="
468             << "0x" << std::hex << active_input_node_id_ << " mute=false";
469     SetInputMuteInternal(false);
470   } else {
471     VLOG(0) << "Audio input NOT allowed by policy, sets input id="
472             << "0x" << std::hex << active_input_node_id_ << " mute=true";
473     SetInputMuteInternal(true);
474     input_mute_locked_ = true;
475   }
476 }
477 
SetOutputNodeVolume(uint64 node_id,int volume)478 void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
479   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
480       SetOutputNodeVolume(node_id, volume);
481 }
482 
SetOutputMuteInternal(bool mute_on)483 bool  CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
484   if (output_mute_locked_)
485     return false;
486 
487   output_mute_on_ = mute_on;
488   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
489       SetOutputUserMute(mute_on);
490   return true;
491 }
492 
SetInputNodeGain(uint64 node_id,int gain)493 void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
494   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
495       SetInputNodeGain(node_id, gain);
496 }
497 
SetInputMuteInternal(bool mute_on)498 bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
499   if (input_mute_locked_)
500     return false;
501 
502   VLOG(1) << "SetInputMuteInternal sets active input device id="
503           << "0x" << std::hex << active_input_node_id_ << " mute=" << mute_on;
504   input_mute_on_ = mute_on;
505   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
506       SetInputMute(mute_on);
507   return true;
508 }
509 
GetNodes()510 void CrasAudioHandler::GetNodes() {
511   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
512       base::Bind(&CrasAudioHandler::HandleGetNodes,
513                  weak_ptr_factory_.GetWeakPtr()),
514       base::Bind(&CrasAudioHandler::HandleGetNodesError,
515                  weak_ptr_factory_.GetWeakPtr()));
516 }
517 
ChangeActiveDevice(const AudioDevice & new_active_device,uint64 * current_active_node_id)518 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device,
519                                           uint64* current_active_node_id) {
520   // If the device we want to switch to is already the current active device,
521   // do nothing.
522   if (new_active_device.active &&
523       new_active_device.id == *current_active_node_id) {
524     return false;
525   }
526 
527   // Reset all other input or output devices' active status. The active audio
528   // device from the previous user session can be remembered by cras, but not
529   // in chrome. see crbug.com/273271.
530   for (AudioDeviceMap::iterator it = audio_devices_.begin();
531        it != audio_devices_.end(); ++it) {
532     if (it->second.is_input == new_active_device.is_input &&
533         it->second.id != new_active_device.id)
534       it->second.active = false;
535   }
536 
537   // Set the current active input/output device to the new_active_device.
538   *current_active_node_id = new_active_device.id;
539   audio_devices_[*current_active_node_id].active = true;
540   return true;
541 }
542 
NonActiveDeviceUnplugged(size_t old_devices_size,size_t new_devices_size,uint64 current_active_node)543 bool CrasAudioHandler::NonActiveDeviceUnplugged(
544     size_t old_devices_size,
545     size_t new_devices_size,
546     uint64 current_active_node) {
547   return (new_devices_size < old_devices_size &&
548           GetDeviceFromId(current_active_node));
549 }
550 
SwitchToDevice(const AudioDevice & device)551 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device) {
552   if (device.is_input) {
553     if (!ChangeActiveDevice(device, &active_input_node_id_))
554       return;
555     SetupAudioInputState();
556     SetActiveInputNode(active_input_node_id_);
557   } else {
558     if (!ChangeActiveDevice(device, &active_output_node_id_))
559       return;
560     SetupAudioOutputState();
561     SetActiveOutputNode(active_output_node_id_);
562   }
563 }
564 
HasDeviceChange(const AudioNodeList & new_nodes,bool is_input)565 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
566                                        bool is_input) {
567   size_t num_old_devices = 0;
568   size_t num_new_devices = 0;
569   for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
570        it != audio_devices_.end(); ++it) {
571     if (is_input == it->second.is_input)
572       ++num_old_devices;
573   }
574 
575   for (AudioNodeList::const_iterator it = new_nodes.begin();
576        it != new_nodes.end(); ++it) {
577     if (is_input == it->is_input) {
578       ++num_new_devices;
579       // Look to see if the new device not in the old device list.
580       AudioDevice device(*it);
581       if (FoundNewDevice(device))
582         return true;
583     }
584   }
585   return num_old_devices != num_new_devices;
586 }
587 
FoundNewDevice(const AudioDevice & device)588 bool CrasAudioHandler::FoundNewDevice(const AudioDevice& device) {
589   const AudioDevice* device_found = GetDeviceFromId(device.id);
590   if (!device_found)
591     return true;
592 
593   if (!IsSameAudioDevice(device, *device_found)) {
594     LOG(WARNING) << "Different Audio devices with same id:"
595         << " new device: " << device.ToString()
596         << " old device: " << device_found->ToString();
597     return true;
598   }
599   return false;
600 }
601 
602 // Sanitize the audio node data. When a device is plugged in or unplugged, there
603 // should be only one NodesChanged signal from cras. However, we've observed
604 // the case that multiple NodesChanged signals being sent from cras. After the
605 // first NodesChanged being processed, chrome sets the active node properly.
606 // However, the NodesChanged received after the first one, can return stale
607 // nodes data in GetNodes call, the staled nodes data does not reflect the
608 // latest active node state. Since active audio node should only be set by
609 // chrome, the inconsistent data from cras could be the result of stale data
610 // described above and sanitized.
GetSanitizedAudioDevice(const AudioNode & node)611 AudioDevice CrasAudioHandler::GetSanitizedAudioDevice(const AudioNode& node) {
612   AudioDevice device(node);
613   if (device.is_input) {
614     if (device.active && device.id != active_input_node_id_) {
615       LOG(WARNING) << "Stale audio device data, should not be active: "
616           << " device = " << device.ToString()
617           << " current active input node id = 0x" << std::hex
618           << active_input_node_id_;
619       device.active = false;
620     } else if (device.id == active_input_node_id_ && !device.active) {
621       LOG(WARNING) << "Stale audio device data, should be active:"
622           << " device = " << device.ToString()
623           << " current active input node id = 0x" << std::hex
624           << active_input_node_id_;
625       device.active = true;
626     }
627   } else {
628     if (device.active && device.id != active_output_node_id_) {
629       LOG(WARNING) << "Stale audio device data, should not be active: "
630           << " device = " << device.ToString()
631           << " current active output node id = 0x" << std::hex
632           << active_output_node_id_;
633       device.active = false;
634     } else if (device.id == active_output_node_id_ && !device.active) {
635       LOG(WARNING) << "Stale audio device data, should be active:"
636           << " device = " << device.ToString()
637           << " current active output node id = 0x" << std::hex
638           << active_output_node_id_;
639       device.active = true;
640     }
641   }
642   return device;
643 }
644 
UpdateDevicesAndSwitchActive(const AudioNodeList & nodes)645 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
646     const AudioNodeList& nodes) {
647   size_t old_audio_devices_size = audio_devices_.size();
648   bool output_devices_changed = HasDeviceChange(nodes, false);
649   bool input_devices_changed = HasDeviceChange(nodes, true);
650   audio_devices_.clear();
651   has_alternative_input_ = false;
652   has_alternative_output_ = false;
653 
654   while (!input_devices_pq_.empty())
655     input_devices_pq_.pop();
656   while (!output_devices_pq_.empty())
657     output_devices_pq_.pop();
658 
659   for (size_t i = 0; i < nodes.size(); ++i) {
660     AudioDevice device = GetSanitizedAudioDevice(nodes[i]);
661     audio_devices_[device.id] = device;
662 
663     if (!has_alternative_input_ &&
664         device.is_input &&
665         device.type != AUDIO_TYPE_INTERNAL_MIC) {
666       has_alternative_input_ = true;
667     } else if (!has_alternative_output_ &&
668                !device.is_input &&
669                device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
670       has_alternative_output_ = true;
671     }
672 
673     if (device.is_input)
674       input_devices_pq_.push(device);
675     else
676       output_devices_pq_.push(device);
677   }
678 
679   // If audio nodes change is caused by unplugging some non-active audio
680   // devices, the previously set active audio device will stay active.
681   // Otherwise, switch to a new active audio device according to their priority.
682   if (input_devices_changed &&
683       !NonActiveDeviceUnplugged(old_audio_devices_size,
684                                 audio_devices_.size(),
685                                 active_input_node_id_) &&
686       !input_devices_pq_.empty())
687     SwitchToDevice(input_devices_pq_.top());
688   if (output_devices_changed &&
689       !NonActiveDeviceUnplugged(old_audio_devices_size,
690                                 audio_devices_.size(),
691                                 active_output_node_id_) &&
692       !output_devices_pq_.empty()) {
693     SwitchToDevice(output_devices_pq_.top());
694   }
695 }
696 
HandleGetNodes(const chromeos::AudioNodeList & node_list,bool success)697 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
698                                       bool success) {
699   if (!success) {
700     LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data";
701     return;
702   }
703 
704   UpdateDevicesAndSwitchActive(node_list);
705   FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
706 }
707 
HandleGetNodesError(const std::string & error_name,const std::string & error_msg)708 void CrasAudioHandler::HandleGetNodesError(const std::string& error_name,
709                                            const std::string& error_msg) {
710   LOG_IF(ERROR, log_errors_) << "Failed to call GetNodes: "
711       << error_name  << ": " << error_msg;
712 }
713 }  // namespace chromeos
714