1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152

media / audio / alive_checker.cc [blame]

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/alive_checker.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/task/sequenced_task_runner.h"

namespace media {

AliveChecker::AliveChecker(base::RepeatingClosure dead_callback,
                           base::TimeDelta check_interval,
                           base::TimeDelta timeout,
                           bool stop_at_first_alive_notification,
                           bool pause_check_during_suspend)
    : AliveChecker(std::move(dead_callback),
                   check_interval,
                   timeout,
                   stop_at_first_alive_notification,
                   pause_check_during_suspend,
                   PowerObserverHelperFactoryCallback()) {}

AliveChecker::AliveChecker(
    base::RepeatingClosure dead_callback,
    base::TimeDelta check_interval,
    base::TimeDelta timeout,
    bool stop_at_first_alive_notification,
    PowerObserverHelperFactoryCallback power_observer_helper_factory_callback)
    : AliveChecker(std::move(dead_callback),
                   check_interval,
                   timeout,
                   stop_at_first_alive_notification,
                   true,
                   std::move(power_observer_helper_factory_callback)) {}

// The private constructor called by the above public constructors.
AliveChecker::AliveChecker(
    base::RepeatingClosure dead_callback,
    base::TimeDelta check_interval,
    base::TimeDelta timeout,
    bool stop_at_first_alive_notification,
    bool pause_check_during_suspend,
    PowerObserverHelperFactoryCallback power_observer_helper_factory_callback)
    : check_interval_(check_interval),
      timeout_(timeout),
      task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
      dead_callback_(std::move(dead_callback)),
      stop_at_first_alive_notification_(stop_at_first_alive_notification) {
  DCHECK(!dead_callback_.is_null());
  DCHECK_GT(check_interval_, base::TimeDelta());
  DCHECK_GT(timeout_, check_interval_);

  if (pause_check_during_suspend) {
    // When suspending, we don't need to take any action. When resuming, we
    // reset |last_alive_notification_time_| to avoid false alarms.
    // Unretained is safe since the PowerObserverHelper runs the callback on
    // the task runner the AliveChecker (and consequently the
    // PowerObserverHelper) is destroyed on.
    if (power_observer_helper_factory_callback.is_null()) {
      power_observer_ = std::make_unique<PowerObserverHelper>(
          task_runner_, base::DoNothing(),
          base::BindRepeating(
              &AliveChecker::SetLastAliveNotificationTimeToNowOnTaskRunner,
              base::Unretained(this)));
    } else {
      power_observer_ =
          std::move(power_observer_helper_factory_callback)
              .Run(task_runner_, base::DoNothing(),
                   base::BindRepeating(
                       &AliveChecker::
                           SetLastAliveNotificationTimeToNowOnTaskRunner,
                       base::Unretained(this)));
    }
  } else {
    // If |pause_check_during_suspend| is false, we expect an empty factory
    // callback.
    DCHECK(power_observer_helper_factory_callback.is_null());
  }
}

AliveChecker::~AliveChecker() {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());
}

void AliveChecker::Start() {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());

  SetLastAliveNotificationTimeToNowOnTaskRunner();
  detected_dead_ = false;

  DCHECK(!check_alive_timer_);
  check_alive_timer_ = std::make_unique<base::RepeatingTimer>();
  check_alive_timer_->Start(FROM_HERE, check_interval_, this,
                            &AliveChecker::CheckIfAlive);
  DCHECK(check_alive_timer_->IsRunning());
}

void AliveChecker::Stop() {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());
  check_alive_timer_.reset();
}

bool AliveChecker::DetectedDead() {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());
  return detected_dead_;
}

void AliveChecker::NotifyAlive() {
  if (!task_runner_->RunsTasksInCurrentSequence()) {
    // We don't need high precision for setting |last_alive_notification_time_|
    // so we don't have to care about the delay added with posting the task.
    task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&AliveChecker::NotifyAlive, weak_factory_.GetWeakPtr()));
    return;
  }

  SetLastAliveNotificationTimeToNowOnTaskRunner();
  if (stop_at_first_alive_notification_)
    Stop();
}

void AliveChecker::CheckIfAlive() {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());

  // The reason we check a flag instead of stopping the timer that runs this
  // function at suspend is that it would require knowing what state we're in
  // when resuming and maybe start the timer. Also, we would still need this
  // flag anyway to maybe start the timer at stream creation.
  // TODO(grunell): Suspend/resume notifications are not supported on Linux. We
  // could possibly use wall clock time as a complement to be able to detect
  // time jumps that probably are caused by suspend/resume.
  if (power_observer_ && power_observer_->IsSuspending())
    return;

  if (base::TimeTicks::Now() - last_alive_notification_time_ > timeout_) {
    Stop();
    detected_dead_ = true;
    dead_callback_.Run();
  }
}

void AliveChecker::SetLastAliveNotificationTimeToNowOnTaskRunner() {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());
  last_alive_notification_time_ = base::TimeTicks::Now();
}

}  // namespace media