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