Chromium Code Reviews| Index: chromeos/audio/cras_audio_handler.cc | 
| diff --git a/chromeos/audio/cras_audio_handler.cc b/chromeos/audio/cras_audio_handler.cc | 
| index 531f82e02a92f957724d0561df3de90a569a679e..462203484df4b6024f3e58e7941422a830beae21 100644 | 
| --- a/chromeos/audio/cras_audio_handler.cc | 
| +++ b/chromeos/audio/cras_audio_handler.cc | 
| @@ -13,7 +13,7 @@ | 
| #include "base/bind.h" | 
| #include "base/bind_helpers.h" | 
| #include "base/logging.h" | 
| -#include "chromeos/audio/audio_devices_pref_handler.h" | 
| +#include "base/sys_info.h" | 
| #include "chromeos/audio/audio_devices_pref_handler_stub.h" | 
| #include "chromeos/dbus/dbus_thread_manager.h" | 
| @@ -37,8 +37,8 @@ const int kHDMIRediscoverGracePeriodDurationInMs = 2000; | 
| static CrasAudioHandler* g_cras_audio_handler = NULL; | 
| bool IsSameAudioDevice(const AudioDevice& a, const AudioDevice& b) { | 
| - return a.id == b.id && a.is_input == b.is_input && a.type == b.type | 
| - && a.device_name == b.device_name; | 
| + return a.stable_device_id == b.stable_device_id && a.is_input == b.is_input && | 
| + a.type == b.type && a.device_name == b.device_name; | 
| } | 
| bool IsInNodeList(uint64_t node_id, | 
| @@ -46,6 +46,14 @@ bool IsInNodeList(uint64_t node_id, | 
| return std::find(id_list.begin(), id_list.end(), node_id) != id_list.end(); | 
| } | 
| +bool IsDeviceInList(const AudioDevice& device, const AudioNodeList& node_list) { | 
| + for (const AudioNode& node : node_list) { | 
| + if (device.stable_device_id == node.stable_device_id) | 
| + return true; | 
| + } | 
| + return false; | 
| +} | 
| + | 
| } // namespace | 
| CrasAudioHandler::AudioObserver::AudioObserver() { | 
| @@ -233,7 +241,7 @@ void CrasAudioHandler::AddActiveNode(uint64_t node_id, bool notify) { | 
| // If there is no primary active device, set |node_id| to primary active node. | 
| if ((device->is_input && !active_input_node_id_) || | 
| (!device->is_input && !active_output_node_id_)) { | 
| - SwitchToDevice(*device, notify); | 
| + SwitchToDevice(*device, notify, ACTIVATE_BY_USER); | 
| return; | 
| } | 
| @@ -388,37 +396,50 @@ void CrasAudioHandler::SetInputMute(bool mute_on) { | 
| OnInputMuteChanged(input_mute_on_)); | 
| } | 
| -void CrasAudioHandler::SetActiveOutputNode(uint64_t node_id, bool notify) { | 
| - chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> | 
| - SetActiveOutputNode(node_id); | 
| +void CrasAudioHandler::SetActiveDevice(const AudioDevice& active_device, | 
| + bool notify, | 
| + DeviceActivateType activate_by) { | 
| + if (active_device.is_input) { | 
| + chromeos::DBusThreadManager::Get() | 
| + ->GetCrasAudioClient() | 
| + ->SetActiveInputNode(active_device.id); | 
| + } else { | 
| + chromeos::DBusThreadManager::Get() | 
| + ->GetCrasAudioClient() | 
| + ->SetActiveOutputNode(active_device.id); | 
| + } | 
| + | 
| if (notify) | 
| - NotifyActiveNodeChanged(false); | 
| + NotifyActiveNodeChanged(active_device.is_input); | 
| - // Save state for all output nodes. | 
| + // Save active state for the nodes. | 
| for (AudioDeviceMap::iterator it = audio_devices_.begin(); | 
| it != audio_devices_.end(); ++it) { | 
| - if (it->second.is_input) | 
| + const AudioDevice& device = it->second; | 
| + if (device.is_input != active_device.is_input) | 
| continue; | 
| - audio_pref_handler_->SetDeviceState(it->second, it->second.active | 
| - ? AUDIO_STATE_ACTIVE | 
| - : AUDIO_STATE_INACTIVE); | 
| + SaveDeviceState(device, device.active, activate_by); | 
| } | 
| } | 
| -void CrasAudioHandler::SetActiveInputNode(uint64_t node_id, bool notify) { | 
| - chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> | 
| - SetActiveInputNode(node_id); | 
| - if (notify) | 
| - NotifyActiveNodeChanged(true); | 
| - | 
| - // Save state for all input nodes. | 
| - for (AudioDeviceMap::iterator it = audio_devices_.begin(); | 
| - it != audio_devices_.end(); ++it) { | 
| - if (!it->second.is_input) | 
| - continue; | 
| - audio_pref_handler_->SetDeviceState(it->second, it->second.active | 
| - ? AUDIO_STATE_ACTIVE | 
| - : AUDIO_STATE_INACTIVE); | 
| +void CrasAudioHandler::SaveDeviceState(const AudioDevice& device, | 
| + bool active, | 
| + DeviceActivateType activate_by) { | 
| + if (!active) { | 
| + audio_pref_handler_->SetDeviceActive(device, false, false); | 
| + } else { | 
| + switch (activate_by) { | 
| + case ACTIVATE_BY_USER: | 
| + audio_pref_handler_->SetDeviceActive(device, true, true); | 
| + break; | 
| + case ACTIVATE_BY_PRIORITY: | 
| + audio_pref_handler_->SetDeviceActive(device, true, false); | 
| + break; | 
| + default: | 
| + // The device is made active due to its previous active state in prefs, | 
| + // don't change its active state settings in prefs. | 
| + break; | 
| + } | 
| } | 
| } | 
| @@ -532,8 +553,8 @@ void CrasAudioHandler::AudioClientRestarted() { | 
| } | 
| void CrasAudioHandler::NodesChanged() { | 
| - // Refresh audio nodes data. | 
| - GetNodes(); | 
| + if (cras_service_available_) | 
| + GetNodes(); | 
| } | 
| void CrasAudioHandler::ActiveOutputNodeChanged(uint64_t node_id) { | 
| @@ -582,6 +603,16 @@ const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64_t device_id) const { | 
| return &(it->second); | 
| } | 
| +const AudioDevice* CrasAudioHandler::GetDeviceFromStableDeviceId( | 
| + uint64_t stable_device_id) const { | 
| + for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); | 
| + it != audio_devices_.end(); ++it) { | 
| + if (it->second.stable_device_id == stable_device_id) | 
| + return &(it->second); | 
| + } | 
| + return NULL; | 
| +} | 
| + | 
| const AudioDevice* CrasAudioHandler::GetKeyboardMic() const { | 
| for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); | 
| it != audio_devices_.end(); it++) { | 
| @@ -655,6 +686,25 @@ void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64_t node_id) { | 
| void CrasAudioHandler::InitializeAudioState() { | 
| ApplyAudioPolicy(); | 
| + | 
| + // Defer querying cras for GetNodes until cras service becomes available. | 
| + cras_service_available_ = false; | 
| + chromeos::DBusThreadManager::Get() | 
| + ->GetCrasAudioClient() | 
| + ->WaitForServiceToBeAvailable(base::Bind( | 
| + &CrasAudioHandler::InitializeAudioAfterCrasServiceAvailable, | 
| + weak_ptr_factory_.GetWeakPtr())); | 
| +} | 
| + | 
| +void CrasAudioHandler::InitializeAudioAfterCrasServiceAvailable( | 
| + bool service_is_available) { | 
| + if (!service_is_available) { | 
| + LOG(ERROR) << "Cras service is not available"; | 
| + cras_service_available_ = false; | 
| + return; | 
| + } | 
| + | 
| + cras_service_available_ = true; | 
| GetNodes(); | 
| } | 
| @@ -750,62 +800,76 @@ void CrasAudioHandler::GetNodes() { | 
| weak_ptr_factory_.GetWeakPtr())); | 
| } | 
| -bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device, | 
| - uint64_t* current_active_node_id) { | 
| +bool CrasAudioHandler::ChangeActiveDevice( | 
| + const AudioDevice& new_active_device) { | 
| + uint64_t& current_active_node_id = new_active_device.is_input | 
| + ? active_input_node_id_ | 
| + : active_output_node_id_; | 
| // If the device we want to switch to is already the current active device, | 
| // do nothing. | 
| if (new_active_device.active && | 
| - new_active_device.id == *current_active_node_id) { | 
| + new_active_device.id == current_active_node_id) { | 
| return false; | 
| } | 
| + bool found_new_active_device = false; | 
| // Reset all other input or output devices' active status. The active audio | 
| // device from the previous user session can be remembered by cras, but not | 
| // in chrome. see crbug.com/273271. | 
| for (AudioDeviceMap::iterator it = audio_devices_.begin(); | 
| it != audio_devices_.end(); ++it) { | 
| if (it->second.is_input == new_active_device.is_input && | 
| - it->second.id != new_active_device.id) | 
| + it->second.id != new_active_device.id) { | 
| it->second.active = false; | 
| + } else if (it->second.is_input == new_active_device.is_input && | 
| + it->second.id == new_active_device.id) { | 
| + found_new_active_device = true; | 
| + } | 
| + } | 
| + | 
| + if (!found_new_active_device) { | 
| + LOG(ERROR) << "Invalid new active device: " << new_active_device.ToString(); | 
| + return false; | 
| } | 
| // Set the current active input/output device to the new_active_device. | 
| - *current_active_node_id = new_active_device.id; | 
| - audio_devices_[*current_active_node_id].active = true; | 
| + current_active_node_id = new_active_device.id; | 
| + audio_devices_[current_active_node_id].active = true; | 
| return true; | 
| } | 
| -bool CrasAudioHandler::NonActiveDeviceUnplugged(size_t old_devices_size, | 
| - size_t new_devices_size, | 
| - uint64_t current_active_node) { | 
| - return (new_devices_size < old_devices_size && | 
| - GetDeviceFromId(current_active_node)); | 
| -} | 
| +void CrasAudioHandler::SwitchToDevice(const AudioDevice& device, | 
| + bool notify, | 
| + DeviceActivateType activate_by) { | 
| + if (!ChangeActiveDevice(device)) | 
| + return; | 
| -void CrasAudioHandler::SwitchToDevice(const AudioDevice& device, bool notify) { | 
| - if (device.is_input) { | 
| - if (!ChangeActiveDevice(device, &active_input_node_id_)) | 
| - return; | 
| + if (device.is_input) | 
| SetupAudioInputState(); | 
| - SetActiveInputNode(active_input_node_id_, notify); | 
| - } else { | 
| - if (!ChangeActiveDevice(device, &active_output_node_id_)) | 
| - return; | 
| + else | 
| SetupAudioOutputState(); | 
| - SetActiveOutputNode(active_output_node_id_, notify); | 
| - } | 
| + | 
| + SetActiveDevice(device, notify, activate_by); | 
| } | 
| -bool CrasAudioHandler::HasDeviceChange( | 
| - const AudioNodeList& new_nodes, | 
| - bool is_input, | 
| - AudioDevicePriorityQueue* new_discovered) { | 
| - size_t num_old_devices = 0; | 
| - size_t num_new_devices = 0; | 
| +bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes, | 
| + bool is_input, | 
| + AudioDevicePriorityQueue* new_discovered, | 
| + bool* device_removed, | 
| + bool* active_device_removed) { | 
| + *device_removed = false; | 
| for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); | 
| it != audio_devices_.end(); ++it) { | 
| - if (is_input == it->second.is_input) | 
| - ++num_old_devices; | 
| + const AudioDevice& device = it->second; | 
| + if (is_input != device.is_input) | 
| + continue; | 
| + if (!IsDeviceInList(device, new_nodes)) { | 
| + *device_removed = true; | 
| + if ((is_input && device.id == active_input_node_id_) || | 
| + (!is_input && device.id == active_output_node_id_)) { | 
| + *active_device_removed = true; | 
| + } | 
| + } | 
| } | 
| bool new_or_changed_device = false; | 
| @@ -813,31 +877,31 @@ bool CrasAudioHandler::HasDeviceChange( | 
| new_discovered->pop(); | 
| for (AudioNodeList::const_iterator it = new_nodes.begin(); | 
| it != new_nodes.end(); ++it) { | 
| - if (is_input == it->is_input) { | 
| - ++num_new_devices; | 
| - // Look to see if the new device not in the old device list. | 
| - AudioDevice device(*it); | 
| - DeviceStatus status = CheckDeviceStatus(device); | 
| - if (status == NEW_DEVICE) | 
| - new_discovered->push(device); | 
| - if (status == NEW_DEVICE || status == CHANGED_DEVICE) { | 
| - new_or_changed_device = true; | 
| - } | 
| + if (is_input != it->is_input) | 
| + continue; | 
| + // Check if the new device is not in the old device list. | 
| + AudioDevice device(*it); | 
| + DeviceStatus status = CheckDeviceStatus(device); | 
| + if (status == NEW_DEVICE) | 
| + new_discovered->push(device); | 
| + if (status == NEW_DEVICE || status == CHANGED_DEVICE) { | 
| + new_or_changed_device = true; | 
| } | 
| } | 
| - return new_or_changed_device || (num_old_devices != num_new_devices); | 
| + return new_or_changed_device || *device_removed; | 
| } | 
| CrasAudioHandler::DeviceStatus CrasAudioHandler::CheckDeviceStatus( | 
| const AudioDevice& device) { | 
| - const AudioDevice* device_found = GetDeviceFromId(device.id); | 
| + const AudioDevice* device_found = | 
| + GetDeviceFromStableDeviceId(device.stable_device_id); | 
| if (!device_found) | 
| return NEW_DEVICE; | 
| if (!IsSameAudioDevice(device, *device_found)) { | 
| - LOG(WARNING) << "Different Audio devices with same id:" | 
| - << " new device: " << device.ToString() | 
| - << " old device: " << device_found->ToString(); | 
| + LOG(ERROR) << "Different Audio devices with same stable device id:" | 
| + << " new device: " << device.ToString() | 
| + << " old device: " << device_found->ToString(); | 
| return CHANGED_DEVICE; | 
| } else if (device.active != device_found->active) { | 
| return CHANGED_DEVICE; | 
| @@ -853,6 +917,150 @@ void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input) { | 
| FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged()); | 
| } | 
| +bool CrasAudioHandler::GetActiveDeviceFromUserPref(bool is_input, | 
| + AudioDevice* active_device) { | 
| + bool found_active_device = false; | 
| + bool last_active_device_activate_by_user = false; | 
| + for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); | 
| + it != audio_devices_.end(); ++it) { | 
| + AudioDevice device = it->second; | 
| + if (device.is_input != is_input) | 
| + continue; | 
| + | 
| + bool active = false; | 
| + bool activate_by_user = false; | 
| + if (!audio_pref_handler_->GetDeviceActive(device, &active, | 
| + &activate_by_user) || | 
| + !active) { | 
| + continue; | 
| + } | 
| + | 
| + if (!found_active_device) { | 
| + found_active_device = true; | 
| + *active_device = device; | 
| + last_active_device_activate_by_user = activate_by_user; | 
| + continue; | 
| + } | 
| + | 
| + // Choose the best one among multiple active devices from prefs. | 
| + if (activate_by_user) { | 
| + if (!last_active_device_activate_by_user) { | 
| + // Device activated by user has higher priority than the one | 
| + // is not activated by user. | 
| + *active_device = device; | 
| + last_active_device_activate_by_user = true; | 
| + } else { | 
| + // If there are more than one active devices activated by user in the | 
| + // prefs, most likely, after the device was shut down, and before it | 
| + // is rebooted, user has plugged in some previously unplugged audio | 
| + // devices. For such case, it does not make sense to honor the active | 
| + // states in the prefs. | 
| + VLOG(1) << "Found more than one user activated devices in the prefs."; | 
| + return false; | 
| + } | 
| + } else if (!last_active_device_activate_by_user) { | 
| + // If there are more than one active devices activated by priority in the | 
| + // prefs, most likely, cras is still enumerating the audio devices | 
| + // progressively. For such case, it does not make sense to honor the | 
| + // active states in the prefs. | 
| + VLOG(1) << "Found more than one active devices by priority in the prefs."; | 
| + return false; | 
| + } | 
| + } | 
| + | 
| + if (found_active_device && !active_device->is_for_simple_usage()) { | 
| + // This is an odd case which is rare but possible to happen during cras | 
| + // initialization depeneding the audio device enumation process. The only | 
| + // audio node coming from cras is an internal audio device not visible | 
| + // to user, such as AUDIO_TYPE_POST_MIX_LOOPBACK. | 
| + return false; | 
| + } | 
| + | 
| + return found_active_device; | 
| +} | 
| + | 
| +void CrasAudioHandler::HandleNonHotplugNodesChange( | 
| + bool is_input, | 
| + const AudioDevicePriorityQueue& hotplug_nodes, | 
| + bool has_device_change, | 
| + bool has_device_removed, | 
| + bool active_device_removed) { | 
| + bool has_current_active_node = | 
| + is_input ? active_input_node_id_ : active_output_node_id_; | 
| + | 
| + // No device change, extra NodesChanged signal received. | 
| + if (!has_device_change && has_current_active_node) | 
| + return; | 
| + | 
| + if (hotplug_nodes.empty()) { | 
| + // Unplugged a non-active device. | 
| + if (has_device_removed) { | 
| + if (!active_device_removed && has_current_active_node) { | 
| + // Removed a non-active device, keep the current active device. | 
| + return; | 
| + } | 
| + | 
| + if (active_device_removed) { | 
| + // Unplugged the current active device. | 
| + SwitchToTopPriorityDevice(is_input); | 
| + return; | 
| + } | 
| + } | 
| + | 
| + // Some unexpected error happens on cras side. See crbug.com/586026. | 
| + // Either cras sent stale nodes to chrome again or cras triggered some | 
| + // error. Restore the previously selected active. | 
| + VLOG(1) << "Odd case from cras, the active node is lost unexpectedly. "; | 
| + SwitchToPreviousActiveDeviceIfAvailable(is_input); | 
| + } else { | 
| + // Looks like a new chrome session starts. | 
| + SwitchToPreviousActiveDeviceIfAvailable(is_input); | 
| + } | 
| +} | 
| + | 
| +void CrasAudioHandler::HandleHotPlugDevice( | 
| + const AudioDevice& hotplug_device, | 
| + const AudioDevicePriorityQueue& device_priority_queue) { | 
| + bool last_state_active = false; | 
| + bool last_activate_by_user = false; | 
| + if (!audio_pref_handler_->GetDeviceActive(hotplug_device, &last_state_active, | 
| + &last_activate_by_user)) { | 
| + // |hotplug_device} is plugged in for the first time, activate it if it | 
| 
 
hychao
2016/03/02 15:34:05
|hotplug_device|
 
jennyz
2016/03/02 19:33:37
Done.
 
 | 
| + // is of the highest priority. | 
| + if (device_priority_queue.top().id == hotplug_device.id) { | 
| + VLOG(1) << "Hotplug a device for the first time: " | 
| + << hotplug_device.ToString(); | 
| + SwitchToDevice(hotplug_device, true, ACTIVATE_BY_PRIORITY); | 
| + } | 
| + } else if (last_state_active) { | 
| + VLOG(1) << "Hotplug a device, restore to active: " | 
| + << hotplug_device.ToString(); | 
| + SwitchToDevice(hotplug_device, true, ACTIVATE_BY_RESTORE_PREVIOUS_STATE); | 
| + } else { | 
| + // Do not active the device if its previous state is inactive. | 
| + VLOG(1) << "Hotplug device remains inactive as its previous state:" | 
| + << hotplug_device.ToString(); | 
| + } | 
| +} | 
| + | 
| +void CrasAudioHandler::SwitchToTopPriorityDevice(bool is_input) { | 
| + AudioDevice top_device = | 
| + is_input ? input_devices_pq_.top() : output_devices_pq_.top(); | 
| + SwitchToDevice(top_device, true, ACTIVATE_BY_PRIORITY); | 
| +} | 
| + | 
| +void CrasAudioHandler::SwitchToPreviousActiveDeviceIfAvailable(bool is_input) { | 
| + AudioDevice previous_active_device; | 
| + if (GetActiveDeviceFromUserPref(is_input, &previous_active_device)) { | 
| + // Switch to previous active device stored in user prefs. | 
| + SwitchToDevice(previous_active_device, true, | 
| + ACTIVATE_BY_RESTORE_PREVIOUS_STATE); | 
| + } else { | 
| + // No previous active device, switch to the top priority device. | 
| + SwitchToTopPriorityDevice(is_input); | 
| + } | 
| +} | 
| + | 
| void CrasAudioHandler::UpdateDevicesAndSwitchActive( | 
| const AudioNodeList& nodes) { | 
| size_t old_output_device_size = 0; | 
| @@ -867,10 +1075,16 @@ void CrasAudioHandler::UpdateDevicesAndSwitchActive( | 
| AudioDevicePriorityQueue hotplug_output_nodes; | 
| AudioDevicePriorityQueue hotplug_input_nodes; | 
| + bool has_output_removed = false; | 
| + bool has_input_removed = false; | 
| + bool active_output_removed = false; | 
| + bool active_input_removed = false; | 
| bool output_devices_changed = | 
| - HasDeviceChange(nodes, false, &hotplug_output_nodes); | 
| + HasDeviceChange(nodes, false, &hotplug_output_nodes, &has_output_removed, | 
| + &active_output_removed); | 
| bool input_devices_changed = | 
| - HasDeviceChange(nodes, true, &hotplug_input_nodes); | 
| + HasDeviceChange(nodes, true, &hotplug_input_nodes, &has_input_removed, | 
| + &active_input_removed); | 
| audio_devices_.clear(); | 
| has_alternative_input_ = false; | 
| has_alternative_output_ = false; | 
| @@ -885,7 +1099,6 @@ void CrasAudioHandler::UpdateDevicesAndSwitchActive( | 
| for (size_t i = 0; i < nodes.size(); ++i) { | 
| AudioDevice device(nodes[i]); | 
| audio_devices_[device.id] = device; | 
| - | 
| if (!has_alternative_input_ && | 
| device.is_input && | 
| device.type != AUDIO_TYPE_INTERNAL_MIC && | 
| @@ -906,88 +1119,48 @@ void CrasAudioHandler::UpdateDevicesAndSwitchActive( | 
| } | 
| } | 
| + // Handle output device changes. | 
| + HandleAudioDeviceChange(false, output_devices_pq_, hotplug_output_nodes, | 
| + output_devices_changed, has_output_removed, | 
| + active_output_removed); | 
| + | 
| + // Handle input device changes. | 
| + HandleAudioDeviceChange(true, input_devices_pq_, hotplug_input_nodes, | 
| + input_devices_changed, has_input_removed, | 
| + active_input_removed); | 
| +} | 
| + | 
| +void CrasAudioHandler::HandleAudioDeviceChange( | 
| + bool is_input, | 
| + const AudioDevicePriorityQueue& devices_pq, | 
| + const AudioDevicePriorityQueue& hotplug_nodes, | 
| + bool has_device_change, | 
| + bool has_device_removed, | 
| + bool active_device_removed) { | 
| + uint64_t& active_node_id = | 
| + is_input ? active_input_node_id_ : active_output_node_id_; | 
| + | 
| + // No audio devices found. | 
| + if (devices_pq.empty()) { | 
| + VLOG(1) << "No " << (is_input ? "input" : "output") << " devices found"; | 
| + active_node_id = 0; | 
| + NotifyActiveNodeChanged(is_input); | 
| + return; | 
| + } | 
| + | 
| // If the previous active device is removed from the new node list, | 
| - // or changed to inactive by cras, reset active_output_node_id_. | 
| + // or changed to inactive by cras, reset active_node_id. | 
| // See crbug.com/478968. | 
| - const AudioDevice* active_output = GetDeviceFromId(active_output_node_id_); | 
| - if (!active_output || !active_output->active) | 
| - active_output_node_id_ = 0; | 
| - const AudioDevice* active_input = GetDeviceFromId(active_input_node_id_); | 
| - if (!active_input || !active_input->active) | 
| - active_input_node_id_ = 0; | 
| - | 
| - // If audio nodes change is caused by unplugging some non-active audio | 
| - // devices, we wont't change the current active audio devices. | 
| - if (input_devices_changed && | 
| - !NonActiveDeviceUnplugged(old_input_device_size, | 
| - new_input_device_size, | 
| - active_input_node_id_)) { | 
| - // Some devices like chromeboxes don't have the internal audio input. In | 
| - // that case the active input node id should be reset. | 
| - if (input_devices_pq_.empty()) { | 
| - active_input_node_id_ = 0; | 
| - NotifyActiveNodeChanged(true); | 
| - } else { | 
| - // If there is no current active node, or, no new hot plugged node, select | 
| - // the active node by their priorities. | 
| - if (!active_input_node_id_ || hotplug_input_nodes.empty()) { | 
| - SwitchToDevice(input_devices_pq_.top(), true); | 
| - } else { | 
| - // If user has hot plugged any input nodes, look at the one with highest | 
| - // priority (normally, there is only one hot plugged input node), and | 
| - // consider switch to it depend on its last state stored in preference. | 
| - AudioDeviceState last_state = | 
| - audio_pref_handler_->GetDeviceState(hotplug_input_nodes.top()); | 
| - switch (last_state) { | 
| - case AUDIO_STATE_ACTIVE: | 
| - // This node was plugged in before and was selected as the active | 
| - // one | 
| - // before it was unplugged last time, switch to this device. | 
| - SwitchToDevice(hotplug_input_nodes.top(), true); | 
| - break; | 
| - case AUDIO_STATE_NOT_AVAILABLE: | 
| - // This is a new node, not plugged in before, with the highest | 
| - // priority. Switch to this device. | 
| - if (input_devices_pq_.top().id == hotplug_input_nodes.top().id) | 
| - SwitchToDevice(hotplug_input_nodes.top(), true); | 
| - break; | 
| - case AUDIO_STATE_INACTIVE: | 
| - // This node was NOT selected as the active one last time before it | 
| - // got unplugged, so don't switch to it. | 
| - default: | 
| - break; | 
| - } | 
| - } | 
| - } | 
| - } | 
| - if (output_devices_changed && | 
| - !NonActiveDeviceUnplugged(old_output_device_size, | 
| - new_output_device_size, | 
| - active_output_node_id_)) { | 
| - // ditto input node logic. | 
| - if (output_devices_pq_.empty()) { | 
| - active_output_node_id_ = 0; | 
| - NotifyActiveNodeChanged(false); | 
| - } else { | 
| - if (!active_output_node_id_ || hotplug_output_nodes.empty()) { | 
| - SwitchToDevice(output_devices_pq_.top(), true); | 
| - } else { | 
| - AudioDeviceState last_state = | 
| - audio_pref_handler_->GetDeviceState(hotplug_output_nodes.top()); | 
| - switch (last_state) { | 
| - case AUDIO_STATE_ACTIVE: | 
| - SwitchToDevice(hotplug_output_nodes.top(), true); | 
| - break; | 
| - case AUDIO_STATE_NOT_AVAILABLE: | 
| - if (output_devices_pq_.top().id == hotplug_output_nodes.top().id) | 
| - SwitchToDevice(hotplug_output_nodes.top(), true); | 
| - break; | 
| - case AUDIO_STATE_INACTIVE: | 
| - default: | 
| - break; | 
| - } | 
| - } | 
| - } | 
| + const AudioDevice* active_device = GetDeviceFromId(active_node_id); | 
| + if (!active_device || !active_device->active) | 
| + active_node_id = 0; | 
| + | 
| + if (!active_node_id || hotplug_nodes.empty() || hotplug_nodes.size() > 1) { | 
| + HandleNonHotplugNodesChange(is_input, hotplug_nodes, has_device_change, | 
| + has_device_removed, active_device_removed); | 
| + } else { | 
| + // Typical user hotplug case. | 
| + HandleHotPlugDevice(hotplug_nodes.top(), devices_pq); | 
| } | 
| } |