// 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_SERVICE_H_ #define CHROMEOS_LACROS_LACROS_SERVICE_H_ #include #include #include #include #include #include "base/check.h" #include "base/component_export.h" #include "base/containers/contains.h" #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/observer_list_threadsafe.h" #include "base/sequence_checker.h" #include "base/task/sequenced_task_runner.h" #include "base/token.h" #include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h" #include "chromeos/crosapi/mojom/account_manager.mojom.h" #include "chromeos/crosapi/mojom/crosapi.mojom.h" #include "chromeos/crosapi/mojom/device_attributes.mojom.h" #include "chromeos/crosapi/mojom/structured_metrics_service.mojom.h" #include "chromeos/crosapi/mojom/video_capture.mojom.h" #include "chromeos/lacros/lacros_service_never_blocking_state.h" #include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h" #include "mojo/public/cpp/bindings/generic_pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace media { namespace stable::mojom { class StableVideoDecoderFactory; } // namespace stable::mojom } // namespace media namespace chromeos { class NativeThemeCache; class SystemIdleCache; // Forward declaration for class defined in .cc file that holds most of the // business logic of this class. class LacrosServiceNeverBlockingState; // 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) LacrosService { public: using ComponentPolicyMap = base::flat_map; class Observer { public: // Called when the new policy data is received from Ash. virtual void OnPolicyUpdated( const std::vector& policy_fetch_response) {} // Called when policy fetch attempt is made in Ash. virtual void OnPolicyFetchAttempt() {} // Called when the new component policy is received from Ash. virtual void OnComponentPolicyUpdated(const ComponentPolicyMap& policy) {} protected: virtual ~Observer() = default; }; // 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 LacrosService* Get(); // This class is expected to be constructed and destroyed on the same // sequence. LacrosService(); LacrosService(const LacrosService&) = delete; LacrosService& operator=(const LacrosService&) = delete; ~LacrosService(); // This can be called on any thread. This call allows LacrosService // to start receiving messages from ash-chrome. // |browser_version| is the version of lacros-chrome displayed to user in // feedback report, etc. // It includes both browser version and channel in the format of: // {browser version} {channel} // For example, "87.0.0.1 dev", "86.0.4240.38 beta". void BindReceiver(const std::string& browser_version); // Each of these functions guards usage of access to the corresponding remote. // Keep these in alphabetical order. // Most use-cases of these methods can be replaced by IsAvailable(). See // crosapi::mojom::Clipboard for an example. bool IsAccountManagerAvailable() const; bool IsBrowserCdmFactoryAvailable() const; bool IsMediaSessionAudioFocusAvailable() const; bool IsMediaSessionAudioFocusDebugAvailable() const; bool IsMediaSessionControllerAvailable() const; bool IsMetricsReportingAvailable() const; bool IsSensorHalClientAvailable() const; bool IsStableVideoDecoderFactoryAvailable() const; // Methods to add/remove observer. Safe to call from any thread. void AddObserver(Observer* obs); void RemoveObserver(Observer* obs); // Notifies that the device account policy is updated with the input data // to observers. The data comes as serialized blob of PolicyFetchResponse // object. // This must be called on the affined sequence. void NotifyPolicyUpdated(const std::vector& policy); // Notifies that an attempt to update the device account policy has been made. void NotifyPolicyFetchAttempt(); // Notifies that the device account component policy is updated with the // input data. Must be called on the affined sequence. void NotifyComponentPolicyUpdated(ComponentPolicyMap policy); // Returns whether this interface uses the automatic registration system to be // available for immediate use at startup. Any crosapi interface can be // registered by using ConstructRemote. template bool IsRegistered() const { return base::Contains(interfaces_, CrosapiInterface::Uuid_); } // Guards usage to the corresponding crosapi interface. Can only be used with // automatically registered interfaces. See IsRegistered(). template bool IsAvailable() const { DCHECK(IsRegistered()); return interfaces_.find(CrosapiInterface::Uuid_)->second->IsAvailable(); } // Returns the automatically registered remote for a given crosapi interface. // Can only be used with automatically registered features that are also // available. This method can only be called from the affine sequence (main // thread). The returned remote can only be used on the affine sequence (main // thread). template mojo::Remote& GetRemote() { DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_); DCHECK(IsAvailable()); return interfaces_.find(CrosapiInterface::Uuid_) ->second->template Get(); } // -------------------------------------------------------------------------- // 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. For // other interfaces, such as media::stable::mojom::StableVideoDecoderFactory, // the main reason to use a Bind*() method instead of GetRemote() is not the // threading model, but the fact that the browser may want to maintain // multiple independent mojo::Remotes, and ash-chrome can use this behavior as // useful information (for example, to start one ash-chrome utility video // decoder process per lacros-chrome renderer process in order to host the // implementation of a media::stable::mojom::StableVideoDecoderFactory). // -------------------------------------------------------------------------- // This may be called on any thread. void BindAccountManagerReceiver( mojo::PendingReceiver pending_receiver); // This may be called on any thread. void BindAudioFocusManager( mojo::PendingReceiver remote); // This may be called on any thread. void BindAudioFocusManagerDebug( mojo::PendingReceiver remote); // This may be called on any thread. void BindBrowserCdmFactory(mojo::GenericPendingReceiver receiver); // This may be called on any thread. void BindGeolocationService( mojo::PendingReceiver pending_receiver); // This may be called on any thread. void BindMachineLearningService( mojo::PendingReceiver< chromeos::machine_learning::mojom::MachineLearningService> receiver); // This may be called on any thread. void BindMediaControllerManager( mojo::PendingReceiver remote); // This may be called on any thread. void BindMetricsReporting( mojo::PendingReceiver receiver); // This may be called on any thread. void BindRemoteAppsLacrosBridge( mojo::PendingReceiver< chromeos::remote_apps::mojom::RemoteAppsLacrosBridge> receiver); // This may be called on any thread. void BindSensorHalClient( mojo::PendingRemote remote); // OnLacrosStartup method of Crosapi can only be called if this method // returns true. bool IsOnBrowserStartupAvailable() const; // Binds video capture host. void BindVideoCaptureDeviceFactory( mojo::PendingReceiver pending_receiver); // This may be called on any thread. void BindStableVideoDecoderFactory( mojo::PendingReceiver receiver); // BindVideoCaptureDeviceFactory() can only be used if this method returns // true. bool IsVideoCaptureDeviceFactoryAvailable() const; // Returns SystemIdleCache, which uses IdleInfoObserver to observe idle info // changes and caches the results. Requires IsIdleServiceAvailable() for full // function, and is robust against unavailability. SystemIdleCache* system_idle_cache() { return system_idle_cache_.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; using Crosapi = crosapi::mojom::Crosapi; // This function binds a pending receiver or remote by posting the // corresponding bind task to the |never_blocking_sequence_|. // This method is public because not all clients can use the syntax sugar of // ConstructRemote(), which relies on the assumption that each crosapi // interface only has a single associated Bind* method. template void BindPendingReceiverOrRemote( PendingReceiverOrRemote pending_receiver_or_remote) { never_blocking_sequence_->PostTask( FROM_HERE, base::BindOnce( &LacrosServiceNeverBlockingState::BindCrosapiFeatureReceiver< PendingReceiverOrRemote, bind_func>, weak_sequenced_state_, std::move(pending_receiver_or_remote))); } private: // This class is a wrapper around a crosapi remote, e.g. // mojo::Remote. This base class uses type erasure // to allow us to store all instances in a single container. class InterfaceEntryBase { public: virtual ~InterfaceEntryBase(); // Returns the remote that is being wrapped. template mojo::Remote& Get() { return *reinterpret_cast*>(GetInternal()); } // Returns whether Ash is sufficiently recent to support the crosapi // protocol that the remote is based on. bool IsAvailable() const { return available_; } // Initialization for the remote and |available_|. virtual void MaybeBind(uint32_t crosapi_version, LacrosService* impl) = 0; protected: InterfaceEntryBase(); InterfaceEntryBase(const InterfaceEntryBase&) = delete; InterfaceEntryBase& operator=(const InterfaceEntryBase&) = delete; // Returns a raw pointer to a mojo::Remote. virtual void* GetInternal() = 0; // See |IsAvailable|. bool available_ = false; }; // LacrosServiceNeverBlockingState is an implementation detail of // this class. friend class LacrosServiceNeverBlockingState; // Forward declare inner class to give it access to private members. template ), uint32_t MethodMinVersion> class InterfaceEntry; // 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(). absl::optional CrosapiVersion() const; // Requests ash-chrome to send idle info updates. void StartSystemIdleCache(); // Requests ash-chrome to send native theme info updates. void StartNativeThemeCache(); // This function initializes a remote for a given CrosapiInterface. // It performs the following operations: // 1) Calls BindNewPipeAndPassReceiver() on the remote. // 2) Calls BindPendingReceiverOrRemote() on the PendingReceiver. template )> void InitializeAndBindRemote(mojo::Remote* remote); // This function constructs a new remote for a crosapi interface and stashes // it in |interfaces_|. This remote will later be bound during BindReceiver(). template ), uint32_t MethodMinVersion> void ConstructRemote(); // BrowserService implementation injected by chrome/. Must only be used on the // affine sequence. // TODO(hidehiko): Remove this. std::unique_ptr browser_service_; // Receiver and cache of system idle info updates. std::unique_ptr system_idle_cache_; // Receiver and cache of native theme info updates. std::unique_ptr native_theme_cache_; // A sequence that is guaranteed to never block. scoped_refptr never_blocking_sequence_; // 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 sequenced_state_; // This member is instantiated on the affine sequence, but only ever // dereferenced on the |never_blocking_sequence_|. base::WeakPtr weak_sequenced_state_; // Set to true after BindReceiver() is called. bool did_bind_receiver_ = false; // The list of observers. scoped_refptr> observer_list_; // Each element of |interfaces_| corresponds to a crosapi interface remote // (e.g. mojo::Remote). The key of the element is // the UUID of the crosapi interface. The value is a wrapper around the // mojo::Remote. Each element can only be used on the affine sequence. Each // element is automatically bound to the corresponding receiver in ash. std::map> interfaces_; // Checks that the method is called on the affine sequence. SEQUENCE_CHECKER(affine_sequence_checker_); base::WeakPtrFactory weak_factory_{this}; }; } // namespace chromeos #endif // CHROMEOS_LACROS_LACROS_SERVICE_H_