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
IsInNodeList(uint64 node_id,const CrasAudioHandler::NodeIdList & id_list)38 bool IsInNodeList(uint64 node_id, const CrasAudioHandler::NodeIdList& id_list) {
39 return std::find(id_list.begin(), id_list.end(), node_id) != id_list.end();
40 }
41
42 } // namespace
43
AudioObserver()44 CrasAudioHandler::AudioObserver::AudioObserver() {
45 }
46
~AudioObserver()47 CrasAudioHandler::AudioObserver::~AudioObserver() {
48 }
49
OnOutputVolumeChanged()50 void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
51 }
52
OnInputGainChanged()53 void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
54 }
55
OnOutputMuteChanged()56 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
57 }
58
OnInputMuteChanged()59 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
60 }
61
OnAudioNodesChanged()62 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
63 }
64
OnActiveOutputNodeChanged()65 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
66 }
67
OnActiveInputNodeChanged()68 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
69 }
70
71 // static
Initialize(scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)72 void CrasAudioHandler::Initialize(
73 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) {
74 CHECK(!g_cras_audio_handler);
75 g_cras_audio_handler = new CrasAudioHandler(audio_pref_handler);
76 }
77
78 // static
InitializeForTesting()79 void CrasAudioHandler::InitializeForTesting() {
80 CHECK(!g_cras_audio_handler);
81 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
82 }
83
84 // static
Shutdown()85 void CrasAudioHandler::Shutdown() {
86 CHECK(g_cras_audio_handler);
87 delete g_cras_audio_handler;
88 g_cras_audio_handler = NULL;
89 }
90
91 // static
IsInitialized()92 bool CrasAudioHandler::IsInitialized() {
93 return g_cras_audio_handler != NULL;
94 }
95
96 // static
Get()97 CrasAudioHandler* CrasAudioHandler::Get() {
98 CHECK(g_cras_audio_handler)
99 << "CrasAudioHandler::Get() called before Initialize().";
100 return g_cras_audio_handler;
101 }
102
AddAudioObserver(AudioObserver * observer)103 void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
104 observers_.AddObserver(observer);
105 }
106
RemoveAudioObserver(AudioObserver * observer)107 void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
108 observers_.RemoveObserver(observer);
109 }
110
HasKeyboardMic()111 bool CrasAudioHandler::HasKeyboardMic() {
112 return GetKeyboardMic() != NULL;
113 }
114
IsOutputMuted()115 bool CrasAudioHandler::IsOutputMuted() {
116 return output_mute_on_;
117 }
118
IsOutputMutedForDevice(uint64 device_id)119 bool CrasAudioHandler::IsOutputMutedForDevice(uint64 device_id) {
120 const AudioDevice* device = GetDeviceFromId(device_id);
121 if (!device)
122 return false;
123 DCHECK(!device->is_input);
124 return audio_pref_handler_->GetMuteValue(*device);
125 }
126
IsOutputVolumeBelowDefaultMuteLevel()127 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
128 return output_volume_ <= kMuteThresholdPercent;
129 }
130
IsInputMuted()131 bool CrasAudioHandler::IsInputMuted() {
132 return input_mute_on_;
133 }
134
IsInputMutedForDevice(uint64 device_id)135 bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id) {
136 const AudioDevice* device = GetDeviceFromId(device_id);
137 if (!device)
138 return false;
139 DCHECK(device->is_input);
140 // We don't record input mute state for each device in the prefs,
141 // for any non-active input device, we assume mute is off.
142 if (device->id == active_input_node_id_)
143 return input_mute_on_;
144 return false;
145 }
146
GetOutputDefaultVolumeMuteThreshold()147 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
148 return kMuteThresholdPercent;
149 }
150
GetOutputVolumePercent()151 int CrasAudioHandler::GetOutputVolumePercent() {
152 return output_volume_;
153 }
154
GetOutputVolumePercentForDevice(uint64 device_id)155 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64 device_id) {
156 if (device_id == active_output_node_id_) {
157 return output_volume_;
158 } else {
159 const AudioDevice* device = GetDeviceFromId(device_id);
160 return static_cast<int>(audio_pref_handler_->GetOutputVolumeValue(device));
161 }
162 }
163
GetInputGainPercent()164 int CrasAudioHandler::GetInputGainPercent() {
165 return input_gain_;
166 }
167
GetInputGainPercentForDevice(uint64 device_id)168 int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id) {
169 if (device_id == active_input_node_id_) {
170 return input_gain_;
171 } else {
172 const AudioDevice* device = GetDeviceFromId(device_id);
173 return static_cast<int>(audio_pref_handler_->GetInputGainValue(device));
174 }
175 }
176
GetPrimaryActiveOutputNode() const177 uint64 CrasAudioHandler::GetPrimaryActiveOutputNode() const {
178 return active_output_node_id_;
179 }
180
GetPrimaryActiveInputNode() const181 uint64 CrasAudioHandler::GetPrimaryActiveInputNode() const {
182 return active_input_node_id_;
183 }
184
GetAudioDevices(AudioDeviceList * device_list) const185 void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
186 device_list->clear();
187 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
188 it != audio_devices_.end(); ++it)
189 device_list->push_back(it->second);
190 }
191
GetPrimaryActiveOutputDevice(AudioDevice * device) const192 bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice* device) const {
193 const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_);
194 if (!active_device || !device)
195 return false;
196 *device = *active_device;
197 return true;
198 }
199
SetKeyboardMicActive(bool active)200 void CrasAudioHandler::SetKeyboardMicActive(bool active) {
201 const AudioDevice* keyboard_mic = GetKeyboardMic();
202 if (!keyboard_mic)
203 return;
204 // Keyboard mic is invisible to chromeos users. It is always added or removed
205 // as additional active node.
206 DCHECK(active_input_node_id_ && active_input_node_id_ != keyboard_mic->id);
207 if (active)
208 AddActiveNode(keyboard_mic->id, true);
209 else
210 RemoveActiveNodeInternal(keyboard_mic->id, true);
211 }
212
AddActiveNode(uint64 node_id,bool notify)213 void CrasAudioHandler::AddActiveNode(uint64 node_id, bool notify) {
214 const AudioDevice* device = GetDeviceFromId(node_id);
215 if (!device) {
216 VLOG(1) << "AddActiveInputNode: Cannot find device id="
217 << "0x" << std::hex << node_id;
218 return;
219 }
220
221 // If there is no primary active device, set |node_id| to primary active node.
222 if ((device->is_input && !active_input_node_id_) ||
223 (!device->is_input && !active_output_node_id_)) {
224 SwitchToDevice(*device, notify);
225 return;
226 }
227
228 AddAdditionalActiveNode(node_id, notify);
229 }
230
ChangeActiveNodes(const NodeIdList & new_active_ids)231 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList& new_active_ids) {
232 // Flags for whether there are input or output nodes passed in from
233 // |new_active_ids|. If there are no input nodes passed in, we will not
234 // make any change for input nodes; same for the output nodes.
235 bool request_input_change = false;
236 bool request_output_change = false;
237
238 // Flags for whether we will actually change active status of input
239 // or output nodes.
240 bool make_input_change = false;
241 bool make_output_change = false;
242
243 NodeIdList nodes_to_activate;
244 for (size_t i = 0; i < new_active_ids.size(); ++i) {
245 const AudioDevice* device = GetDeviceFromId(new_active_ids[i]);
246 if (device) {
247 if (device->is_input)
248 request_input_change = true;
249 else
250 request_output_change = true;
251
252 // If the new active device is already active, keep it as active.
253 if (device->active)
254 continue;
255
256 nodes_to_activate.push_back(new_active_ids[i]);
257 if (device->is_input)
258 make_input_change = true;
259 else
260 make_output_change = true;
261 }
262 }
263
264 // Remove all existing active devices that are not in the |new_active_ids|
265 // list.
266 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
267 it != audio_devices_.end(); ++it) {
268 AudioDevice device = it->second;
269 // Remove the existing active input or output nodes that are not in the new
270 // active node list if there are new input or output nodes specified.
271 if (device.active) {
272 if ((device.is_input && request_input_change &&
273 !IsInNodeList(device.id, new_active_ids))) {
274 make_input_change = true;
275 RemoveActiveNodeInternal(device.id, false); // no notification.
276 } else if (!device.is_input && request_output_change &&
277 !IsInNodeList(device.id, new_active_ids)) {
278 make_output_change = true;
279 RemoveActiveNodeInternal(device.id, false); // no notification.
280 }
281 }
282 }
283
284 // Adds the new active devices.
285 for (size_t i = 0; i < nodes_to_activate.size(); ++i)
286 AddActiveNode(nodes_to_activate[i], false); // no notification.
287
288 // Notify the active nodes change now.
289 if (make_input_change)
290 NotifyActiveNodeChanged(true);
291 if (make_output_change)
292 NotifyActiveNodeChanged(false);
293 }
294
has_alternative_input() const295 bool CrasAudioHandler::has_alternative_input() const {
296 return has_alternative_input_;
297 }
298
has_alternative_output() const299 bool CrasAudioHandler::has_alternative_output() const {
300 return has_alternative_output_;
301 }
302
SetOutputVolumePercent(int volume_percent)303 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
304 // Set all active devices to the same volume.
305 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
306 it != audio_devices_.end();
307 it++) {
308 const AudioDevice& device = it->second;
309 if (!device.is_input && device.active)
310 SetOutputNodeVolumePercent(device.id, volume_percent);
311 }
312 }
313
314 // TODO: Rename the 'Percent' to something more meaningful.
SetInputGainPercent(int gain_percent)315 void CrasAudioHandler::SetInputGainPercent(int gain_percent) {
316 // TODO(jennyz): Should we set all input devices' gain to the same level?
317 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
318 it != audio_devices_.end();
319 it++) {
320 const AudioDevice& device = it->second;
321 if (device.is_input && device.active)
322 SetInputNodeGainPercent(active_input_node_id_, gain_percent);
323 }
324 }
325
AdjustOutputVolumeByPercent(int adjust_by_percent)326 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
327 SetOutputVolumePercent(output_volume_ + adjust_by_percent);
328 }
329
SetOutputMute(bool mute_on)330 void CrasAudioHandler::SetOutputMute(bool mute_on) {
331 if (!SetOutputMuteInternal(mute_on))
332 return;
333
334 // Save the mute state for all active output audio devices.
335 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
336 it != audio_devices_.end();
337 it++) {
338 const AudioDevice& device = it->second;
339 if (!device.is_input && device.active) {
340 audio_pref_handler_->SetMuteValue(device, output_mute_on_);
341 }
342 }
343
344 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged());
345 }
346
AdjustOutputVolumeToAudibleLevel()347 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
348 if (output_volume_ <= kMuteThresholdPercent) {
349 // Avoid the situation when sound has been unmuted, but the volume
350 // is set to a very low value, so user still can't hear any sound.
351 SetOutputVolumePercent(kDefaultUnmuteVolumePercent);
352 }
353 }
354
SetInputMute(bool mute_on)355 void CrasAudioHandler::SetInputMute(bool mute_on) {
356 if (!SetInputMuteInternal(mute_on))
357 return;
358
359 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
360 }
361
SetActiveOutputNode(uint64 node_id,bool notify)362 void CrasAudioHandler::SetActiveOutputNode(uint64 node_id, bool notify) {
363 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
364 SetActiveOutputNode(node_id);
365 if (notify)
366 NotifyActiveNodeChanged(false);
367 }
368
SetActiveInputNode(uint64 node_id,bool notify)369 void CrasAudioHandler::SetActiveInputNode(uint64 node_id, bool notify) {
370 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
371 SetActiveInputNode(node_id);
372 if (notify)
373 NotifyActiveNodeChanged(true);
374 }
375
SetVolumeGainPercentForDevice(uint64 device_id,int value)376 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id,
377 int value) {
378 const AudioDevice* device = GetDeviceFromId(device_id);
379 if (!device)
380 return;
381
382 if (device->is_input)
383 SetInputNodeGainPercent(device_id, value);
384 else
385 SetOutputNodeVolumePercent(device_id, value);
386 }
387
SetMuteForDevice(uint64 device_id,bool mute_on)388 void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) {
389 if (device_id == active_output_node_id_) {
390 SetOutputMute(mute_on);
391 return;
392 } else if (device_id == active_input_node_id_) {
393 VLOG(1) << "SetMuteForDevice sets active input device id="
394 << "0x" << std::hex << device_id << " mute=" << mute_on;
395 SetInputMute(mute_on);
396 return;
397 }
398
399 const AudioDevice* device = GetDeviceFromId(device_id);
400 // Input device's mute state is not recorded in the pref. crbug.com/365050.
401 if (device && !device->is_input)
402 audio_pref_handler_->SetMuteValue(*device, mute_on);
403 }
404
LogErrors()405 void CrasAudioHandler::LogErrors() {
406 log_errors_ = true;
407 }
408
CrasAudioHandler(scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)409 CrasAudioHandler::CrasAudioHandler(
410 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
411 : audio_pref_handler_(audio_pref_handler),
412 output_mute_on_(false),
413 input_mute_on_(false),
414 output_volume_(0),
415 input_gain_(0),
416 active_output_node_id_(0),
417 active_input_node_id_(0),
418 has_alternative_input_(false),
419 has_alternative_output_(false),
420 output_mute_locked_(false),
421 input_mute_locked_(false),
422 log_errors_(false),
423 weak_ptr_factory_(this) {
424 if (!audio_pref_handler.get())
425 return;
426 // If the DBusThreadManager or the CrasAudioClient aren't available, there
427 // isn't much we can do. This should only happen when running tests.
428 if (!chromeos::DBusThreadManager::IsInitialized() ||
429 !chromeos::DBusThreadManager::Get() ||
430 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
431 return;
432 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
433 audio_pref_handler_->AddAudioPrefObserver(this);
434 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
435 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
436 AddObserver(this);
437 }
438 InitializeAudioState();
439 }
440
~CrasAudioHandler()441 CrasAudioHandler::~CrasAudioHandler() {
442 if (!chromeos::DBusThreadManager::IsInitialized() ||
443 !chromeos::DBusThreadManager::Get() ||
444 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
445 return;
446 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
447 RemoveObserver(this);
448 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
449 RemoveObserver(this);
450 if (audio_pref_handler_.get())
451 audio_pref_handler_->RemoveAudioPrefObserver(this);
452 audio_pref_handler_ = NULL;
453 }
454
AudioClientRestarted()455 void CrasAudioHandler::AudioClientRestarted() {
456 // Make sure the logging is enabled in case cras server
457 // restarts after crashing.
458 LogErrors();
459 InitializeAudioState();
460 }
461
NodesChanged()462 void CrasAudioHandler::NodesChanged() {
463 // Refresh audio nodes data.
464 GetNodes();
465 }
466
ActiveOutputNodeChanged(uint64 node_id)467 void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) {
468 if (active_output_node_id_ == node_id)
469 return;
470
471 // Active audio output device should always be changed by chrome.
472 // During system boot, cras may change active input to unknown device 0x1,
473 // we don't need to log it, since it is not an valid device.
474 if (GetDeviceFromId(node_id)) {
475 LOG_IF(WARNING, log_errors_)
476 << "Active output node changed unexpectedly by system node_id="
477 << "0x" << std::hex << node_id;
478 }
479 }
480
ActiveInputNodeChanged(uint64 node_id)481 void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) {
482 if (active_input_node_id_ == node_id)
483 return;
484
485 // Active audio input device should always be changed by chrome.
486 // During system boot, cras may change active input to unknown device 0x2,
487 // we don't need to log it, since it is not an valid device.
488 if (GetDeviceFromId(node_id)) {
489 LOG_IF(WARNING, log_errors_)
490 << "Active input node changed unexpectedly by system node_id="
491 << "0x" << std::hex << node_id;
492 }
493 }
494
OnAudioPolicyPrefChanged()495 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
496 ApplyAudioPolicy();
497 }
498
EmitLoginPromptVisibleCalled()499 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
500 // Enable logging after cras server is started, which will be after
501 // EmitLoginPromptVisible.
502 LogErrors();
503 }
504
GetDeviceFromId(uint64 device_id) const505 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64 device_id) const {
506 AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
507 if (it == audio_devices_.end())
508 return NULL;
509
510 return &(it->second);
511 }
512
GetKeyboardMic() const513 const AudioDevice* CrasAudioHandler::GetKeyboardMic() const {
514 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
515 it != audio_devices_.end(); it++) {
516 if (it->second.is_input && it->second.type == AUDIO_TYPE_KEYBOARD_MIC)
517 return &(it->second);
518 }
519 return NULL;
520 }
521
SetupAudioInputState()522 void CrasAudioHandler::SetupAudioInputState() {
523 // Set the initial audio state to the ones read from audio prefs.
524 const AudioDevice* device = GetDeviceFromId(active_input_node_id_);
525 if (!device) {
526 LOG_IF(ERROR, log_errors_)
527 << "Can't set up audio state for unknown input device id ="
528 << "0x" << std::hex << active_input_node_id_;
529 return;
530 }
531 input_gain_ = audio_pref_handler_->GetInputGainValue(device);
532 VLOG(1) << "SetupAudioInputState for active device id="
533 << "0x" << std::hex << device->id << " mute=" << input_mute_on_;
534 SetInputMuteInternal(input_mute_on_);
535 // TODO(rkc,jennyz): Set input gain once we decide on how to store
536 // the gain values since the range and step are both device specific.
537 }
538
SetupAudioOutputState()539 void CrasAudioHandler::SetupAudioOutputState() {
540 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
541 if (!device) {
542 LOG_IF(ERROR, log_errors_)
543 << "Can't set up audio state for unknown output device id ="
544 << "0x" << std::hex << active_output_node_id_;
545 return;
546 }
547 DCHECK(!device->is_input);
548 output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
549 output_volume_ = audio_pref_handler_->GetOutputVolumeValue(device);
550
551 SetOutputMuteInternal(output_mute_on_);
552 SetOutputNodeVolume(active_output_node_id_, output_volume_);
553 }
554
555 // This sets up the state of an additional active node.
SetupAdditionalActiveAudioNodeState(uint64 node_id)556 void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64 node_id) {
557 const AudioDevice* device = GetDeviceFromId(node_id);
558 if (!device) {
559 VLOG(1) << "Can't set up audio state for unknown device id ="
560 << "0x" << std::hex << node_id;
561 return;
562 }
563
564 DCHECK(node_id != active_output_node_id_ && node_id != active_input_node_id_);
565
566 // Note: The mute state is a system wide state, we don't set mute per device,
567 // but just keep the mute state consistent for the active node in prefs.
568 // The output volume should be set to the same value for all active output
569 // devices. For input devices, we don't restore their gain value so far.
570 // TODO(jennyz): crbug.com/417418, track the status for the decison if
571 // we should persist input gain value in prefs.
572 if (!device->is_input) {
573 audio_pref_handler_->SetMuteValue(*device, IsOutputMuted());
574 SetOutputNodeVolumePercent(node_id, GetOutputVolumePercent());
575 }
576 }
577
InitializeAudioState()578 void CrasAudioHandler::InitializeAudioState() {
579 ApplyAudioPolicy();
580 GetNodes();
581 }
582
ApplyAudioPolicy()583 void CrasAudioHandler::ApplyAudioPolicy() {
584 output_mute_locked_ = false;
585 if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
586 // Mute the device, but do not update the preference.
587 SetOutputMuteInternal(true);
588 output_mute_locked_ = true;
589 } else {
590 // Restore the mute state.
591 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
592 if (device)
593 SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
594 }
595
596 input_mute_locked_ = false;
597 if (audio_pref_handler_->GetAudioCaptureAllowedValue()) {
598 VLOG(1) << "Audio input allowed by policy, sets input id="
599 << "0x" << std::hex << active_input_node_id_ << " mute=false";
600 SetInputMuteInternal(false);
601 } else {
602 VLOG(0) << "Audio input NOT allowed by policy, sets input id="
603 << "0x" << std::hex << active_input_node_id_ << " mute=true";
604 SetInputMuteInternal(true);
605 input_mute_locked_ = true;
606 }
607 }
608
SetOutputNodeVolume(uint64 node_id,int volume)609 void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
610 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
611 SetOutputNodeVolume(node_id, volume);
612 }
613
SetOutputNodeVolumePercent(uint64 node_id,int volume_percent)614 void CrasAudioHandler::SetOutputNodeVolumePercent(uint64 node_id,
615 int volume_percent) {
616 const AudioDevice* device = this->GetDeviceFromId(node_id);
617 if (!device || device->is_input)
618 return;
619
620 volume_percent = min(max(volume_percent, 0), 100);
621 if (volume_percent <= kMuteThresholdPercent)
622 volume_percent = 0;
623 if (node_id == active_output_node_id_)
624 output_volume_ = volume_percent;
625
626 audio_pref_handler_->SetVolumeGainValue(*device, volume_percent);
627
628 if (device->active) {
629 SetOutputNodeVolume(node_id, volume_percent);
630 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged());
631 }
632 }
633
SetOutputMuteInternal(bool mute_on)634 bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
635 if (output_mute_locked_)
636 return false;
637
638 output_mute_on_ = mute_on;
639 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
640 SetOutputUserMute(mute_on);
641 return true;
642 }
643
SetInputNodeGain(uint64 node_id,int gain)644 void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
645 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
646 SetInputNodeGain(node_id, gain);
647 }
648
SetInputNodeGainPercent(uint64 node_id,int gain_percent)649 void CrasAudioHandler::SetInputNodeGainPercent(uint64 node_id,
650 int gain_percent) {
651 const AudioDevice* device = GetDeviceFromId(node_id);
652 if (!device || !device->is_input)
653 return;
654
655 // NOTE: We do not sanitize input gain values since the range is completely
656 // dependent on the device.
657 if (active_input_node_id_ == node_id)
658 input_gain_ = gain_percent;
659
660 audio_pref_handler_->SetVolumeGainValue(*device, gain_percent);
661
662 if (device->active) {
663 SetInputNodeGain(node_id, gain_percent);
664 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged());
665 }
666 }
667
SetInputMuteInternal(bool mute_on)668 bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
669 if (input_mute_locked_)
670 return false;
671
672 input_mute_on_ = mute_on;
673 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
674 SetInputMute(mute_on);
675 return true;
676 }
677
GetNodes()678 void CrasAudioHandler::GetNodes() {
679 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
680 base::Bind(&CrasAudioHandler::HandleGetNodes,
681 weak_ptr_factory_.GetWeakPtr()),
682 base::Bind(&CrasAudioHandler::HandleGetNodesError,
683 weak_ptr_factory_.GetWeakPtr()));
684 }
685
ChangeActiveDevice(const AudioDevice & new_active_device,uint64 * current_active_node_id)686 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device,
687 uint64* current_active_node_id) {
688 // If the device we want to switch to is already the current active device,
689 // do nothing.
690 if (new_active_device.active &&
691 new_active_device.id == *current_active_node_id) {
692 return false;
693 }
694
695 // Reset all other input or output devices' active status. The active audio
696 // device from the previous user session can be remembered by cras, but not
697 // in chrome. see crbug.com/273271.
698 for (AudioDeviceMap::iterator it = audio_devices_.begin();
699 it != audio_devices_.end(); ++it) {
700 if (it->second.is_input == new_active_device.is_input &&
701 it->second.id != new_active_device.id)
702 it->second.active = false;
703 }
704
705 // Set the current active input/output device to the new_active_device.
706 *current_active_node_id = new_active_device.id;
707 audio_devices_[*current_active_node_id].active = true;
708 return true;
709 }
710
NonActiveDeviceUnplugged(size_t old_devices_size,size_t new_devices_size,uint64 current_active_node)711 bool CrasAudioHandler::NonActiveDeviceUnplugged(
712 size_t old_devices_size,
713 size_t new_devices_size,
714 uint64 current_active_node) {
715 return (new_devices_size < old_devices_size &&
716 GetDeviceFromId(current_active_node));
717 }
718
SwitchToDevice(const AudioDevice & device,bool notify)719 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device, bool notify) {
720 if (device.is_input) {
721 if (!ChangeActiveDevice(device, &active_input_node_id_))
722 return;
723 SetupAudioInputState();
724 SetActiveInputNode(active_input_node_id_, notify);
725 } else {
726 if (!ChangeActiveDevice(device, &active_output_node_id_))
727 return;
728 SetupAudioOutputState();
729 SetActiveOutputNode(active_output_node_id_, notify);
730 }
731 }
732
HasDeviceChange(const AudioNodeList & new_nodes,bool is_input)733 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
734 bool is_input) {
735 size_t num_old_devices = 0;
736 size_t num_new_devices = 0;
737 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
738 it != audio_devices_.end(); ++it) {
739 if (is_input == it->second.is_input)
740 ++num_old_devices;
741 }
742
743 for (AudioNodeList::const_iterator it = new_nodes.begin();
744 it != new_nodes.end(); ++it) {
745 if (is_input == it->is_input) {
746 ++num_new_devices;
747 // Look to see if the new device not in the old device list.
748 AudioDevice device(*it);
749 if (FoundNewOrChangedDevice(device))
750 return true;
751 }
752 }
753 return num_old_devices != num_new_devices;
754 }
755
FoundNewOrChangedDevice(const AudioDevice & device)756 bool CrasAudioHandler::FoundNewOrChangedDevice(const AudioDevice& device) {
757 const AudioDevice* device_found = GetDeviceFromId(device.id);
758 if (!device_found)
759 return true;
760
761 if (!IsSameAudioDevice(device, *device_found)) {
762 LOG(WARNING) << "Different Audio devices with same id:"
763 << " new device: " << device.ToString()
764 << " old device: " << device_found->ToString();
765 return true;
766 } else if (device.active != device_found->active) {
767 return true;
768 }
769
770 return false;
771 }
772
NotifyActiveNodeChanged(bool is_input)773 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input) {
774 if (is_input)
775 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
776 else
777 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
778 }
779
UpdateDevicesAndSwitchActive(const AudioNodeList & nodes)780 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
781 const AudioNodeList& nodes) {
782 size_t old_audio_devices_size = audio_devices_.size();
783 bool output_devices_changed = HasDeviceChange(nodes, false);
784 bool input_devices_changed = HasDeviceChange(nodes, true);
785 audio_devices_.clear();
786 has_alternative_input_ = false;
787 has_alternative_output_ = false;
788
789 while (!input_devices_pq_.empty())
790 input_devices_pq_.pop();
791 while (!output_devices_pq_.empty())
792 output_devices_pq_.pop();
793
794 for (size_t i = 0; i < nodes.size(); ++i) {
795 AudioDevice device(nodes[i]);
796 audio_devices_[device.id] = device;
797
798 if (!has_alternative_input_ &&
799 device.is_input &&
800 device.type != AUDIO_TYPE_INTERNAL_MIC &&
801 device.type != AUDIO_TYPE_KEYBOARD_MIC) {
802 has_alternative_input_ = true;
803 } else if (!has_alternative_output_ &&
804 !device.is_input &&
805 device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
806 has_alternative_output_ = true;
807 }
808
809 if (device.is_input)
810 input_devices_pq_.push(device);
811 else
812 output_devices_pq_.push(device);
813 }
814
815 // If audio nodes change is caused by unplugging some non-active audio
816 // devices, the previously set active audio device will stay active.
817 // Otherwise, switch to a new active audio device according to their priority.
818 if (input_devices_changed &&
819 !NonActiveDeviceUnplugged(old_audio_devices_size,
820 audio_devices_.size(),
821 active_input_node_id_) &&
822 !input_devices_pq_.empty())
823 SwitchToDevice(input_devices_pq_.top(), true);
824 if (output_devices_changed &&
825 !NonActiveDeviceUnplugged(old_audio_devices_size,
826 audio_devices_.size(),
827 active_output_node_id_) &&
828 !output_devices_pq_.empty()) {
829 SwitchToDevice(output_devices_pq_.top(), true);
830 }
831 }
832
HandleGetNodes(const chromeos::AudioNodeList & node_list,bool success)833 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
834 bool success) {
835 if (!success) {
836 LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data";
837 return;
838 }
839
840 UpdateDevicesAndSwitchActive(node_list);
841 FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
842 }
843
HandleGetNodesError(const std::string & error_name,const std::string & error_msg)844 void CrasAudioHandler::HandleGetNodesError(const std::string& error_name,
845 const std::string& error_msg) {
846 LOG_IF(ERROR, log_errors_) << "Failed to call GetNodes: "
847 << error_name << ": " << error_msg;
848 }
849
AddAdditionalActiveNode(uint64 node_id,bool notify)850 void CrasAudioHandler::AddAdditionalActiveNode(uint64 node_id, bool notify) {
851 const AudioDevice* device = GetDeviceFromId(node_id);
852 if (!device) {
853 VLOG(1) << "AddActiveInputNode: Cannot find device id="
854 << "0x" << std::hex << node_id;
855 return;
856 }
857
858 audio_devices_[node_id].active = true;
859 SetupAdditionalActiveAudioNodeState(node_id);
860
861 if (device->is_input) {
862 DCHECK(node_id != active_input_node_id_);
863 chromeos::DBusThreadManager::Get()
864 ->GetCrasAudioClient()
865 ->AddActiveInputNode(node_id);
866 if (notify)
867 NotifyActiveNodeChanged(true);
868 } else {
869 DCHECK(node_id != active_output_node_id_);
870 chromeos::DBusThreadManager::Get()
871 ->GetCrasAudioClient()
872 ->AddActiveOutputNode(node_id);
873 if (notify)
874 NotifyActiveNodeChanged(false);
875 }
876 }
877
RemoveActiveNodeInternal(uint64 node_id,bool notify)878 void CrasAudioHandler::RemoveActiveNodeInternal(uint64 node_id, bool notify) {
879 const AudioDevice* device = GetDeviceFromId(node_id);
880 if (!device) {
881 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
882 << "0x" << std::hex << node_id;
883 return;
884 }
885
886 audio_devices_[node_id].active = false;
887 if (device->is_input) {
888 if (node_id == active_input_node_id_)
889 active_input_node_id_ = 0;
890 chromeos::DBusThreadManager::Get()
891 ->GetCrasAudioClient()
892 ->RemoveActiveInputNode(node_id);
893 if (notify)
894 NotifyActiveNodeChanged(true);
895 } else {
896 if (node_id == active_output_node_id_)
897 active_output_node_id_ = 0;
898 chromeos::DBusThreadManager::Get()
899 ->GetCrasAudioClient()
900 ->RemoveActiveOutputNode(node_id);
901 if (notify)
902 NotifyActiveNodeChanged(false);
903 }
904 }
905
906 } // namespace chromeos
907