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
17using std::max;
18using std::min;
19
20namespace chromeos {
21
22namespace {
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.
26const int kDefaultUnmuteVolumePercent = 4;
27
28// Volume value which should be considered as muted in range [0, 100].
29const int kMuteThresholdPercent = 1;
30
31static CrasAudioHandler* g_cras_audio_handler = NULL;
32
33}  // namespace
34
35CrasAudioHandler::AudioObserver::AudioObserver() {
36}
37
38CrasAudioHandler::AudioObserver::~AudioObserver() {
39}
40
41void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
42}
43
44void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
45}
46
47void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
48}
49
50void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
51}
52
53void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
54}
55
56void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
57}
58
59void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
60}
61
62// static
63void CrasAudioHandler::Initialize(
64    scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) {
65  CHECK(!g_cras_audio_handler);
66  g_cras_audio_handler = new CrasAudioHandler(audio_pref_handler);
67}
68
69// static
70void CrasAudioHandler::InitializeForTesting() {
71  CHECK(!g_cras_audio_handler);
72  CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
73}
74
75// static
76void CrasAudioHandler::Shutdown() {
77  CHECK(g_cras_audio_handler);
78  delete g_cras_audio_handler;
79  g_cras_audio_handler = NULL;
80}
81
82// static
83bool CrasAudioHandler::IsInitialized() {
84  return g_cras_audio_handler != NULL;
85}
86
87// static
88CrasAudioHandler* CrasAudioHandler::Get() {
89  CHECK(g_cras_audio_handler)
90      << "CrasAudioHandler::Get() called before Initialize().";
91  return g_cras_audio_handler;
92}
93
94void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
95  observers_.AddObserver(observer);
96}
97
98void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
99  observers_.RemoveObserver(observer);
100}
101
102bool CrasAudioHandler::IsOutputMuted() {
103  return output_mute_on_;
104}
105
106bool CrasAudioHandler::IsOutputMutedForDevice(uint64 device_id) {
107  const AudioDevice* device = GetDeviceFromId(device_id);
108  if (!device)
109    return false;
110  return audio_pref_handler_->GetMuteValue(*device);
111}
112
113bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLvel() {
114  return output_volume_ <= kMuteThresholdPercent;
115}
116
117bool CrasAudioHandler::IsInputMuted() {
118  return input_mute_on_;
119}
120
121bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id) {
122  const AudioDevice* device = GetDeviceFromId(device_id);
123  if (!device)
124    return false;
125  return audio_pref_handler_->GetMuteValue(*device);
126}
127
128int CrasAudioHandler::GetOutputVolumePercent() {
129  return output_volume_;
130}
131
132int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64 device_id) {
133  if (device_id == active_output_node_id_) {
134    return output_volume_;
135  } else {
136    const AudioDevice* device = GetDeviceFromId(device_id);
137    if (!device)
138      return kDefaultVolumeGainPercent;
139    return static_cast<int>(audio_pref_handler_->GetVolumeGainValue(*device));
140  }
141}
142
143int CrasAudioHandler::GetInputGainPercent() {
144  return input_gain_;
145}
146
147int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id) {
148  if (device_id == active_input_node_id_) {
149    return input_gain_;
150  } else {
151    const AudioDevice* device = GetDeviceFromId(device_id);
152    if (!device)
153      return kDefaultVolumeGainPercent;
154    return static_cast<int>(audio_pref_handler_->GetVolumeGainValue(*device));
155  }
156}
157
158uint64 CrasAudioHandler::GetActiveOutputNode() const {
159  return active_output_node_id_;
160}
161
162uint64 CrasAudioHandler::GetActiveInputNode() const {
163  return active_input_node_id_;
164}
165
166void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
167  device_list->clear();
168  for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
169       it != audio_devices_.end(); ++it)
170    device_list->push_back(it->second);
171}
172
173bool CrasAudioHandler::GetActiveOutputDevice(AudioDevice* device) const {
174  const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_);
175  if (!active_device || !device)
176    return false;
177  *device = *active_device;
178  return true;
179}
180
181bool CrasAudioHandler::has_alternative_input() const {
182  return has_alternative_input_;
183}
184
185bool CrasAudioHandler::has_alternative_output() const {
186  return has_alternative_output_;
187}
188
189void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
190  volume_percent = min(max(volume_percent, 0), 100);
191  if (volume_percent <= kMuteThresholdPercent)
192    volume_percent = 0;
193  output_volume_ = volume_percent;
194
195  if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_))
196    audio_pref_handler_->SetVolumeGainValue(*device, output_volume_);
197
198  SetOutputNodeVolume(active_output_node_id_, output_volume_);
199  FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged());
200}
201
202// TODO: Rename the 'Percent' to something more meaningful.
203void CrasAudioHandler::SetInputGainPercent(int gain_percent) {
204  // NOTE: We do not sanitize input gain values since the range is completely
205  // dependent on the device.
206  input_gain_ = gain_percent;
207
208  if (const AudioDevice* device = GetDeviceFromId(active_input_node_id_))
209    audio_pref_handler_->SetVolumeGainValue(*device, input_gain_);
210
211  SetInputNodeGain(active_input_node_id_, input_gain_);
212  FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged());
213}
214
215void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
216  SetOutputVolumePercent(output_volume_ + adjust_by_percent);
217}
218
219void CrasAudioHandler::SetOutputMute(bool mute_on) {
220  if (!SetOutputMuteInternal(mute_on))
221    return;
222
223  if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_))
224    audio_pref_handler_->SetMuteValue(*device, output_mute_on_);
225
226  FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged());
227}
228
229void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
230  if (output_volume_ <= kMuteThresholdPercent) {
231    // Avoid the situation when sound has been unmuted, but the volume
232    // is set to a very low value, so user still can't hear any sound.
233    SetOutputVolumePercent(kDefaultUnmuteVolumePercent);
234  }
235}
236
237void CrasAudioHandler::SetInputMute(bool mute_on) {
238  if (!SetInputMuteInternal(mute_on))
239    return;
240
241  AudioDevice device;
242  if (const AudioDevice* device = GetDeviceFromId(active_input_node_id_))
243    audio_pref_handler_->SetMuteValue(*device, input_mute_on_);
244
245
246  FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
247}
248
249void CrasAudioHandler::SetActiveOutputNode(uint64 node_id) {
250  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
251      SetActiveOutputNode(node_id);
252  FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
253}
254
255void CrasAudioHandler::SetActiveInputNode(uint64 node_id) {
256  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
257      SetActiveInputNode(node_id);
258  FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
259}
260
261void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id,
262                                                     int value) {
263  if (device_id == active_output_node_id_) {
264    SetOutputVolumePercent(value);
265    return;
266  } else if (device_id == active_input_node_id_) {
267    SetInputGainPercent(value);
268    return;
269  }
270
271  if (const AudioDevice* device = GetDeviceFromId(device_id)) {
272    if (!device->is_input) {
273      value = min(max(value, 0), 100);
274      if (value <= kMuteThresholdPercent)
275        value = 0;
276    }
277    audio_pref_handler_->SetVolumeGainValue(*device, value);
278  }
279}
280
281void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) {
282  if (device_id == active_output_node_id_) {
283    SetOutputMute(mute_on);
284    return;
285  } else if (device_id == active_input_node_id_) {
286    SetInputMute(mute_on);
287    return;
288  }
289
290  AudioDevice device;
291  if (const AudioDevice* device = GetDeviceFromId(device_id))
292    audio_pref_handler_->SetMuteValue(*device, mute_on);
293}
294
295CrasAudioHandler::CrasAudioHandler(
296    scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
297    : audio_pref_handler_(audio_pref_handler),
298      weak_ptr_factory_(this),
299      output_mute_on_(false),
300      input_mute_on_(false),
301      output_volume_(0),
302      input_gain_(0),
303      active_output_node_id_(0),
304      active_input_node_id_(0),
305      has_alternative_input_(false),
306      has_alternative_output_(false),
307      output_mute_locked_(false),
308      input_mute_locked_(false) {
309  if (!audio_pref_handler.get())
310    return;
311  // If the DBusThreadManager or the CrasAudioClient aren't available, there
312  // isn't much we can do. This should only happen when running tests.
313  if (!chromeos::DBusThreadManager::IsInitialized() ||
314      !chromeos::DBusThreadManager::Get() ||
315      !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
316    return;
317  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
318  audio_pref_handler_->AddAudioPrefObserver(this);
319  InitializeAudioState();
320}
321
322CrasAudioHandler::~CrasAudioHandler() {
323  if (!chromeos::DBusThreadManager::IsInitialized() ||
324      !chromeos::DBusThreadManager::Get() ||
325      !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
326    return;
327  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
328      RemoveObserver(this);
329  if (audio_pref_handler_.get())
330    audio_pref_handler_->RemoveAudioPrefObserver(this);
331  audio_pref_handler_ = NULL;
332}
333
334void CrasAudioHandler::AudioClientRestarted() {
335  InitializeAudioState();
336}
337
338void CrasAudioHandler::NodesChanged() {
339  // Refresh audio nodes data.
340  GetNodes();
341}
342
343void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) {
344  if (active_output_node_id_ == node_id)
345    return;
346
347  // Active audio output device should always be changed by chrome.
348  LOG(WARNING) << "Active output node changed unexpectedly by system node_id="
349      << "0x" << std::hex << node_id;
350}
351
352void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) {
353  if (active_input_node_id_ == node_id)
354    return;
355
356  // Active audio input device should always be changed by chrome.
357  LOG(WARNING) << "Active input node changed unexpectedly by system node_id="
358      << "0x" << std::hex << node_id;
359}
360
361void CrasAudioHandler::OnAudioPolicyPrefChanged() {
362  ApplyAudioPolicy();
363}
364
365const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64 device_id) const {
366  AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
367  if (it == audio_devices_.end())
368    return NULL;
369
370  return &(it->second);
371}
372
373void CrasAudioHandler::SetupAudioInputState() {
374  // Set the initial audio state to the ones read from audio prefs.
375  const AudioDevice* device = GetDeviceFromId(active_input_node_id_);
376  if (!device) {
377    LOG(ERROR) << "Can't set up audio state for unknow input device id ="
378        << "0x" << std::hex << active_input_node_id_;
379    return;
380  }
381  input_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
382  input_gain_ = audio_pref_handler_->GetVolumeGainValue(*device);
383  SetInputMuteInternal(input_mute_on_);
384  // TODO(rkc,jennyz): Set input gain once we decide on how to store
385  // the gain values since the range and step are both device specific.
386}
387
388void CrasAudioHandler::SetupAudioOutputState() {
389  const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
390  if (!device) {
391    LOG(ERROR) << "Can't set up audio state for unknow output device id ="
392               << "0x" << std::hex << active_output_node_id_;
393    return;
394  }
395  output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
396  output_volume_ = audio_pref_handler_->GetVolumeGainValue(*device);
397
398  SetOutputMuteInternal(output_mute_on_);
399  SetOutputNodeVolume(active_output_node_id_, output_volume_);
400}
401
402void CrasAudioHandler::InitializeAudioState() {
403  ApplyAudioPolicy();
404  GetNodes();
405}
406
407void CrasAudioHandler::ApplyAudioPolicy() {
408  output_mute_locked_ = false;
409  if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
410    // Mute the device, but do not update the preference.
411    SetOutputMuteInternal(true);
412    output_mute_locked_ = true;
413  } else {
414    // Restore the mute state.
415    const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
416    if (device)
417      SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
418  }
419
420  input_mute_locked_ = false;
421  if (audio_pref_handler_->GetAudioCaptureAllowedValue()) {
422    SetInputMute(false);
423  } else {
424    SetInputMute(true);
425    input_mute_locked_ = true;
426  }
427}
428
429void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
430  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
431      SetOutputNodeVolume(node_id, volume);
432}
433
434bool  CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
435  if (output_mute_locked_)
436    return false;
437
438  output_mute_on_ = mute_on;
439  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
440      SetOutputUserMute(mute_on);
441  return true;
442}
443
444void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
445  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
446      SetInputNodeGain(node_id, gain);
447}
448
449bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
450  if (input_mute_locked_)
451    return false;
452
453  input_mute_on_ = mute_on;
454  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
455      SetInputMute(mute_on);
456  return true;
457}
458
459void CrasAudioHandler::GetNodes() {
460  chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
461      base::Bind(&CrasAudioHandler::HandleGetNodes,
462                 weak_ptr_factory_.GetWeakPtr()));
463}
464
465bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device,
466                                          uint64* current_active_node_id) {
467  // If the device we want to switch to is already the current active device,
468  // do nothing.
469  if (new_active_device.active &&
470      new_active_device.id == *current_active_node_id) {
471    return false;
472  }
473
474  // Reset all other input or output devices' active status. The active audio
475  // device from the previous user session can be remembered by cras, but not
476  // in chrome. see crbug.com/273271.
477  for (AudioDeviceMap::iterator it = audio_devices_.begin();
478       it != audio_devices_.end(); ++it) {
479    if (it->second.is_input == new_active_device.is_input &&
480        it->second.id != new_active_device.id)
481      it->second.active = false;
482  }
483
484  // Set the current active input/output device to the new_active_device.
485  *current_active_node_id = new_active_device.id;
486  audio_devices_[*current_active_node_id].active = true;
487  return true;
488}
489
490bool CrasAudioHandler::NonActiveDeviceUnplugged(
491    size_t old_devices_size,
492    size_t new_devices_size,
493    uint64 current_active_node) {
494  // There could be cases that more than one NodesChanged signals are
495  // triggered by cras for unplugging or plugging one audio devices, both coming
496  // with the same node data. After handling the first NodesChanged signal, the
497  // audio_devices_ can be overwritten by staled node data from handling 2nd
498  // NodesChanged signal. Therefore, we need to check if the device with
499  // current_active_node is consistently active or not.
500  // crbug.com/274641.
501  return (new_devices_size <= old_devices_size &&
502          GetDeviceFromId(current_active_node) &&
503          audio_devices_[current_active_node].active);
504}
505
506void CrasAudioHandler::SwitchToDevice(const AudioDevice& device) {
507  if (device.is_input) {
508    if (!ChangeActiveDevice(device, &active_input_node_id_))
509      return;
510    SetupAudioInputState();
511    SetActiveInputNode(active_input_node_id_);
512  } else {
513    if (!ChangeActiveDevice(device, &active_output_node_id_))
514      return;
515    SetupAudioOutputState();
516    SetActiveOutputNode(active_output_node_id_);
517  }
518}
519
520bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
521                                       bool is_input) {
522  size_t num_old_devices = 0;
523  size_t num_new_devices = 0;
524  for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
525       it != audio_devices_.end(); ++it) {
526    if (is_input == it->second.is_input)
527      ++num_old_devices;
528  }
529
530  for (AudioNodeList::const_iterator it = new_nodes.begin();
531       it != new_nodes.end(); ++it) {
532    if (is_input == it->is_input)
533      ++num_new_devices;
534  }
535  return num_old_devices != num_new_devices;
536}
537
538void CrasAudioHandler::UpdateDevicesAndSwitchActive(
539    const AudioNodeList& nodes) {
540  size_t old_audio_devices_size = audio_devices_.size();
541  bool output_devices_changed = HasDeviceChange(nodes, false);
542  bool input_devices_changed = HasDeviceChange(nodes, true);
543  audio_devices_.clear();
544  has_alternative_input_ = false;
545  has_alternative_output_ = false;
546
547  while (!input_devices_pq_.empty())
548    input_devices_pq_.pop();
549  while (!output_devices_pq_.empty())
550    output_devices_pq_.pop();
551
552  for (size_t i = 0; i < nodes.size(); ++i) {
553    AudioDevice device(nodes[i]);
554    audio_devices_[device.id] = device;
555
556    if (!has_alternative_input_ &&
557        device.is_input &&
558        device.type != AUDIO_TYPE_INTERNAL_MIC) {
559      has_alternative_input_ = true;
560    } else if (!has_alternative_output_ &&
561               !device.is_input &&
562               device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
563      has_alternative_output_ = true;
564    }
565
566    if (device.is_input)
567      input_devices_pq_.push(device);
568    else
569      output_devices_pq_.push(device);
570  }
571
572  // If audio nodes change is caused by unplugging some non-active audio
573  // devices, the previously set active audio device will stay active.
574  // Otherwise, switch to a new active audio device according to their priority.
575  if (input_devices_changed &&
576      !NonActiveDeviceUnplugged(old_audio_devices_size,
577                                audio_devices_.size(),
578                                active_input_node_id_) &&
579      !input_devices_pq_.empty())
580    SwitchToDevice(input_devices_pq_.top());
581  if (output_devices_changed &&
582      !NonActiveDeviceUnplugged(old_audio_devices_size,
583                                audio_devices_.size(),
584                                active_output_node_id_) &&
585      !output_devices_pq_.empty()) {
586    SwitchToDevice(output_devices_pq_.top());
587  }
588}
589
590void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
591                                      bool success) {
592  if (!success) {
593    LOG(ERROR) << "Failed to retrieve audio nodes data";
594    return;
595  }
596
597  UpdateDevicesAndSwitchActive(node_list);
598  FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
599}
600
601}  // namespace chromeos
602