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