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