Index: chromeos/audio/cras_audio_handler_unittest.cc |
diff --git a/chromeos/audio/cras_audio_handler_unittest.cc b/chromeos/audio/cras_audio_handler_unittest.cc |
index de2c1e48b172f700b3a328b0811ddcdaa0e8750f..95660658f5a15621d91d0e664dadcc4d9aaa4cdd 100644 |
--- a/chromeos/audio/cras_audio_handler_unittest.cc |
+++ b/chromeos/audio/cras_audio_handler_unittest.cc |
@@ -15,6 +15,7 @@ |
#include "base/run_loop.h" |
#include "base/thread_task_runner_handle.h" |
#include "base/values.h" |
+#include "chromeos/audio/audio_devices_pref_handler.h" |
#include "chromeos/audio/audio_devices_pref_handler_stub.h" |
#include "chromeos/dbus/audio_node.h" |
#include "chromeos/dbus/dbus_thread_manager.h" |
@@ -324,6 +325,40 @@ class CrasAudioHandlerTest : public testing::Test { |
message_loop_.RunUntilIdle(); |
} |
+ // Set up cras audio handlers with |audio_nodes| and set the active state of |
+ // |active_device_in_pref| as active and |activate_by_user| in the pref, |
+ // and rest of nodes in |audio_nodes_in_pref| as inactive. |
+ void SetupCrasAudioHandlerWithActiveNodeInPref( |
+ const AudioNodeList& audio_nodes, |
+ const AudioNodeList& audio_nodes_in_pref, |
+ const AudioDevice& active_device_in_pref, |
+ bool activate_by_user) { |
+ DBusThreadManager::Initialize(); |
+ fake_cras_audio_client_ = static_cast<FakeCrasAudioClient*>( |
+ DBusThreadManager::Get()->GetCrasAudioClient()); |
+ audio_pref_handler_ = new AudioDevicesPrefHandlerStub(); |
+ bool active; |
+ for (const AudioNode& node : audio_nodes_in_pref) { |
+ active = node.id == active_device_in_pref.id; |
+ audio_pref_handler_->SetDeviceActive(AudioDevice(node), active, |
+ activate_by_user); |
+ } |
+ |
+ bool activate_by; |
+ EXPECT_TRUE(audio_pref_handler_->GetDeviceActive(active_device_in_pref, |
+ &active, &activate_by)); |
+ EXPECT_TRUE(active); |
+ EXPECT_EQ(activate_by, activate_by_user); |
+ |
+ fake_cras_audio_client_->SetAudioNodesForTesting(audio_nodes); |
+ CrasAudioHandler::Initialize(audio_pref_handler_); |
+ |
+ cras_audio_handler_ = CrasAudioHandler::Get(); |
+ test_observer_.reset(new TestObserver); |
+ cras_audio_handler_->AddAudioObserver(test_observer_.get()); |
+ message_loop_.RunUntilIdle(); |
+ } |
+ |
void SetUpCrasAudioHandlerWithPrimaryActiveNode( |
const AudioNodeList& audio_nodes, |
const AudioNode& primary_active_node) { |
@@ -559,7 +594,8 @@ TEST_F(CrasAudioHandlerTest, SwitchActiveOutputDevice) { |
// Switch the active output to internal speaker. |
AudioDevice internal_speaker(kInternalSpeaker); |
- cras_audio_handler_->SwitchToDevice(internal_speaker, true); |
+ cras_audio_handler_->SwitchToDevice(internal_speaker, true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
// Verify the active output is switched to internal speaker, and the |
// ActiveOutputNodeChanged event is fired. |
@@ -586,7 +622,8 @@ TEST_F(CrasAudioHandlerTest, SwitchActiveInputDevice) { |
// Switch the active input to internal mic. |
AudioDevice internal_mic(kInternalMic); |
- cras_audio_handler_->SwitchToDevice(internal_mic, true); |
+ cras_audio_handler_->SwitchToDevice(internal_mic, true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
// Verify the active output is switched to internal speaker, and the active |
// ActiveInputNodeChanged event is fired. |
@@ -1140,7 +1177,8 @@ TEST_F(CrasAudioHandlerTest, UnplugUSBHeadphonesWithActiveSpeaker) { |
// Select the speaker to be the active output device. |
AudioDevice internal_speaker(kInternalSpeaker); |
- cras_audio_handler_->SwitchToDevice(internal_speaker, true); |
+ cras_audio_handler_->SwitchToDevice(internal_speaker, true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
// Verify the active output is switched to internal speaker, and the |
// ActiveOutputNodeChanged event is fired. |
@@ -1371,7 +1409,8 @@ TEST_F(CrasAudioHandlerTest, PlugUSBMicNotAffectActiveOutput) { |
// Switch the active output to internal speaker. |
AudioDevice internal_speaker(kInternalSpeaker); |
- cras_audio_handler_->SwitchToDevice(internal_speaker, true); |
+ cras_audio_handler_->SwitchToDevice(internal_speaker, true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
// Verify the active output is switched to internal speaker, and the |
// ActiveOutputNodeChanged event is fired. |
@@ -2066,7 +2105,8 @@ TEST_F(CrasAudioHandlerTest, ActiveDeviceSelectionWithStableDeviceId) { |
// Change the active device to internal speaker, now USB headphone becomes |
// inactive. |
AudioDevice speaker(kInternalSpeaker); |
- cras_audio_handler_->SwitchToDevice(speaker, true); |
+ cras_audio_handler_->SwitchToDevice(speaker, true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
EXPECT_NE(kUSBHeadphone1.id, |
cras_audio_handler_->GetPrimaryActiveOutputNode()); |
@@ -2112,6 +2152,11 @@ TEST_F(CrasAudioHandlerTest, ActiveDeviceSelectionWithStableDeviceId) { |
// by its priority. |
EXPECT_EQ(usb_headset.id, cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+ audio_nodes.clear(); |
+ internal_speaker.active = false; |
+ audio_nodes.push_back(internal_speaker); |
+ usb_headset.active = true; |
+ audio_nodes.push_back(usb_headset); |
usb_headset2.active = false; |
usb_headset2.plugged_time = 80000002; |
audio_nodes.push_back(usb_headset2); |
@@ -2122,6 +2167,212 @@ TEST_F(CrasAudioHandlerTest, ActiveDeviceSelectionWithStableDeviceId) { |
EXPECT_EQ(usb_headset2.id, cras_audio_handler_->GetPrimaryActiveOutputNode()); |
} |
+// Test the device new session case, either via reboot or logout, if there |
+// is an active device in the previous session, that device should still |
+// be set as active after the new session starts. |
+TEST_F(CrasAudioHandlerTest, PersistActiveDeviceAcrossSession) { |
+ // Set the active device to internal speaker before the session starts. |
+ AudioNodeList audio_nodes; |
+ audio_nodes.push_back(kInternalSpeaker); |
+ audio_nodes.push_back(kHeadphone); |
+ SetupCrasAudioHandlerWithActiveNodeInPref( |
+ audio_nodes, audio_nodes, AudioDevice(kInternalSpeaker), true); |
+ |
+ // Verify the audio devices size. |
+ AudioDeviceList audio_devices; |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ |
+ // Verify the active device is the internal speaker, which is of a lower |
+ // priority, but selected as active since it was the active device previously. |
+ EXPECT_EQ(kInternalSpeaker.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+} |
+ |
+TEST_F(CrasAudioHandlerTest, PersistActiveSpeakerAcrossReboot) { |
+ // Simulates the device was shut down with three audio devices, and |
+ // internal speaker being the active one selected by user. |
+ AudioNodeList audio_nodes_in_pref; |
+ audio_nodes_in_pref.push_back(kInternalSpeaker); |
+ audio_nodes_in_pref.push_back(kHeadphone); |
+ audio_nodes_in_pref.push_back(kUSBHeadphone1); |
+ |
+ // Simulate the first NodesChanged signal coming with only one node. |
+ AudioNodeList audio_nodes; |
+ audio_nodes.push_back(kUSBHeadphone1); |
+ |
+ SetupCrasAudioHandlerWithActiveNodeInPref( |
+ audio_nodes, audio_nodes_in_pref, AudioDevice(kInternalSpeaker), true); |
+ |
+ // Verify the usb headphone has been made active. |
+ AudioDeviceList audio_devices; |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kUSBHeadphone1.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+ |
+ // Simulate another NodesChanged signal coming later with all ndoes. |
+ audio_nodes.push_back(kInternalSpeaker); |
+ audio_nodes.push_back(kHeadphone); |
+ ChangeAudioNodes(audio_nodes); |
+ |
+ // Verify the active output has been restored to internal speaker. |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kInternalSpeaker.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+} |
+ |
+TEST_F(CrasAudioHandlerTest, |
+ PersistActiveUsbHeadphoneAcrossRebootUsbComeLater) { |
+ // Simulates the device was shut down with three audio devices, and |
+ // usb headphone being the active one selected by priority. |
+ AudioNodeList audio_nodes_in_pref; |
+ audio_nodes_in_pref.push_back(kInternalSpeaker); |
+ audio_nodes_in_pref.push_back(kHeadphone); |
+ audio_nodes_in_pref.push_back(kUSBHeadphone1); |
+ |
+ // Simulate the first NodesChanged signal coming with only internal speaker |
+ // and the headphone. |
+ AudioNodeList audio_nodes; |
+ audio_nodes.push_back(kInternalSpeaker); |
+ audio_nodes.push_back(kHeadphone); |
+ |
+ SetupCrasAudioHandlerWithActiveNodeInPref(audio_nodes, audio_nodes_in_pref, |
+ AudioDevice(kUSBHeadphone1), false); |
+ |
+ // Verify the headphone has been made active. |
+ AudioDeviceList audio_devices; |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kHeadphone.id, cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+ |
+ // Simulate USB node comes later with all ndoes. |
+ AudioNode usb_node(kUSBHeadphone1); |
+ usb_node.plugged_time = 80000000; |
+ audio_nodes.push_back(usb_node); |
+ ChangeAudioNodes(audio_nodes); |
+ |
+ // Verify the active output has been restored to usb headphone. |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kUSBHeadphone1.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+} |
+ |
+TEST_F(CrasAudioHandlerTest, |
+ PersistActiveUsbHeadphoneAcrossRebootUsbComeFirst) { |
+ // Simulates the device was shut down with three audio devices, and |
+ // usb headphone being the active one selected by priority. |
+ AudioNodeList audio_nodes_in_pref; |
+ audio_nodes_in_pref.push_back(kInternalSpeaker); |
+ audio_nodes_in_pref.push_back(kHeadphone); |
+ audio_nodes_in_pref.push_back(kUSBHeadphone1); |
+ |
+ // Simulate the first NodesChanged signal coming with only internal speaker |
+ // and the USB headphone. |
+ AudioNodeList audio_nodes; |
+ audio_nodes.push_back(kInternalSpeaker); |
+ audio_nodes.push_back(kUSBHeadphone1); |
+ |
+ SetupCrasAudioHandlerWithActiveNodeInPref(audio_nodes, audio_nodes_in_pref, |
+ AudioDevice(kUSBHeadphone1), false); |
+ |
+ // Verify the USB headphone has been made active. |
+ AudioDeviceList audio_devices; |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kUSBHeadphone1.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+ |
+ // Simulate another NodesChanged signal coming later with all ndoes. |
+ AudioNode headphone_node(kHeadphone); |
+ headphone_node.plugged_time = 80000000; |
+ audio_nodes.push_back(headphone_node); |
+ ChangeAudioNodes(audio_nodes); |
+ |
+ // Verify the active output has been restored to USB headphone. |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kUSBHeadphone1.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+} |
+ |
+// This covers the crbug.com/586026. Cras lost the active state of the internal |
+// speaker when user unplugs the headphone, which is a bug in cras. However, |
+// chrome code is still resilient and set the internal speaker back to active. |
+TEST_F(CrasAudioHandlerTest, UnplugHeadphoneLostActiveInternalSpeakerByCras) { |
+ // Set up with three nodes. |
+ AudioNodeList audio_nodes; |
+ audio_nodes.push_back(kInternalSpeaker); |
+ audio_nodes.push_back(kHeadphone); |
+ audio_nodes.push_back(kUSBHeadphone1); |
+ SetUpCrasAudioHandler(audio_nodes); |
+ |
+ // Switch the active output to internal speaker. |
+ cras_audio_handler_->SwitchToDevice(AudioDevice(kInternalSpeaker), true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
+ |
+ // Verify internal speaker has been made active. |
+ AudioDeviceList audio_devices; |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kInternalSpeaker.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+ |
+ // Simulate unplug the headphone. Cras sends NodesChanged signal with |
+ // both internal speaker and usb headphone being inactive. |
+ audio_nodes.clear(); |
+ audio_nodes.push_back(kInternalSpeaker); |
+ audio_nodes.push_back(kUSBHeadphone1); |
+ EXPECT_FALSE(kInternalSpeaker.active); |
+ EXPECT_FALSE(kUSBHeadphone1.active); |
+ ChangeAudioNodes(audio_nodes); |
+ |
+ // Verify the active output is set back to internal speaker. |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kInternalSpeaker.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+} |
+ |
+TEST_F(CrasAudioHandlerTest, RemoveNonActiveDevice) { |
+ // Set up with three nodes. |
+ AudioNodeList audio_nodes; |
+ audio_nodes.push_back(kInternalSpeaker); |
+ audio_nodes.push_back(kHeadphone); |
+ audio_nodes.push_back(kUSBHeadphone1); |
+ SetUpCrasAudioHandler(audio_nodes); |
+ |
+ // Switch the active output to internal speaker. |
+ cras_audio_handler_->SwitchToDevice(AudioDevice(kInternalSpeaker), true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
+ |
+ // Verify internal speaker has been made active. |
+ AudioDeviceList audio_devices; |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kInternalSpeaker.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+ |
+ // Remove headphone, which is an non-active device. |
+ audio_nodes.clear(); |
+ AudioNode speaker(kInternalSpeaker); |
+ speaker.active = true; |
+ audio_nodes.push_back(speaker); |
+ AudioNode usb_headphone(kUSBHeadphone1); |
+ usb_headphone.active = false; |
+ audio_nodes.push_back(usb_headphone); |
+ |
+ ChangeAudioNodes(audio_nodes); |
+ |
+ // Verify the active output remains as internal speaker. |
+ cras_audio_handler_->GetAudioDevices(&audio_devices); |
+ EXPECT_EQ(audio_nodes.size(), audio_devices.size()); |
+ EXPECT_EQ(kInternalSpeaker.id, |
+ cras_audio_handler_->GetPrimaryActiveOutputNode()); |
+} |
+ |
TEST_F(CrasAudioHandlerTest, ChangeActiveNodesHotrodInit) { |
AudioNodeList audio_nodes; |
audio_nodes.push_back(kHDMIOutput); |
@@ -2552,7 +2803,8 @@ TEST_F(CrasAudioHandlerTest, HotPlugHDMINotChangeActiveOutput) { |
// Manually set the active output to internal speaker. |
AudioDevice internal_output(kInternalSpeaker); |
- cras_audio_handler_->SwitchToDevice(internal_output, true); |
+ cras_audio_handler_->SwitchToDevice(internal_output, true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
// Verify the active output is switched to internal speaker. |
EXPECT_EQ(internal_speaker.id, |
@@ -2631,7 +2883,8 @@ TEST_F(CrasAudioHandlerTest, HDMIRemainInactiveAfterSuspendResume) { |
EXPECT_EQ(hdmi_output.id, cras_audio_handler_->GetPrimaryActiveOutputNode()); |
// Manually set the active output to internal speaker. |
- cras_audio_handler_->SwitchToDevice(AudioDevice(internal_speaker), true); |
+ cras_audio_handler_->SwitchToDevice(AudioDevice(internal_speaker), true, |
+ CrasAudioHandler::ACTIVATE_BY_USER); |
EXPECT_EQ(internal_speaker.id, |
cras_audio_handler_->GetPrimaryActiveOutputNode()); |