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..64114c9071ebffacd4b16f45f9f469bf2ac72d7d 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 |
+ // 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); |
} |
} |