// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/arc/arc_session_runner.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/task_runner.h" namespace arc { namespace { constexpr base::TimeDelta kDefaultRestartDelay = base::TimeDelta::FromSeconds(5); } // namespace ArcSessionRunner::ArcSessionRunner(const ArcSessionFactory& factory) : restart_delay_(kDefaultRestartDelay), factory_(factory), weak_ptr_factory_(this) {} ArcSessionRunner::~ArcSessionRunner() { DCHECK(thread_checker_.CalledOnValidThread()); if (arc_session_) arc_session_->RemoveObserver(this); } void ArcSessionRunner::AddObserver(Observer* observer) { DCHECK(thread_checker_.CalledOnValidThread()); observer_list_.AddObserver(observer); } void ArcSessionRunner::RemoveObserver(Observer* observer) { DCHECK(thread_checker_.CalledOnValidThread()); observer_list_.RemoveObserver(observer); } void ArcSessionRunner::RequestStart() { DCHECK(thread_checker_.CalledOnValidThread()); // Consecutive RequestStart() call. Do nothing. if (run_requested_) return; VLOG(1) << "Session started"; run_requested_ = true; // Here |run_requested_| transitions from false to true. So, |restart_timer_| // must be stopped (either not even started, or has been cancelled in // previous RequestStop() call). DCHECK(!restart_timer_.IsRunning()); if (arc_session_) { // In this case, RequestStop() was called, and before |arc_session_| had // finished stopping, RequestStart() was called. Do nothing in that case, // since when |arc_session_| does actually stop, OnSessionStopped() will // be called, where it should automatically restart. DCHECK_EQ(state_, State::STOPPING); } else { DCHECK_EQ(state_, State::STOPPED); StartArcSession(); } } void ArcSessionRunner::RequestStop() { DCHECK(thread_checker_.CalledOnValidThread()); // Consecutive RequestStop() call. Do nothing. if (!run_requested_) return; VLOG(1) << "Session ended"; run_requested_ = false; if (arc_session_) { // The |state_| could be either STARTING, RUNNING or STOPPING. DCHECK_NE(state_, State::STOPPED); if (state_ == State::STOPPING) { // STOPPING is found in the senario of "RequestStart() -> RequestStop() // -> RequestStart() -> RequestStop()" case. // In the first RequestStop() call, |state_| is set to STOPPING, // and in the second RequestStop() finds it (so this is the second call). // In that case, ArcSession::Stop() is already called, so do nothing. return; } state_ = State::STOPPING; arc_session_->Stop(); } else { DCHECK_EQ(state_, State::STOPPED); // In case restarting is in progress, cancel it. restart_timer_.Stop(); } } void ArcSessionRunner::OnShutdown() { DCHECK(thread_checker_.CalledOnValidThread()); VLOG(1) << "OnShutdown"; run_requested_ = false; restart_timer_.Stop(); if (arc_session_) { DCHECK_NE(state_, State::STOPPED); state_ = State::STOPPING; arc_session_->OnShutdown(); } // ArcSession::OnShutdown() invokes OnSessionStopped() synchronously. // In the observer method, |arc_session_| should be destroyed. DCHECK(!arc_session_); } bool ArcSessionRunner::IsRunning() const { DCHECK(thread_checker_.CalledOnValidThread()); return state_ == State::RUNNING; } bool ArcSessionRunner::IsStopped() const { DCHECK(thread_checker_.CalledOnValidThread()); return state_ == State::STOPPED; } void ArcSessionRunner::SetRestartDelayForTesting( const base::TimeDelta& restart_delay) { DCHECK_EQ(state_, State::STOPPED); DCHECK(!arc_session_); DCHECK(!restart_timer_.IsRunning()); restart_delay_ = restart_delay; } void ArcSessionRunner::StartArcSession() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_EQ(state_, State::STOPPED); DCHECK(!arc_session_); DCHECK(!restart_timer_.IsRunning()); VLOG(1) << "Starting ARC instance"; arc_session_ = factory_.Run(); arc_session_->AddObserver(this); state_ = State::STARTING; arc_session_->Start(); } void ArcSessionRunner::OnSessionReady() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_EQ(state_, State::STARTING); DCHECK(arc_session_); DCHECK(!restart_timer_.IsRunning()); VLOG(0) << "ARC ready"; state_ = State::RUNNING; } void ArcSessionRunner::OnSessionStopped(ArcStopReason stop_reason) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_NE(state_, State::STOPPED); DCHECK(arc_session_); DCHECK(!restart_timer_.IsRunning()); VLOG(0) << "ARC stopped: " << stop_reason; arc_session_->RemoveObserver(this); arc_session_.reset(); // If RUNNING, ARC instance unexpectedly crashed so we need to restart it // automatically. // If STOPPING, at least once RequestStop() is called. If |session_started_| // is true, RequestStart() is following so schedule to restart ARC session. // Otherwise, do nothing. // If STARTING, ARC instance has not been booted properly, so do not // restart it automatically. const bool restarting = (state_ == State::RUNNING || (state_ == State::STOPPING && run_requested_)); if (restarting) { // This check is for RUNNING case. In RUNNING case |run_requested_| should // be always true, because if once RequestStop() is called, the state_ // will be set to STOPPING. DCHECK(run_requested_); // There was a previous invocation and it crashed for some reason. Try // starting ARC instance later again. // Note that even |restart_delay_| is 0 (for testing), it needs to // PostTask, because observer callback may call RequestStart()/Stop(). VLOG(0) << "ARC restarting"; restart_timer_.Start(FROM_HERE, restart_delay_, base::Bind(&ArcSessionRunner::StartArcSession, weak_ptr_factory_.GetWeakPtr())); } state_ = State::STOPPED; for (auto& observer : observer_list_) observer.OnSessionStopped(stop_reason, restarting); } } // namespace arc