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