summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjennyz <jennyz@chromium.org>2015-06-29 16:46:06 -0700
committerCommit bot <commit-bot@chromium.org>2015-06-29 23:47:54 +0000
commitc770dc778a8d2aedf0cc2f9a803a75db4211e4ac (patch)
treeddde1a02b2e44e89a3ced038abcdc43a06120075
parent0a3e147218d4e6fc8ddff329f54b35283e1e164a (diff)
downloadchromium_src-c770dc778a8d2aedf0cc2f9a803a75db4211e4ac.zip
chromium_src-c770dc778a8d2aedf0cc2f9a803a75db4211e4ac.tar.gz
chromium_src-c770dc778a8d2aedf0cc2f9a803a75db4211e4ac.tar.bz2
Work around for HDMI audio output rediscovering transistion loss.
When audio output is playing on HDMI device, each time user opens or closes the lid of the device, changes the resolution of the hdmi display, or the device resumes after suspension, there is a short period of time the audio will be played on internal speaker, then goes back to hdmi device., The root cause is due to the fact that cras will lose hdmi output audio device for a short period of time and rediscover it later during the transition period of these scenarios. The hot plug cause the internal speaker be selected as an alternative active output device during the transition period. For some platform, the transition period is quite long as several seconds and it is an annoying user experience. I've made a workaround in CrasAudioHandler to mute the output during the hdmi rediscovering grace period, so that the audio will NOT be leaked. BUG=503667 Review URL: https://codereview.chromium.org/1199413008 Cr-Commit-Position: refs/heads/master@{#336681}
-rw-r--r--ash/system/audio/tray_audio.cc30
-rw-r--r--ash/system/audio/tray_audio.h12
-rw-r--r--ash/system/audio/tray_audio_delegate.h9
-rw-r--r--ash/system/chromeos/audio/tray_audio_chromeos.cc38
-rw-r--r--ash/system/chromeos/audio/tray_audio_chromeos.h14
-rw-r--r--ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc6
-rw-r--r--ash/system/chromeos/audio/tray_audio_delegate_chromeos.h2
-rw-r--r--ash/system/win/audio/tray_audio_delegate_win.cc4
-rw-r--r--ash/system/win/audio/tray_audio_delegate_win.h2
-rw-r--r--chromeos/audio/cras_audio_handler.cc65
-rw-r--r--chromeos/audio/cras_audio_handler.h25
-rw-r--r--chromeos/audio/cras_audio_handler_unittest.cc104
12 files changed, 288 insertions, 23 deletions
diff --git a/ash/system/audio/tray_audio.cc b/ash/system/audio/tray_audio.cc
index 7260fc0..13269bc 100644
--- a/ash/system/audio/tray_audio.cc
+++ b/ash/system/audio/tray_audio.cc
@@ -138,6 +138,21 @@ void TrayAudio::OnActiveInputNodeChanged() {
Update();
}
+void TrayAudio::ChangeInternalSpeakerChannelMode() {
+ // Swap left/right channel only if it is in Yoga mode.
+ system::TrayAudioDelegate::AudioChannelMode channel_mode =
+ system::TrayAudioDelegate::NORMAL;
+ if (gfx::Display::InternalDisplayId() != gfx::Display::kInvalidDisplayID) {
+ const DisplayInfo& display_info =
+ Shell::GetInstance()->display_manager()->GetDisplayInfo(
+ gfx::Display::InternalDisplayId());
+ if (display_info.GetActiveRotation() == gfx::Display::ROTATE_180)
+ channel_mode = system::TrayAudioDelegate::LEFT_RIGHT_SWAPPED;
+ }
+
+ audio_delegate_->SetInternalSpeakerChannelMode(channel_mode);
+}
+
void TrayAudio::OnDisplayAdded(const gfx::Display& new_display) {
if (new_display.id() != gfx::Display::InternalDisplayId())
return;
@@ -159,21 +174,6 @@ void TrayAudio::OnDisplayMetricsChanged(const gfx::Display& display,
ChangeInternalSpeakerChannelMode();
}
-void TrayAudio::ChangeInternalSpeakerChannelMode() {
- // Swap left/right channel only if it is in Yoga mode.
- system::TrayAudioDelegate::AudioChannelMode channel_mode =
- system::TrayAudioDelegate::NORMAL;
- if (gfx::Display::InternalDisplayId() != gfx::Display::kInvalidDisplayID) {
- const DisplayInfo& display_info =
- Shell::GetInstance()->display_manager()->GetDisplayInfo(
- gfx::Display::InternalDisplayId());
- if (display_info.GetActiveRotation() == gfx::Display::ROTATE_180)
- channel_mode = system::TrayAudioDelegate::LEFT_RIGHT_SWAPPED;
- }
-
- audio_delegate_->SetInternalSpeakerChannelMode(channel_mode);
-}
-
void TrayAudio::Update() {
if (tray_view())
tray_view()->SetVisible(GetInitialVisibility());
diff --git a/ash/system/audio/tray_audio.h b/ash/system/audio/tray_audio.h
index 9d3b2c9..caf5760 100644
--- a/ash/system/audio/tray_audio.h
+++ b/ash/system/audio/tray_audio.h
@@ -31,6 +31,12 @@ class TrayAudio : public TrayImageItem,
static bool ShowAudioDeviceMenu();
protected:
+ // Overridden from gfx::DisplayObserver.
+ void OnDisplayAdded(const gfx::Display& new_display) override;
+ void OnDisplayRemoved(const gfx::Display& old_display) override;
+ void OnDisplayMetricsChanged(const gfx::Display& display,
+ uint32_t changed_metrics) override;
+
virtual void Update();
scoped_ptr<system::TrayAudioDelegate> audio_delegate_;
@@ -59,12 +65,6 @@ class TrayAudio : public TrayImageItem,
void OnActiveOutputNodeChanged() override;
void OnActiveInputNodeChanged() override;
- // Overridden from gfx::DisplayObserver.
- void OnDisplayAdded(const gfx::Display& new_display) override;
- void OnDisplayRemoved(const gfx::Display& old_display) override;
- void OnDisplayMetricsChanged(const gfx::Display& display,
- uint32_t changed_metrics) override;
-
void ChangeInternalSpeakerChannelMode();
DISALLOW_COPY_AND_ASSIGN(TrayAudio);
diff --git a/ash/system/audio/tray_audio_delegate.h b/ash/system/audio/tray_audio_delegate.h
index 335b90d..bcb1c61 100644
--- a/ash/system/audio/tray_audio_delegate.h
+++ b/ash/system/audio/tray_audio_delegate.h
@@ -47,6 +47,15 @@ class ASH_EXPORT TrayAudioDelegate {
// Sets the internal speaker's channel mode.
virtual void SetInternalSpeakerChannelMode(AudioChannelMode mode) = 0;
+
+ // If necessary, sets the starting point for re-discovering the active HDMI
+ // output device caused by device entering/exiting docking mode, HDMI display
+ // changing resolution, or chromeos device suspend/resume. If
+ // |force_rediscovering| is true, it will force to set the starting point for
+ // re-discovering the active HDMI output device again if it has been in the
+ // middle of rediscovering the HDMI active output device.
+ virtual void SetActiveHDMIOutoutRediscoveringIfNecessary(
+ bool force_rediscovering) = 0;
};
} // namespace system
diff --git a/ash/system/chromeos/audio/tray_audio_chromeos.cc b/ash/system/chromeos/audio/tray_audio_chromeos.cc
index d944e4a..3d420ff 100644
--- a/ash/system/chromeos/audio/tray_audio_chromeos.cc
+++ b/ash/system/chromeos/audio/tray_audio_chromeos.cc
@@ -9,6 +9,7 @@
#include "ash/system/audio/volume_view.h"
#include "ash/system/chromeos/audio/audio_detailed_view.h"
#include "ash/system/chromeos/audio/tray_audio_delegate_chromeos.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
#include "ui/views/view.h"
namespace ash {
@@ -20,9 +21,13 @@ TrayAudioChromeOs::TrayAudioChromeOs(SystemTray* system_tray)
: TrayAudio(system_tray,
scoped_ptr<TrayAudioDelegate>(new TrayAudioDelegateChromeOs())),
audio_detail_view_(NULL) {
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
+ this);
}
TrayAudioChromeOs::~TrayAudioChromeOs() {
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
+ this);
}
void TrayAudioChromeOs::Update() {
@@ -53,4 +58,37 @@ void TrayAudioChromeOs::DestroyDetailedView() {
}
}
+void TrayAudioChromeOs::OnDisplayAdded(const gfx::Display& new_display) {
+ TrayAudio::OnDisplayAdded(new_display);
+
+ // This event will be triggered when the lid of the device is opened to exit
+ // the docked mode, we should always start or re-start HDMI re-discovering
+ // grace period right after this event.
+ audio_delegate_->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
+}
+
+void TrayAudioChromeOs::OnDisplayRemoved(const gfx::Display& old_display) {
+ TrayAudio::OnDisplayRemoved(old_display);
+
+ // This event will be triggered when the lid of the device is closed to enter
+ // the docked mode, we should always start or re-start HDMI re-discovering
+ // grace period right after this event.
+ audio_delegate_->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
+}
+
+void TrayAudioChromeOs::OnDisplayMetricsChanged(const gfx::Display& display,
+ uint32_t changed_metrics) {
+ // The event could be triggered multiple times during the HDMI display
+ // transition, we don't need to restart HDMI re-discovering grace period
+ // it is already started earlier.
+ audio_delegate_->SetActiveHDMIOutoutRediscoveringIfNecessary(false);
+}
+
+void TrayAudioChromeOs::SuspendDone(const base::TimeDelta& sleep_duration) {
+ // This event is triggered when the device resumes after earlier suspension,
+ // we should always start or re-start HDMI re-discovering
+ // grace period right after this event.
+ audio_delegate_->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
+}
+
} // namespace ash
diff --git a/ash/system/chromeos/audio/tray_audio_chromeos.h b/ash/system/chromeos/audio/tray_audio_chromeos.h
index a20a49d..ed313e0 100644
--- a/ash/system/chromeos/audio/tray_audio_chromeos.h
+++ b/ash/system/chromeos/audio/tray_audio_chromeos.h
@@ -8,13 +8,16 @@
#include "ash/ash_export.h"
#include "ash/system/audio/tray_audio.h"
#include "base/memory/scoped_ptr.h"
+#include "chromeos/dbus/power_manager_client.h"
namespace ash {
namespace tray {
class AudioDetailedView;
}
-class ASH_EXPORT TrayAudioChromeOs : public TrayAudio {
+class ASH_EXPORT TrayAudioChromeOs
+ : public TrayAudio,
+ public chromeos::PowerManagerClient::Observer {
public:
explicit TrayAudioChromeOs(SystemTray* system_tray);
~TrayAudioChromeOs() override;
@@ -28,6 +31,15 @@ class ASH_EXPORT TrayAudioChromeOs : public TrayAudio {
views::View* CreateDetailedView(user::LoginStatus status) override;
void DestroyDetailedView() override;
+ // Overridden from gfx::DisplayObserver.
+ void OnDisplayAdded(const gfx::Display& new_display) override;
+ void OnDisplayRemoved(const gfx::Display& old_display) override;
+ void OnDisplayMetricsChanged(const gfx::Display& display,
+ uint32_t changed_metrics) override;
+
+ // Overriden from chromeos::PowerManagerClient::Observer.
+ void SuspendDone(const base::TimeDelta& sleep_duration) override;
+
tray::AudioDetailedView* audio_detail_view_;
DISALLOW_COPY_AND_ASSIGN(TrayAudioChromeOs);
diff --git a/ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc b/ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc
index fb1532f..daa3453 100644
--- a/ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc
+++ b/ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc
@@ -66,5 +66,11 @@ void TrayAudioDelegateChromeOs::SetInternalSpeakerChannelMode(
mode == LEFT_RIGHT_SWAPPED);
}
+void TrayAudioDelegateChromeOs::SetActiveHDMIOutoutRediscoveringIfNecessary(
+ bool force_rediscovering) {
+ CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(
+ force_rediscovering);
+}
+
} // namespace system
} // namespace ash
diff --git a/ash/system/chromeos/audio/tray_audio_delegate_chromeos.h b/ash/system/chromeos/audio/tray_audio_delegate_chromeos.h
index c51d246b..0695e4b 100644
--- a/ash/system/chromeos/audio/tray_audio_delegate_chromeos.h
+++ b/ash/system/chromeos/audio/tray_audio_delegate_chromeos.h
@@ -26,6 +26,8 @@ class ASH_EXPORT TrayAudioDelegateChromeOs : public TrayAudioDelegate {
void SetOutputAudioIsMuted(bool is_muted) override;
void SetOutputVolumeLevel(int level) override;
void SetInternalSpeakerChannelMode(AudioChannelMode mode) override;
+ void SetActiveHDMIOutoutRediscoveringIfNecessary(
+ bool force_rediscovering) override;
};
} // namespace system
diff --git a/ash/system/win/audio/tray_audio_delegate_win.cc b/ash/system/win/audio/tray_audio_delegate_win.cc
index 18e46af..2b6095a 100644
--- a/ash/system/win/audio/tray_audio_delegate_win.cc
+++ b/ash/system/win/audio/tray_audio_delegate_win.cc
@@ -96,6 +96,10 @@ void TrayAudioDelegateWin::SetInternalSpeakerChannelMode(
AudioChannelMode mode) {
}
+void TrayAudioDelegateWin::SetActiveHDMIOutoutRediscoveringIfNecessary(
+ bool force_rediscovering) {
+}
+
ScopedComPtr<ISimpleAudioVolume>
TrayAudioDelegateWin::CreateDefaultVolumeControl() {
ScopedComPtr<ISimpleAudioVolume> volume_control;
diff --git a/ash/system/win/audio/tray_audio_delegate_win.h b/ash/system/win/audio/tray_audio_delegate_win.h
index 9ab0208..ec23c9c 100644
--- a/ash/system/win/audio/tray_audio_delegate_win.h
+++ b/ash/system/win/audio/tray_audio_delegate_win.h
@@ -30,6 +30,8 @@ class ASH_EXPORT TrayAudioDelegateWin : public TrayAudioDelegate {
void SetOutputAudioIsMuted(bool is_muted) override;
void SetOutputVolumeLevel(int level) override;
void SetInternalSpeakerChannelMode(AudioChannelMode mode) override;
+ void SetActiveHDMIOutoutRediscoveringIfNecessary(
+ bool force_rediscovering) override;
private:
base::win::ScopedComPtr<ISimpleAudioVolume> CreateDefaultVolumeControl();
diff --git a/chromeos/audio/cras_audio_handler.cc b/chromeos/audio/cras_audio_handler.cc
index 3a86d86..1cf03d1 100644
--- a/chromeos/audio/cras_audio_handler.cc
+++ b/chromeos/audio/cras_audio_handler.cc
@@ -28,6 +28,9 @@ const int kDefaultUnmuteVolumePercent = 4;
// Volume value which should be considered as muted in range [0, 100].
const int kMuteThresholdPercent = 1;
+// The duration of HDMI output re-discover grace period in milliseconds.
+const int kHDMIRediscoverGracePeriodDurationInMs = 2000;
+
static CrasAudioHandler* g_cras_audio_handler = NULL;
bool IsSameAudioDevice(const AudioDevice& a, const AudioDevice& b) {
@@ -432,6 +435,26 @@ void CrasAudioHandler::LogErrors() {
log_errors_ = true;
}
+// If the HDMI device is the active output device, when the device enters/exits
+// docking mode, or HDMI display changes resolution, or chromeos device
+// suspends/resumes, cras will lose the HDMI output node for a short period of
+// time, then rediscover it. This hotplug behavior will cause the audio output
+// be leaked to the alternatvie active audio output during HDMI re-discovering
+// period. See crbug.com/503667.
+void CrasAudioHandler::SetActiveHDMIOutoutRediscoveringIfNecessary(
+ bool force_rediscovering) {
+ if (!GetDeviceFromId(active_output_node_id_))
+ return;
+
+ // Marks the start of the HDMI re-discovering grace period, during which we
+ // will mute the audio output to prevent it to be be leaked to the
+ // alternative output device.
+ if ((hdmi_rediscovering_ && force_rediscovering) ||
+ (!hdmi_rediscovering_ && IsHDMIPrimaryOutputDevice())) {
+ StartHDMIRediscoverGracePeriod();
+ }
+}
+
CrasAudioHandler::CrasAudioHandler(
scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
: audio_pref_handler_(audio_pref_handler),
@@ -445,6 +468,9 @@ CrasAudioHandler::CrasAudioHandler(
has_alternative_output_(false),
output_mute_locked_(false),
log_errors_(false),
+ hdmi_rediscover_grace_period_duration_in_ms_(
+ kHDMIRediscoverGracePeriodDurationInMs),
+ hdmi_rediscovering_(false),
weak_ptr_factory_(this) {
if (!audio_pref_handler.get())
return;
@@ -464,6 +490,7 @@ CrasAudioHandler::CrasAudioHandler(
}
CrasAudioHandler::~CrasAudioHandler() {
+ hdmi_rediscover_timer_.Stop();
if (!chromeos::DBusThreadManager::IsInitialized() ||
!chromeos::DBusThreadManager::Get() ||
!chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
@@ -570,7 +597,13 @@ void CrasAudioHandler::SetupAudioOutputState() {
return;
}
DCHECK(!device->is_input);
- output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
+ // Mute the output during HDMI re-discovering grace period.
+ if (hdmi_rediscovering_ && !IsHDMIPrimaryOutputDevice()) {
+ VLOG(1) << "Mute the output during HDMI re-discovering grace period";
+ output_mute_on_ = true;
+ } else {
+ output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
+ }
output_volume_ = audio_pref_handler_->GetOutputVolumeValue(device);
SetOutputMuteInternal(output_mute_on_);
@@ -976,4 +1009,34 @@ void CrasAudioHandler::RemoveActiveNodeInternal(uint64_t node_id, bool notify) {
}
}
+void CrasAudioHandler::UpdateAudioAfterHDMIRediscoverGracePeriod() {
+ VLOG(1) << "HDMI output re-discover grace period ends.";
+ hdmi_rediscovering_ = false;
+ if (!IsOutputMutedForDevice(active_output_node_id_)) {
+ // Unmute the audio output after the HDMI transition period.
+ VLOG(1) << "Unmute output after HDMI rediscovring grace period.";
+ SetOutputMuteInternal(false);
+ }
+}
+
+bool CrasAudioHandler::IsHDMIPrimaryOutputDevice() const {
+ const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
+ return (device && device->type == chromeos::AUDIO_TYPE_HDMI);
+}
+
+void CrasAudioHandler::StartHDMIRediscoverGracePeriod() {
+ VLOG(1) << "Start HDMI rediscovering grace period.";
+ hdmi_rediscovering_ = true;
+ hdmi_rediscover_timer_.Stop();
+ hdmi_rediscover_timer_.Start(
+ FROM_HERE, base::TimeDelta::FromMilliseconds(
+ hdmi_rediscover_grace_period_duration_in_ms_),
+ this, &CrasAudioHandler::UpdateAudioAfterHDMIRediscoverGracePeriod);
+}
+
+void CrasAudioHandler::SetHDMIRediscoverGracePeriodForTesting(
+ int duration_in_ms) {
+ hdmi_rediscover_grace_period_duration_in_ms_ = duration_in_ms;
+}
+
} // namespace chromeos
diff --git a/chromeos/audio/cras_audio_handler.h b/chromeos/audio/cras_audio_handler.h
index 633e30a..10c77a4 100644
--- a/chromeos/audio/cras_audio_handler.h
+++ b/chromeos/audio/cras_audio_handler.h
@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "base/timer/timer.h"
#include "chromeos/audio/audio_device.h"
#include "chromeos/audio/audio_pref_observer.h"
#include "chromeos/dbus/audio_node.h"
@@ -188,6 +189,15 @@ class CHROMEOS_EXPORT CrasAudioHandler : public CrasAudioClient::Observer,
// Enables error logging.
virtual void LogErrors();
+ // If necessary, sets the starting point for re-discovering the active HDMI
+ // output device caused by device entering/exiting docking mode, HDMI display
+ // changing resolution, or chromeos device suspend/resume. If
+ // |force_rediscovering| is true, it will force to set the starting point for
+ // re-discovering the active HDMI output device again if it has been in the
+ // middle of rediscovering the HDMI active output device.
+ virtual void SetActiveHDMIOutoutRediscoveringIfNecessary(
+ bool force_rediscovering);
+
protected:
explicit CrasAudioHandler(
scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler);
@@ -293,6 +303,16 @@ class CHROMEOS_EXPORT CrasAudioHandler : public CrasAudioClient::Observer,
// Removes |node_id| from additional active nodes.
void RemoveActiveNodeInternal(uint64_t node_id, bool notify);
+ void UpdateAudioAfterHDMIRediscoverGracePeriod();
+
+ bool IsHDMIPrimaryOutputDevice() const;
+
+ void StartHDMIRediscoverGracePeriod();
+
+ bool hdmi_rediscovering() const { return hdmi_rediscovering_; }
+
+ void SetHDMIRediscoverGracePeriodForTesting(int duration_in_ms);
+
enum DeviceStatus {
OLD_DEVICE,
NEW_DEVICE,
@@ -328,6 +348,11 @@ class CHROMEOS_EXPORT CrasAudioHandler : public CrasAudioClient::Observer,
// Failures are not logged at startup, since CRAS may not be running yet.
bool log_errors_;
+ // Timer for HDMI re-discovering grace period.
+ base::OneShotTimer<CrasAudioHandler> hdmi_rediscover_timer_;
+ int hdmi_rediscover_grace_period_duration_in_ms_;
+ bool hdmi_rediscovering_;
+
base::WeakPtrFactory<CrasAudioHandler> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(CrasAudioHandler);
diff --git a/chromeos/audio/cras_audio_handler_unittest.cc b/chromeos/audio/cras_audio_handler_unittest.cc
index fa5dbc3..7472a7f 100644
--- a/chromeos/audio/cras_audio_handler_unittest.cc
+++ b/chromeos/audio/cras_audio_handler_unittest.cc
@@ -4,9 +4,12 @@
#include "chromeos/audio/cras_audio_handler.h"
+#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
#include "base/values.h"
#include "chromeos/audio/audio_devices_pref_handler_stub.h"
#include "chromeos/dbus/audio_node.h"
@@ -343,6 +346,18 @@ class CrasAudioHandlerTest : public testing::Test {
return num_active_nodes;
}
+ void SetActiveHDMIRediscover() {
+ cras_audio_handler_->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
+ }
+
+ void SetHDMIRediscoverGracePeriodDuration(int duration_in_ms) {
+ cras_audio_handler_->SetHDMIRediscoverGracePeriodForTesting(duration_in_ms);
+ }
+
+ bool IsDuringHDMIRediscoverGracePeriod() {
+ return cras_audio_handler_->hdmi_rediscovering();
+ }
+
protected:
base::MessageLoopForUI message_loop_;
CrasAudioHandler* cras_audio_handler_; // Not owned.
@@ -354,6 +369,46 @@ class CrasAudioHandlerTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(CrasAudioHandlerTest);
};
+class HDMIRediscoverWaiter {
+ public:
+ HDMIRediscoverWaiter(CrasAudioHandlerTest* cras_audio_handler_test,
+ int grace_period_duration_in_ms)
+ : cras_audio_handler_test_(cras_audio_handler_test),
+ grace_period_duration_in_ms_(grace_period_duration_in_ms) {}
+
+ void WaitUntilTimeOut(int wait_duration_in_ms) {
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(wait_duration_in_ms));
+ run_loop.Run();
+ }
+
+ void CheckHDMIRediscoverGracePeriodEnd(const base::Closure& quit_loop_func) {
+ if (!cras_audio_handler_test_->IsDuringHDMIRediscoverGracePeriod()) {
+ quit_loop_func.Run();
+ return;
+ }
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&HDMIRediscoverWaiter::CheckHDMIRediscoverGracePeriodEnd,
+ base::Unretained(this), quit_loop_func),
+ base::TimeDelta::FromMilliseconds(grace_period_duration_in_ms_ / 4));
+ }
+
+ void WaitUntilHDMIRediscoverGracePeriodEnd() {
+ base::RunLoop run_loop;
+ CheckHDMIRediscoverGracePeriodEnd(run_loop.QuitClosure());
+ run_loop.Run();
+ }
+
+ private:
+ CrasAudioHandlerTest* cras_audio_handler_test_; // not owned
+ int grace_period_duration_in_ms_;
+
+ DISALLOW_COPY_AND_ASSIGN(HDMIRediscoverWaiter);
+};
+
TEST_F(CrasAudioHandlerTest, InitializeWithOnlyDefaultAudioDevices) {
AudioNodeList audio_nodes;
audio_nodes.push_back(kInternalSpeaker);
@@ -2493,4 +2548,53 @@ TEST_F(CrasAudioHandlerTest, ActiveNodeLostDuringLoginSession) {
EXPECT_TRUE(headphone_resumed->active);
}
+// This test HDMI output rediscovering case in crbug.com/503667.
+TEST_F(CrasAudioHandlerTest, HDMIOutputRediscover) {
+ AudioNodeList audio_nodes;
+ audio_nodes.push_back(kInternalSpeaker);
+ audio_nodes.push_back(kHDMIOutput);
+ SetUpCrasAudioHandler(audio_nodes);
+
+ // Verify the HDMI device has been selected as the active output, and audio
+ // output is not muted.
+ AudioDevice active_output;
+ EXPECT_TRUE(
+ cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
+ EXPECT_EQ(kHDMIOutput.id, active_output.id);
+ EXPECT_EQ(kHDMIOutput.id, cras_audio_handler_->GetPrimaryActiveOutputNode());
+ EXPECT_TRUE(cras_audio_handler_->has_alternative_output());
+ EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
+
+ // Trigger HDMI rediscovering grace period, and remove the HDMI node.
+ const int grace_period_in_ms = 200;
+ SetHDMIRediscoverGracePeriodDuration(grace_period_in_ms);
+ SetActiveHDMIRediscover();
+ AudioNodeList audio_nodes_lost_hdmi;
+ audio_nodes_lost_hdmi.push_back(kInternalSpeaker);
+ ChangeAudioNodes(audio_nodes_lost_hdmi);
+
+ // Verify the active output is switched to internal speaker, it is not muted
+ // by preference, but the system output is muted during the grace period.
+ EXPECT_TRUE(
+ cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
+ EXPECT_EQ(kInternalSpeaker.id, active_output.id);
+ EXPECT_FALSE(
+ cras_audio_handler_->IsOutputMutedForDevice(kInternalSpeaker.id));
+ EXPECT_TRUE(cras_audio_handler_->IsOutputMuted());
+
+ // Re-attach the HDMI device after a little delay.
+ HDMIRediscoverWaiter waiter(this, grace_period_in_ms);
+ waiter.WaitUntilTimeOut(grace_period_in_ms / 4);
+ ChangeAudioNodes(audio_nodes);
+
+ // After HDMI re-discover grace period, verify HDMI output is selected as the
+ // active device and not muted.
+ waiter.WaitUntilHDMIRediscoverGracePeriodEnd();
+ EXPECT_TRUE(
+ cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
+ EXPECT_EQ(kHDMIOutput.id, active_output.id);
+ EXPECT_EQ(kHDMIOutput.id, cras_audio_handler_->GetPrimaryActiveOutputNode());
+ EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
+}
+
} // namespace chromeos