• 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 
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