Kill audio process at audio thread hang.

* Introduce feature AudioServiceOutOfProcessKillAtHang with timeout parameter.
* If feature is enabled, kill process when audio thread is detected
  as hung, using timeout (or default timeout).
* Add info about feature and parameter in media-internals.

Enable with command line flags
--enable-features=AudioServiceAudioStreams,AudioServiceOutOfProcess,"AudioServiceOutOfProcessKillAtHang<MyStudyName" --force-fieldtrials=MyStudyName/Group1 --force-fieldtrial-params=MyStudyName.Group1:timeout_seconds/60

Bug: 963810
Change-Id: Ic05a552763452855982ae76dc92c4b04872b0220
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1615018
Reviewed-by: Olga Sharonova <olka@chromium.org>
Reviewed-by: Max Morin <maxmorin@chromium.org>
Commit-Queue: Henrik Grunell <grunell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#661404}
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index 344aa3a..d1a17e07 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -12,9 +12,11 @@
 #include "base/bind.h"
 #include "base/containers/adapters.h"
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
@@ -33,6 +35,7 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/service_manager_connection.h"
+#include "media/audio/audio_features.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/media_log_event.h"
 #include "media/filters/gpu_video_decoder.h"
@@ -705,8 +708,25 @@
         base::Value(base::FeatureList::IsEnabled(feature) ? "Enabled"
                                                           : "Disabled"));
   };
+
   set_feature_data(features::kAudioServiceAudioStreams);
   set_feature_data(features::kAudioServiceOutOfProcess);
+
+  std::string feature_value_string;
+  if (base::FeatureList::IsEnabled(
+          features::kAudioServiceOutOfProcessKillAtHang)) {
+    std::string timeout_value = base::GetFieldTrialParamValueByFeature(
+        features::kAudioServiceOutOfProcessKillAtHang, "timeout_seconds");
+    if (timeout_value.empty())
+      timeout_value = "<undefined>";
+    feature_value_string =
+        base::StrCat({"Enabled, timeout = ", timeout_value, " seconds"});
+  } else {
+    feature_value_string = "Disabled";
+  }
+  audio_info_data.SetKey(features::kAudioServiceOutOfProcessKillAtHang.name,
+                         base::Value(feature_value_string));
+
   set_feature_data(features::kAudioServiceLaunchOnStartup);
   set_feature_data(service_manager::features::kAudioServiceSandbox);
   set_feature_data(features::kWebRtcApmInAudioService);
diff --git a/media/audio/audio_features.cc b/media/audio/audio_features.cc
index cfb0b53..e5fdc299 100644
--- a/media/audio/audio_features.cc
+++ b/media/audio/audio_features.cc
@@ -6,6 +6,11 @@
 
 namespace features {
 
+// When the audio service in a separate process, kill it when a hang is
+// detected. It will be restarted when needed.
+const base::Feature kAudioServiceOutOfProcessKillAtHang{
+    "AudioServiceOutOfProcessKillAtHang", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled, base::DumpWithoutCrashing is called whenever an audio service
 // hang is detected.
 const base::Feature kDumpOnAudioServiceHang{"DumpOnAudioServiceHang",
diff --git a/media/audio/audio_features.h b/media/audio/audio_features.h
index abf4c66..70e228d 100644
--- a/media/audio/audio_features.h
+++ b/media/audio/audio_features.h
@@ -11,6 +11,7 @@
 
 namespace features {
 
+MEDIA_EXPORT extern const base::Feature kAudioServiceOutOfProcessKillAtHang;
 MEDIA_EXPORT extern const base::Feature kDumpOnAudioServiceHang;
 
 #if defined(OS_CHROMEOS)
diff --git a/media/audio/audio_thread_hang_monitor.cc b/media/audio/audio_thread_hang_monitor.cc
index 7e94838..727e198 100644
--- a/media/audio/audio_thread_hang_monitor.cc
+++ b/media/audio/audio_thread_hang_monitor.cc
@@ -8,10 +8,12 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/location.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/power_monitor/power_monitor.h"
+#include "base/process/process.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
@@ -28,7 +30,9 @@
 // caught (e.g., the system suspends before a OnSuspend() event can be fired).
 constexpr int kMaxFailedPingsCount = 3;
 
-constexpr base::TimeDelta kHungDeadline = base::TimeDelta::FromMinutes(1);
+// The default deadline after which we consider the audio thread hung.
+constexpr base::TimeDelta kDefaultHangDeadline =
+    base::TimeDelta::FromMinutes(3);
 
 }  // namespace
 
@@ -37,7 +41,8 @@
 
 // static
 AudioThreadHangMonitor::Ptr AudioThreadHangMonitor::Create(
-    bool dump_on_hang,
+    HangAction hang_action,
+    base::Optional<base::TimeDelta> hang_deadline,
     const base::TickClock* clock,
     scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner,
     scoped_refptr<base::SequencedTaskRunner> monitor_task_runner) {
@@ -45,7 +50,7 @@
     monitor_task_runner = base::CreateSequencedTaskRunnerWithTraits({});
 
   auto monitor =
-      Ptr(new AudioThreadHangMonitor(dump_on_hang, clock,
+      Ptr(new AudioThreadHangMonitor(hang_action, hang_deadline, clock,
                                      std::move(audio_thread_task_runner)),
           base::OnTaskRunnerDeleter(monitor_task_runner));
 
@@ -65,13 +70,19 @@
 }
 
 AudioThreadHangMonitor::AudioThreadHangMonitor(
-    bool dump_on_hang,
+    HangAction hang_action,
+    base::Optional<base::TimeDelta> hang_deadline,
     const base::TickClock* clock,
     scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner)
     : clock_(clock),
       alive_flag_(base::MakeRefCounted<SharedAtomicFlag>()),
       audio_task_runner_(std::move(audio_thread_task_runner)),
-      dump_on_hang_(dump_on_hang),
+      hang_action_(hang_action),
+      ping_interval_((hang_deadline ? hang_deadline.value().is_zero()
+                                          ? kDefaultHangDeadline
+                                          : hang_deadline.value()
+                                    : kDefaultHangDeadline) /
+                     kMaxFailedPingsCount),
       timer_(clock_) {
   DETACH_FROM_SEQUENCE(monitor_sequence_);
 }
@@ -88,7 +99,7 @@
 
   // |this| owns |timer_|, so Unretained is safe.
   timer_.Start(
-      FROM_HERE, kHungDeadline,
+      FROM_HERE, ping_interval_,
       base::BindRepeating(&AudioThreadHangMonitor::CheckIfAudioThreadIsAlive,
                           base::Unretained(this)));
 }
@@ -112,7 +123,7 @@
   // An unexpected |time_since_last_check| may indicate that the system has been
   // in sleep mode, in which case the audio thread may have had insufficient
   // time to respond to the ping. In such a case, skip the check for now.
-  if (time_since_last_check > kHungDeadline + base::TimeDelta::FromSeconds(1))
+  if (time_since_last_check > ping_interval_ + base::TimeDelta::FromSeconds(1))
     return;
 
   const bool audio_thread_responded_to_last_ping = alive_flag_->flag_;
@@ -138,8 +149,13 @@
       audio_thread_status_ = ThreadStatus::kHung;
       LogHistogramThreadStatus();
 
-      if (dump_on_hang_) {
-        base::debug::DumpWithoutCrashing();
+      if (hang_action_ == HangAction::kDump ||
+          hang_action_ == HangAction::kDumpAndTerminateCurrentProcess) {
+        DumpWithoutCrashing();
+      }
+      if (hang_action_ == HangAction::kTerminateCurrentProcess ||
+          hang_action_ == HangAction::kDumpAndTerminateCurrentProcess) {
+        TerminateCurrentProcess();
       }
     }
   }
@@ -158,4 +174,25 @@
                             audio_thread_status_.load());
 }
 
+void AudioThreadHangMonitor::SetHangActionCallbacksForTesting(
+    base::RepeatingClosure dump_callback,
+    base::RepeatingClosure terminate_process_callback) {
+  dump_callback_ = std::move(dump_callback);
+  terminate_process_callback_ = std::move(terminate_process_callback);
+}
+
+void AudioThreadHangMonitor::DumpWithoutCrashing() {
+  if (!dump_callback_.is_null())
+    dump_callback_.Run();
+  else
+    base::debug::DumpWithoutCrashing();
+}
+
+void AudioThreadHangMonitor::TerminateCurrentProcess() {
+  if (!terminate_process_callback_.is_null())
+    terminate_process_callback_.Run();
+  else
+    base::Process::TerminateCurrentProcessImmediately(1);
+}
+
 }  // namespace media
diff --git a/media/audio/audio_thread_hang_monitor.h b/media/audio/audio_thread_hang_monitor.h
index bcd9d51..363dd69 100644
--- a/media/audio/audio_thread_hang_monitor.h
+++ b/media/audio/audio_thread_hang_monitor.h
@@ -10,9 +10,12 @@
 #include <atomic>
 #include <memory>
 
+#include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/time/time.h"
@@ -48,10 +51,25 @@
     kMaxValue = kRecovered
   };
 
+  enum class HangAction {
+    // Do nothing. (UMA logging is always done.)
+    kDoNothing,
+    // A crash dump will be collected the first time the thread is detected as
+    // hung (note that no actual crashing is involved).
+    kDump,
+    // Terminate the current process with exit code 0.
+    kTerminateCurrentProcess,
+    // Terminate the current process with exit code 1, which yields a crash
+    // dump.
+    kDumpAndTerminateCurrentProcess
+  };
+
   // |monitor_task_runner| may be set explicitly by tests only. Other callers
-  // should use the default.
+  // should use the default. If |hang_deadline| is not provided, or if it's
+  // zero, a default value is used.
   static Ptr Create(
-      bool dump_on_hang,
+      HangAction hang_action,
+      base::Optional<base::TimeDelta> hang_deadline,
       const base::TickClock* clock,
       scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner,
       scoped_refptr<base::SequencedTaskRunner> monitor_task_runner = nullptr);
@@ -62,6 +80,8 @@
   bool IsAudioThreadHung() const;
 
  private:
+  friend class AudioThreadHangMonitorTest;
+
   class SharedAtomicFlag final
       : public base::RefCountedThreadSafe<SharedAtomicFlag> {
    public:
@@ -75,7 +95,8 @@
   };
 
   AudioThreadHangMonitor(
-      bool dump_on_hang,
+      HangAction hang_action,
+      base::Optional<base::TimeDelta> hang_deadline,
       const base::TickClock* clock,
       scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner);
 
@@ -94,6 +115,16 @@
   // LogHistogramThreadStatus logs |thread_status_| to a histogram.
   void LogHistogramThreadStatus();
 
+  // For tests. See below functions.
+  void SetHangActionCallbacksForTesting(
+      base::RepeatingClosure dump_callback,
+      base::RepeatingClosure terminate_process_callback);
+
+  // Thin wrapper functions that either executes the default or runs a callback
+  // set with SetHangActioncallbacksForTesting(), for testing purposes.
+  void DumpWithoutCrashing();
+  void TerminateCurrentProcess();
+
   const base::TickClock* const clock_;
 
   // This flag is set to false on the monitor sequence and then set to true on
@@ -103,9 +134,15 @@
   // |audio_task_runner_| is the task runner of the audio thread.
   const scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
 
-  // If |dump_on_hang_| is set, a crash dump will be collected the first time
-  // the thread is detected as hung (note that no actual crashing is involved).
-  const bool dump_on_hang_;
+  // Which action(s) to take when detected hung thread.
+  const HangAction hang_action_;
+
+  // At which interval to ping and see if the thread is running.
+  const base::TimeDelta ping_interval_;
+
+  // For testing. See DumpWithoutCrashing() and TerminateCurrentProcess().
+  base::RepeatingClosure dump_callback_;
+  base::RepeatingClosure terminate_process_callback_;
 
   std::atomic<ThreadStatus> audio_thread_status_ = {ThreadStatus::kStarted};
 
diff --git a/media/audio/audio_thread_hang_monitor_unittest.cc b/media/audio/audio_thread_hang_monitor_unittest.cc
index c90d4f6..364edb7 100644
--- a/media/audio/audio_thread_hang_monitor_unittest.cc
+++ b/media/audio/audio_thread_hang_monitor_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/location.h"
+#include "base/optional.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -16,6 +17,7 @@
 
 using testing::ElementsAre;
 using testing::Test;
+using HangAction = media::AudioThreadHangMonitor::HangAction;
 
 namespace media {
 
@@ -28,6 +30,9 @@
 constexpr int kRecovered =
     static_cast<int>(AudioThreadHangMonitor::ThreadStatus::kRecovered);
 
+constexpr base::TimeDelta kShortHangDeadline = base::TimeDelta::FromSeconds(5);
+constexpr base::TimeDelta kLongHangDeadline = base::TimeDelta::FromMinutes(30);
+
 }  // namespace
 
 class AudioThreadHangMonitorTest : public Test {
@@ -42,8 +47,8 @@
     // runner since ScopedTaskEnvironment::FastForwardBy only works for the main
     // thread.
     hang_monitor_ = AudioThreadHangMonitor::Create(
-        false, task_env_.GetMockTickClock(), audio_thread_.task_runner(),
-        task_env_.GetMainThreadTaskRunner());
+        HangAction::kDoNothing, base::nullopt, task_env_.GetMockTickClock(),
+        audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
   }
 
   ~AudioThreadHangMonitorTest() override {
@@ -51,6 +56,14 @@
     task_env_.RunUntilIdle();
   }
 
+  void SetHangActionCallbacksForTesting() {
+    hang_monitor_->SetHangActionCallbacksForTesting(
+        base::BindRepeating(&AudioThreadHangMonitorTest::HangActionDump,
+                            base::Unretained(this)),
+        base::BindRepeating(&AudioThreadHangMonitorTest::HangActionTerminate,
+                            base::Unretained(this)));
+  }
+
   void RunUntilIdle() { task_env_.RunUntilIdle(); }
 
   void FlushAudioThread() {
@@ -70,6 +83,9 @@
         base::BindOnce(&base::WaitableEvent::Wait, base::Unretained(&event_)));
   }
 
+  MOCK_METHOD0(HangActionDump, void());
+  MOCK_METHOD0(HangActionTerminate, void());
+
   base::WaitableEvent event_;
   base::test::ScopedTaskEnvironment task_env_;
   base::HistogramTester histograms_;
@@ -109,6 +125,95 @@
               ElementsAre(base::Bucket(kStarted, 1), base::Bucket(kHung, 1)));
 }
 
+TEST_F(AudioThreadHangMonitorTest, DoesNotLogThreadHungWithShortDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, kShortHangDeadline, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(kShortHangDeadline / 2);
+  event_.Signal();
+
+  // Two started events, one for the originally created hang monitor and one for
+  // the new created here.
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, LogsThreadHungWithShortDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, kShortHangDeadline, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(kShortHangDeadline * 2);
+  event_.Signal();
+
+  // Two started events, one for the originally created hang monitor and one for
+  // the new created here.
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, DoesNotLogThreadHungWithLongDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, kLongHangDeadline, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(kLongHangDeadline / 2);
+  event_.Signal();
+
+  // Two started events, one for the originally created hang monitor and one for
+  // the new created here.
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, LogsThreadHungWithLongDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, kLongHangDeadline, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(kLongHangDeadline * 2);
+  event_.Signal();
+
+  // Two started events, one for the originally created hang monitor and one for
+  // the new created here.
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+// Zero deadline means that the default deadline should be used.
+TEST_F(AudioThreadHangMonitorTest, ZeroDeadlineMeansDefaultDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, base::TimeDelta(), task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  for (int i = 0; i < 10; ++i) {
+    // Flush the audio thread, then advance the clock. The audio thread should
+    // register as "alive" every time.
+    FlushAudioThread();
+    task_env_.FastForwardBy(base::TimeDelta::FromMinutes(1));
+  }
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2)));
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::TimeDelta::FromMinutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
 TEST_F(AudioThreadHangMonitorTest,
        LogsRecoveredWhenAudioThreadIsBlockedThenRecovers) {
   RunUntilIdle();
@@ -129,4 +234,74 @@
                           base::Bucket(kRecovered, 1)));
 }
 
+TEST_F(AudioThreadHangMonitorTest, NoHangActionWhenOk) {
+  SetHangActionCallbacksForTesting();
+  RunUntilIdle();
+
+  for (int i = 0; i < 10; ++i) {
+    // Flush the audio thread, then advance the clock. The audio thread should
+    // register as "alive" every time.
+    FlushAudioThread();
+    task_env_.FastForwardBy(base::TimeDelta::FromMinutes(1));
+  }
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, DumpsWhenAudioThreadIsBlocked) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDump, base::nullopt, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  SetHangActionCallbacksForTesting();
+  RunUntilIdle();
+
+  EXPECT_CALL(*this, HangActionDump).Times(1);
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::TimeDelta::FromMinutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, TerminatesProcessWhenAudioThreadIsBlocked) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kTerminateCurrentProcess, base::nullopt,
+      task_env_.GetMockTickClock(), audio_thread_.task_runner(),
+      task_env_.GetMainThreadTaskRunner());
+  SetHangActionCallbacksForTesting();
+  RunUntilIdle();
+
+  EXPECT_CALL(*this, HangActionTerminate).Times(1);
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::TimeDelta::FromMinutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest,
+       DumpsAndTerminatesProcessWhenAudioThreadIsBlocked) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDumpAndTerminateCurrentProcess, base::nullopt,
+      task_env_.GetMockTickClock(), audio_thread_.task_runner(),
+      task_env_.GetMainThreadTaskRunner());
+  SetHangActionCallbacksForTesting();
+  RunUntilIdle();
+
+  EXPECT_CALL(*this, HangActionDump).Times(1);
+  EXPECT_CALL(*this, HangActionTerminate).Times(1);
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::TimeDelta::FromMinutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
 }  // namespace media
diff --git a/media/audio/audio_thread_impl.cc b/media/audio/audio_thread_impl.cc
index e1bdc11d..c3dfddf 100644
--- a/media/audio/audio_thread_impl.cc
+++ b/media/audio/audio_thread_impl.cc
@@ -4,6 +4,7 @@
 
 #include "media/audio/audio_thread_impl.h"
 
+#include "base/optional.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_tick_clock.h"
 #include "build/build_config.h"
@@ -37,7 +38,8 @@
   // https://crbug.com/946968: The hang monitor possibly causes crashes on
   // Android
   hang_monitor_ = AudioThreadHangMonitor::Create(
-      false, base::DefaultTickClock::GetInstance(), task_runner_);
+      AudioThreadHangMonitor::HangAction::kDoNothing, base::nullopt,
+      base::DefaultTickClock::GetInstance(), task_runner_);
 #endif
 }
 
diff --git a/services/audio/owning_audio_manager_accessor.cc b/services/audio/owning_audio_manager_accessor.cc
index c2998c3..9ecffa35 100644
--- a/services/audio/owning_audio_manager_accessor.cc
+++ b/services/audio/owning_audio_manager_accessor.cc
@@ -9,9 +9,11 @@
 
 #include "base/feature_list.h"
 #include "base/macros.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/threading/thread.h"
 #include "base/time/default_tick_clock.h"
 #include "media/audio/audio_features.h"
@@ -19,10 +21,37 @@
 #include "media/audio/audio_thread.h"
 #include "media/audio/audio_thread_hang_monitor.h"
 
+using HangAction = media::AudioThreadHangMonitor::HangAction;
+
 namespace audio {
 
 namespace {
 
+base::Optional<base::TimeDelta> GetAudioThreadHangDeadline() {
+  if (!base::FeatureList::IsEnabled(
+          features::kAudioServiceOutOfProcessKillAtHang)) {
+    return base::nullopt;
+  }
+  const std::string timeout_string = base::GetFieldTrialParamValueByFeature(
+      features::kAudioServiceOutOfProcessKillAtHang, "timeout_seconds");
+  int timeout_int = 0;
+  if (!base::StringToInt(timeout_string, &timeout_int) || timeout_int == 0)
+    return base::nullopt;
+  return base::TimeDelta::FromSeconds(timeout_int);
+}
+
+HangAction GetAudioThreadHangAction() {
+  const bool dump =
+      base::FeatureList::IsEnabled(features::kDumpOnAudioServiceHang);
+  const bool kill = base::FeatureList::IsEnabled(
+      features::kAudioServiceOutOfProcessKillAtHang);
+  if (dump) {
+    return kill ? HangAction::kDumpAndTerminateCurrentProcess
+                : HangAction::kDump;
+  }
+  return kill ? HangAction::kTerminateCurrentProcess : HangAction::kDoNothing;
+}
+
 // Thread class for hosting owned AudioManager on the main thread of the
 // service, with a separate worker thread (started on-demand) for running things
 // that shouldn't be blocked by main-thread tasks.
@@ -53,7 +82,8 @@
     : task_runner_(base::ThreadTaskRunnerHandle::Get()),
       worker_thread_("AudioWorkerThread"),
       hang_monitor_(media::AudioThreadHangMonitor::Create(
-          base::FeatureList::IsEnabled(features::kDumpOnAudioServiceHang),
+          GetAudioThreadHangAction(),
+          GetAudioThreadHangDeadline(),
           base::DefaultTickClock::GetInstance(),
           task_runner_)) {}