Home | History | Annotate | Download | only in audio
      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 
     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 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 
     44 CrasAudioHandler::AudioObserver::AudioObserver() {
     45 }
     46 
     47 CrasAudioHandler::AudioObserver::~AudioObserver() {
     48 }
     49 
     50 void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
     51 }
     52 
     53 void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
     54 }
     55 
     56 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
     57 }
     58 
     59 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
     60 }
     61 
     62 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
     63 }
     64 
     65 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
     66 }
     67 
     68 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
     69 }
     70 
     71 // static
     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
     79 void CrasAudioHandler::InitializeForTesting() {
     80   CHECK(!g_cras_audio_handler);
     81   CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
     82 }
     83 
     84 // static
     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
     92 bool CrasAudioHandler::IsInitialized() {
     93   return g_cras_audio_handler != NULL;
     94 }
     95 
     96 // static
     97 CrasAudioHandler* CrasAudioHandler::Get() {
     98   CHECK(g_cras_audio_handler)
     99       << "CrasAudioHandler::Get() called before Initialize().";
    100   return g_cras_audio_handler;
    101 }
    102 
    103 void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
    104   observers_.AddObserver(observer);
    105 }
    106 
    107 void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
    108   observers_.RemoveObserver(observer);
    109 }
    110 
    111 bool CrasAudioHandler::HasKeyboardMic() {
    112   return GetKeyboardMic() != NULL;
    113 }
    114 
    115 bool CrasAudioHandler::IsOutputMuted() {
    116   return output_mute_on_;
    117 }
    118 
    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 
    127 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
    128   return output_volume_ <= kMuteThresholdPercent;
    129 }
    130 
    131 bool CrasAudioHandler::IsInputMuted() {
    132   return input_mute_on_;
    133 }
    134 
    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 
    147 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
    148   return kMuteThresholdPercent;
    149 }
    150 
    151 int CrasAudioHandler::GetOutputVolumePercent() {
    152   return output_volume_;
    153 }
    154 
    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 
    164 int CrasAudioHandler::GetInputGainPercent() {
    165   return input_gain_;
    166 }
    167 
    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 
    177 uint64 CrasAudioHandler::GetPrimaryActiveOutputNode() const {
    178   return active_output_node_id_;
    179 }
    180 
    181 uint64 CrasAudioHandler::GetPrimaryActiveInputNode() const {
    182   return active_input_node_id_;
    183 }
    184 
    185 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 
    192 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 
    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 
    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 
    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 
    295 bool CrasAudioHandler::has_alternative_input() const {
    296   return has_alternative_input_;
    297 }
    298 
    299 bool CrasAudioHandler::has_alternative_output() const {
    300   return has_alternative_output_;
    301 }
    302 
    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.
    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 
    326 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
    327   SetOutputVolumePercent(output_volume_ + adjust_by_percent);
    328 }
    329 
    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 
    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 
    355 void CrasAudioHandler::SetInputMute(bool mute_on) {
    356   if (!SetInputMuteInternal(mute_on))
    357     return;
    358 
    359   FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
    360 }
    361 
    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 
    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 
    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 
    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 
    405 void CrasAudioHandler::LogErrors() {
    406   log_errors_ = true;
    407 }
    408 
    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 
    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 
    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 
    462 void CrasAudioHandler::NodesChanged() {
    463   // Refresh audio nodes data.
    464   GetNodes();
    465 }
    466 
    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 
    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 
    495 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
    496   ApplyAudioPolicy();
    497 }
    498 
    499 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
    500   // Enable logging after cras server is started, which will be after
    501   // EmitLoginPromptVisible.
    502   LogErrors();
    503 }
    504 
    505 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 
    513 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 
    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 
    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.
    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 
    578 void CrasAudioHandler::InitializeAudioState() {
    579   ApplyAudioPolicy();
    580   GetNodes();
    581 }
    582 
    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 
    609 void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
    610   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    611       SetOutputNodeVolume(node_id, volume);
    612 }
    613 
    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 
    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 
    644 void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
    645   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    646       SetInputNodeGain(node_id, gain);
    647 }
    648 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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