blob: 31cf3bb134ed72854b10527695e568f981b685a0 [file] [log] [blame]
// Copyright 2020 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 "chromeos/lacros/lacros_chrome_service_impl.h"
#include <atomic>
#include <utility>
#include "base/logging.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chromeos/lacros/lacros_chrome_service_delegate.h"
namespace chromeos {
namespace {
// We use a std::atomic here rather than a base::NoDestructor because we want to
// allow instances of LacrosChromeServiceImpl to be destroyed to facilitate
// testing.
std::atomic<LacrosChromeServiceImpl*> g_instance = {nullptr};
} // namespace
// This class that holds all state that is affine to a single, never-blocking
// sequence. The sequence must be never-blocking to avoid deadlocks, see
// https://crbug.com/1103765.
class LacrosChromeServiceNeverBlockingState
: public crosapi::mojom::LacrosChromeService {
public:
LacrosChromeServiceNeverBlockingState(
scoped_refptr<base::SequencedTaskRunner> owner_sequence,
base::WeakPtr<LacrosChromeServiceImpl> owner,
crosapi::mojom::LacrosInitParamsPtr* init_params)
: owner_sequence_(owner_sequence),
owner_(owner),
init_params_(init_params) {
DCHECK(init_params);
DETACH_FROM_SEQUENCE(sequence_checker_);
}
~LacrosChromeServiceNeverBlockingState() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
// crosapi::mojom::LacrosChromeService:
void Init(crosapi::mojom::LacrosInitParamsPtr params) override {
*init_params_ = std::move(params);
initialized_.Signal();
}
void RequestAshChromeServiceReceiver(
RequestAshChromeServiceReceiverCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(hidehiko): Remove non-error logging from here.
LOG(WARNING) << "AshChromeServiceReceiver requested.";
std::move(callback).Run(std::move(pending_ash_chrome_service_receiver_));
}
void NewWindow(NewWindowCallback callback) override {
owner_sequence_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&LacrosChromeServiceImpl::NewWindowAffineSequence,
owner_),
std::move(callback));
}
// Unlike most of other methods of this class, this is called on the
// affined thread. Specifically, it is intended to be called before starting
// the message pumping of the affined thread to pass the initialization
// parameter from ash-chrome needed for the procedure running before the
// message pumping.
void WaitForInit() { initialized_.Wait(); }
// AshChromeService is the interface that lacros-chrome uses to message
// ash-chrome. This method binds the remote, which allows queuing of message
// to ash-chrome. The messages will not go through until
// RequestAshChromeServiceReceiver() is invoked.
void BindAshChromeServiceRemote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_ash_chrome_service_receiver_ =
ash_chrome_service_.BindNewPipeAndPassReceiver();
}
// LacrosChromeService is the interface that ash-chrome uses to message
// lacros-chrome. This handles and routes all incoming messages from
// ash-chrome.
void BindLacrosChromeServiceReceiver(
mojo::PendingReceiver<crosapi::mojom::LacrosChromeService> receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
receiver_.Bind(std::move(receiver));
}
// These methods pass the receiver end of a mojo message pipe to ash-chrome.
// This effectively allows ash-chrome to receive messages sent on these
// message pipes.
void BindMessageCenterReceiver(
mojo::PendingReceiver<crosapi::mojom::MessageCenter> pending_receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ash_chrome_service_->BindMessageCenter(std::move(pending_receiver));
}
void BindSelectFileReceiver(
mojo::PendingReceiver<crosapi::mojom::SelectFile> pending_receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ash_chrome_service_->BindSelectFile(std::move(pending_receiver));
}
void BindHidManagerReceiver(
mojo::PendingReceiver<device::mojom::HidManager> pending_receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ash_chrome_service_->BindHidManager(std::move(pending_receiver));
}
void BindScreenManagerReceiver(
mojo::PendingReceiver<crosapi::mojom::ScreenManager> pending_receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ash_chrome_service_->BindScreenManager(std::move(pending_receiver));
}
void BindKeystoreServiceReceiver(
mojo::PendingReceiver<crosapi::mojom::KeystoreService> pending_receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ash_chrome_service_->BindKeystoreService(std::move(pending_receiver));
}
base::WeakPtr<LacrosChromeServiceNeverBlockingState> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
// Receives and routes messages from ash-chrome.
mojo::Receiver<crosapi::mojom::LacrosChromeService> receiver_{this};
// This remote allows lacros-chrome to send messages to ash-chrome.
mojo::Remote<crosapi::mojom::AshChromeService> ash_chrome_service_;
// This class holds onto the receiver for AshChromeService until ash-chrome
// is ready to bind it.
mojo::PendingReceiver<crosapi::mojom::AshChromeService>
pending_ash_chrome_service_receiver_;
// This allows LacrosChromeServiceNeverBlockingState to route IPC messages
// back to the affine thread on LacrosChromeServiceImpl. |owner_| is affine to
// |owner_sequence_|.
scoped_refptr<base::SequencedTaskRunner> owner_sequence_;
base::WeakPtr<LacrosChromeServiceImpl> owner_;
// Owned by LacrosChromeServiceImpl.
crosapi::mojom::LacrosInitParamsPtr* const init_params_;
// Lock to wait for Init() invocation.
// Because the parameters are needed before starting the affined thread's
// message pumping, it is necessary to use sync primitive here, instead.
base::WaitableEvent initialized_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<LacrosChromeServiceNeverBlockingState> weak_factory_{
this};
};
// static
LacrosChromeServiceImpl* LacrosChromeServiceImpl::Get() {
return g_instance;
}
LacrosChromeServiceImpl::LacrosChromeServiceImpl(
std::unique_ptr<LacrosChromeServiceDelegate> delegate)
: delegate_(std::move(delegate)),
sequenced_state_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {
// The sequence on which this object was constructed, and thus affine to.
scoped_refptr<base::SequencedTaskRunner> affine_sequence =
base::SequencedTaskRunnerHandle::Get();
never_blocking_sequence_ = base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
sequenced_state_ = std::unique_ptr<LacrosChromeServiceNeverBlockingState,
base::OnTaskRunnerDeleter>(
new LacrosChromeServiceNeverBlockingState(
affine_sequence, weak_factory_.GetWeakPtr(), &init_params_),
base::OnTaskRunnerDeleter(never_blocking_sequence_));
weak_sequenced_state_ = sequenced_state_->GetWeakPtr();
never_blocking_sequence_->PostTask(
FROM_HERE,
base::BindOnce(
&LacrosChromeServiceNeverBlockingState::BindAshChromeServiceRemote,
weak_sequenced_state_));
// Bind the remote for SelectFile on the current thread, and then pass the
// receiver to the never_blocking_sequence_.
never_blocking_sequence_->PostTask(
FROM_HERE,
base::BindOnce(
&LacrosChromeServiceNeverBlockingState::BindMessageCenterReceiver,
weak_sequenced_state_,
message_center_remote_.BindNewPipeAndPassReceiver()));
// Bind the remote for SelectFile on the current thread, and then pass the
// receiver to the never_blocking_sequence_.
mojo::PendingReceiver<crosapi::mojom::SelectFile>
select_file_pending_receiver =
select_file_remote_.BindNewPipeAndPassReceiver();
never_blocking_sequence_->PostTask(
FROM_HERE,
base::BindOnce(
&LacrosChromeServiceNeverBlockingState::BindSelectFileReceiver,
weak_sequenced_state_, std::move(select_file_pending_receiver)));
mojo::PendingReceiver<crosapi::mojom::KeystoreService>
keystore_service_pending_receiver =
keystore_service_remote_.BindNewPipeAndPassReceiver();
never_blocking_sequence_->PostTask(
FROM_HERE,
base::BindOnce(
&LacrosChromeServiceNeverBlockingState::BindKeystoreServiceReceiver,
weak_sequenced_state_, std::move(keystore_service_pending_receiver)));
mojo::PendingReceiver<device::mojom::HidManager>
hid_manager_pending_receiver =
hid_manager_remote_.BindNewPipeAndPassReceiver();
never_blocking_sequence_->PostTask(
FROM_HERE,
base::BindOnce(
&LacrosChromeServiceNeverBlockingState::BindHidManagerReceiver,
weak_sequenced_state_, std::move(hid_manager_pending_receiver)));
DCHECK(!g_instance);
g_instance = this;
}
LacrosChromeServiceImpl::~LacrosChromeServiceImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK_EQ(this, g_instance);
g_instance = nullptr;
}
void LacrosChromeServiceImpl::BindReceiver(
mojo::PendingReceiver<crosapi::mojom::LacrosChromeService> receiver) {
never_blocking_sequence_->PostTask(
FROM_HERE, base::BindOnce(&LacrosChromeServiceNeverBlockingState::
BindLacrosChromeServiceReceiver,
weak_sequenced_state_, std::move(receiver)));
sequenced_state_->WaitForInit();
}
void LacrosChromeServiceImpl::BindScreenManagerReceiver(
mojo::PendingReceiver<crosapi::mojom::ScreenManager> pending_receiver) {
never_blocking_sequence_->PostTask(
FROM_HERE,
base::BindOnce(
&LacrosChromeServiceNeverBlockingState::BindScreenManagerReceiver,
weak_sequenced_state_, std::move(pending_receiver)));
}
void LacrosChromeServiceImpl::NewWindowAffineSequence() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
delegate_->NewWindow();
}
} // namespace chromeos