blob: c5777d05a30b30c866192841387d8b359bf4a8c9 [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.
#ifndef CHROMEOS_LACROS_LACROS_CHROME_SERVICE_IMPL_H_
#define CHROMEOS_LACROS_LACROS_CHROME_SERVICE_IMPL_H_
#include <memory>
#include "base/component_export.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "chromeos/crosapi/mojom/account_manager.mojom.h"
#include "chromeos/crosapi/mojom/cert_database.mojom.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "chromeos/crosapi/mojom/device_attributes.mojom.h"
#include "chromeos/crosapi/mojom/feedback.mojom.h"
#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
#include "chromeos/crosapi/mojom/message_center.mojom.h"
#include "chromeos/crosapi/mojom/metrics_reporting.mojom.h"
#include "chromeos/crosapi/mojom/prefs.mojom.h"
#include "chromeos/crosapi/mojom/screen_manager.mojom.h"
#include "chromeos/crosapi/mojom/select_file.mojom.h"
#include "chromeos/crosapi/mojom/test_controller.mojom.h"
#include "chromeos/crosapi/mojom/url_handler.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/public/mojom/hid.mojom.h"
class GURL;
namespace chromeos {
class LacrosChromeServiceDelegate;
// Forward declaration for class defined in .cc file that holds most of the
// business logic of this class.
class LacrosChromeServiceNeverBlockingState;
// This class is responsible for receiving and routing mojo messages from
// ash-chrome via the mojo::Receiver |sequenced_state_.receiver_|. This class is
// responsible for sending and routing messages to ash-chrome via the
// mojo::Remote |sequenced_state_.crosapi_|. Messages are sent and
// received on a dedicated, never-blocking sequence to avoid deadlocks.
//
// This object is constructed, destroyed, and mostly used on an "affine
// sequence". For most intents and purposes, this is the main/UI thread.
//
// This class is a singleton but is not thread safe. Each method is individually
// documented with threading requirements.
class COMPONENT_EXPORT(CHROMEOS_LACROS) LacrosChromeServiceImpl {
public:
// The getter is safe to call from all threads.
//
// This method returns nullptr very early or late in the application
// lifecycle. We've chosen to have precise constructor/destructor timings
// rather than rely on a lazy initializer and no destructor to allow for
// more precise testing.
//
// If this is accessed on a thread other than the affine sequence, the caller
// must invalidate or destroy the pointer before shutdown. Attempting to use
// this pointer during shutdown can result in UaF.
static LacrosChromeServiceImpl* Get();
// This class is expected to be constructed and destroyed on the same
// sequence.
explicit LacrosChromeServiceImpl(
std::unique_ptr<LacrosChromeServiceDelegate> delegate);
~LacrosChromeServiceImpl();
// This can be called on any thread. This call allows LacrosChromeServiceImpl
// to start receiving messages from ash-chrome.
void BindReceiver(
mojo::PendingReceiver<crosapi::mojom::BrowserService> receiver);
// Called during tests on affine sequence to disable all crosapi
// functionality.
// TODO(https://crbug.com/1131722): Ideally we could stub this out or make
// this functional for tests without modifying production code
static void DisableCrosapiForTests();
// --------------------------------------------------------------------------
// mojo::Remote is sequence affine. The following methods are convenient
// helpers that expose pre-established Remotes that can only be used from the
// affine sequence (main thread).
// --------------------------------------------------------------------------
// message_center_remote() can only be used if this method returns true.
bool IsMessageCenterAvailable() const;
// This must be called on the affine sequence.
mojo::Remote<crosapi::mojom::MessageCenter>& message_center_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsMessageCenterAvailable());
return message_center_remote_;
}
// select_file_remote() can only be used if this method returns true.
bool IsSelectFileAvailable() const;
// This must be called on the affine sequence. It exposes a remote that can
// be used to show a select-file dialog.
mojo::Remote<crosapi::mojom::SelectFile>& select_file_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsSelectFileAvailable());
return select_file_remote_;
}
// keystore_service_remote() can only be used if this method returns true.
bool IsKeystoreServiceAvailable() const;
// This must be called on the affine sequence. It exposes a remote that can
// be used to query the system keystores.
mojo::Remote<crosapi::mojom::KeystoreService>& keystore_service_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsKeystoreServiceAvailable());
return keystore_service_remote_;
}
// hid_manager_remote() can only be used if this method returns true.
bool IsHidManagerAvailable() const;
// This must be called on the affine sequence. It exposes a remote that can
// be used to support HID devices.
mojo::Remote<device::mojom::HidManager>& hid_manager_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsHidManagerAvailable());
return hid_manager_remote_;
}
// feedback_remote() can only be used when this method returns true;
bool IsFeedbackAvailable() const;
// This must be called on the affine sequence.
mojo::Remote<crosapi::mojom::Feedback>& feedback_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsFeedbackAvailable());
return feedback_remote_;
}
// media_session_audio_focus_remote() can only be used when this method
// returns true;
bool IsMediaSessionAudioFocusAvailable() const;
// This must be called on the affine sequence.
void BindAudioFocusManager(
mojo::PendingReceiver<media_session::mojom::AudioFocusManager> remote);
// media_session_audio_focus_debug_remote() can only be used when this method
// returns true;
bool IsMediaSessionAudioFocusDebugAvailable() const;
// This must be called on the affine sequence.
void BindAudioFocusManagerDebug(
mojo::PendingReceiver<media_session::mojom::AudioFocusManagerDebug>
remote);
// media_session_controller_remote() can only be used when this method returns
// true;
bool IsMediaSessionControllerAvailable() const;
// This must be called on the affine sequence.
void BindMediaControllerManager(
mojo::PendingReceiver<media_session::mojom::MediaControllerManager>
remote);
// Whether the MetricsReporting API is available.
bool IsMetricsReportingAvailable() const;
// Binds a receiver for the MetricsReporting API. May be called on any thread.
void BindMetricsReporting(
mojo::PendingReceiver<crosapi::mojom::MetricsReporting> receiver);
// cert_database_remote() can only be used when this method returns true;
bool IsCertDbAvailable() const;
// This must be called on the affine sequence.
mojo::Remote<crosapi::mojom::CertDatabase>& cert_database_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsCertDbAvailable());
return cert_database_remote_;
}
// Whether the DeviceAttributes API is available.
bool IsDeviceAttributesAvailable() const;
// This must be called on the affine sequence. It exposes a remote that can
// be used to interface with DeviceAttributes.
mojo::Remote<crosapi::mojom::DeviceAttributes>& device_attributes_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsDeviceAttributesAvailable());
return device_attributes_remote_;
}
// file_manager_remote() can only be used if this method returns true.
bool IsFileManagerAvailable() const;
// Must be called on the affine sequence.
mojo::Remote<crosapi::mojom::FileManager>& file_manager_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsFileManagerAvailable());
return file_manager_remote_;
}
// test_controller_remote() can only be used if this method returns true.
bool IsTestControllerAvailable() const;
// Must be called on the affine sequence.
mojo::Remote<crosapi::mojom::TestController>& test_controller_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsTestControllerAvailable());
return test_controller_remote_;
}
// clipboard_remote() can only be used if this method returns true.
bool IsClipboardAvailable() const;
// This must be called on the affine sequence. It exposes a remote that can
// be used to interface with the clipboard
mojo::Remote<crosapi::mojom::Clipboard>& clipboard_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsClipboardAvailable());
return clipboard_remote_;
}
// Whether the Prefs API is available.
bool IsPrefsAvailable() const;
// This must be called on the affine sequence. It exposes a remote that can
// be used to interface with Prefs.
mojo::Remote<crosapi::mojom::Prefs>& prefs_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsPrefsAvailable());
return prefs_remote_;
}
// Whether the UrlHandler API is available.
bool IsUrlHandlerAvailable() const;
// This must be called on the affine sequence. It exposes a remote that can
// be used to interface with UrlHandler.
mojo::Remote<crosapi::mojom::UrlHandler>& url_handler_remote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
DCHECK(IsUrlHandlerAvailable());
return url_handler_remote_;
}
// --------------------------------------------------------------------------
// Some clients will want to use mojo::Remotes on arbitrary sequences (e.g.
// background threads). The following methods allow the client to construct a
// mojo::Remote bound to an arbitrary sequence, and pass the other endpoint of
// the Remote (mojo::PendingReceiver) to ash to set up the interface.
// --------------------------------------------------------------------------
// BindScreenManagerReceiver() can only be used if this method returns true.
bool IsScreenManagerAvailable() const;
// This may be called on any thread.
void BindScreenManagerReceiver(
mojo::PendingReceiver<crosapi::mojom::ScreenManager> pending_receiver);
// BindAccountManagerReceiver() can only be used if this method returns true.
bool IsAccountManagerAvailable() const;
// This may be called on any thread.
void BindAccountManagerReceiver(
mojo::PendingReceiver<crosapi::mojom::AccountManager> pending_receiver);
// OnLacrosStartup method of Crosapi can only be called if this method
// returns true.
bool IsOnBrowserStartupAvailable() const;
// Returns BrowserInitParams which is passed from ash-chrome. On launching
// lacros-chrome from ash-chrome, ash-chrome creates a memory backed file
// serializes the BrowserInitParams to it, and the forked/executed
// lacros-chrome process inherits the file descriptor. The data is read
// in the constructor so is available from the beginning.
// Note that, in older versions, ash-chrome passes the data via
// LacrosChromeService::Init() mojo call to lacros-chrome. That case is still
// handled for backward compatibility, and planned to be removed in the
// future (crbug.com/1156033). Though, until the removal, it is recommended
// to consider both cases, specifically, at least not to cause a crash.
const crosapi::mojom::BrowserInitParams* init_params() const {
return init_params_.get();
}
// Returns the version for an ash interface with a given UUID. Returns -1 if
// the interface is not found. This is a synchronous version of
// mojo::Remote::QueryVersion. It relies on Ash M88. Features that need to
// work on M87 or older should not use this.
int GetInterfaceVersion(base::Token interface_uuid) const;
// Sets `init_params_` to the provided value.
// Useful for tests that cannot setup a full Lacros test environment with a
// working Mojo connection to Ash.
void SetInitParamsForTests(crosapi::mojom::BrowserInitParamsPtr init_params);
private:
// LacrosChromeServiceNeverBlockingState is an implementation detail of this
// class.
friend class LacrosChromeServiceNeverBlockingState;
// Creates a new window on the affine sequence.
void NewWindowAffineSequence();
using GetFeedbackDataCallback = base::OnceCallback<void(base::Value)>;
// Gets feedback data on the affine sequence.
void GetFeedbackDataAffineSequence(GetFeedbackDataCallback callback);
using GetHistogramsCallback = base::OnceCallback<void(const std::string&)>;
// Gets histograms on the affine sequence.
void GetHistogramsAffineSequence(GetHistogramsCallback callback);
using GetActiveTabUrlCallback =
base::OnceCallback<void(const base::Optional<GURL>&)>;
// Gets Url of the active tab on the affine sequence.
void GetActiveTabUrlAffineSequence(GetActiveTabUrlCallback callback);
// Returns ash's version of the Crosapi mojo interface version. This
// determines which interface methods are available. This is safe to call from
// any sequence. This can only be called after BindReceiver().
base::Optional<uint32_t> CrosapiVersion() const;
// Delegate instance to inject Chrome dependent code. Must only be used on the
// affine sequence.
std::unique_ptr<LacrosChromeServiceDelegate> delegate_;
// Parameters passed from ash-chrome.
crosapi::mojom::BrowserInitParamsPtr init_params_;
// These members are affine to the affine sequence. They are initialized in
// the constructor and are immediately available for use.
mojo::Remote<crosapi::mojom::MessageCenter> message_center_remote_;
mojo::Remote<crosapi::mojom::SelectFile> select_file_remote_;
mojo::Remote<device::mojom::HidManager> hid_manager_remote_;
mojo::Remote<crosapi::mojom::Feedback> feedback_remote_;
mojo::Remote<crosapi::mojom::CertDatabase> cert_database_remote_;
mojo::Remote<crosapi::mojom::DeviceAttributes> device_attributes_remote_;
mojo::Remote<crosapi::mojom::KeystoreService> keystore_service_remote_;
mojo::Remote<crosapi::mojom::FileManager> file_manager_remote_;
mojo::Remote<crosapi::mojom::TestController> test_controller_remote_;
mojo::Remote<crosapi::mojom::Clipboard> clipboard_remote_;
mojo::Remote<crosapi::mojom::Prefs> prefs_remote_;
mojo::Remote<crosapi::mojom::UrlHandler> url_handler_remote_;
// This member is instantiated on the affine sequence alongside the
// constructor. All subsequent invocations of this member, including
// destruction, happen on the |never_blocking_sequence_|.
std::unique_ptr<LacrosChromeServiceNeverBlockingState,
base::OnTaskRunnerDeleter>
sequenced_state_;
// This member is instantiated on the affine sequence, but only ever
// dereferenced on the |never_blocking_sequence_|.
base::WeakPtr<LacrosChromeServiceNeverBlockingState> weak_sequenced_state_;
// A sequence that is guaranteed to never block.
scoped_refptr<base::SequencedTaskRunner> never_blocking_sequence_;
// Set to true after BindReceiver() is called.
bool did_bind_receiver_ = false;
// Checks that the method is called on the affine sequence.
SEQUENCE_CHECKER(affine_sequence_checker_);
base::WeakPtrFactory<LacrosChromeServiceImpl> weak_factory_{this};
};
} // namespace chromeos
#endif // CHROMEOS_LACROS_LACROS_CHROME_SERVICE_IMPL_H_