// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_CAPTURE_VIDEO_CHROMEOS_CAMERA_HAL_DISPATCHER_IMPL_H_
#define MEDIA_CAPTURE_VIDEO_CHROMEOS_CAMERA_HAL_DISPATCHER_IMPL_H_
#include <memory>
#include <set>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list_threadsafe.h"
#include "base/observer_list_types.h"
#include "base/scoped_observation_traits.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/thread_annotations.h"
#include "base/threading/thread.h"
#include "base/unguessable_token.h"
#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
#include "media/capture/capture_export.h"
#include "media/capture/video/chromeos/mojo_service_manager_observer.h"
#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom.h"
#include "media/capture/video/chromeos/token_manager.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace base {
class SingleThreadTaskRunner;
class WaitableEvent;
} // namespace base
namespace ash {
class CameraEffectsController;
} // namespace ash
namespace media {
class CAPTURE_EXPORT CameraClientObserver {
public:
CameraClientObserver(cros::mojom::CameraClientType type,
base::UnguessableToken auth_token)
: type_(type), auth_token_(auth_token) {}
virtual ~CameraClientObserver();
virtual void OnChannelCreated(
mojo::PendingRemote<cros::mojom::CameraModule> camera_module) = 0;
cros::mojom::CameraClientType GetType() { return type_; }
const base::UnguessableToken GetAuthToken() { return auth_token_; }
bool Authenticate(TokenManager* token_manager);
private:
cros::mojom::CameraClientType type_;
base::UnguessableToken auth_token_;
};
class CAPTURE_EXPORT CameraActiveClientObserver : public base::CheckedObserver {
public:
// |is_new_active_client| is true if the client of |type| becomes active. If
// it is inactive or already active, |is_new_active_client| is false.
// |active_device_ids| are device ids of open cameras associated with the
// client of |type|. If |active_device_ids.empty()|, the client of |type| is
// inactive. Otherwise, it is active.
virtual void OnActiveClientChange(
cros::mojom::CameraClientType type,
bool is_new_active_client,
const base::flat_set<std::string>& active_device_ids) {}
};
class CAPTURE_EXPORT CameraPrivacySwitchObserver
: public base::CheckedObserver {
public:
~CameraPrivacySwitchObserver() override = default;
virtual void OnCameraHWPrivacySwitchStateChanged(
const std::string& device_id,
cros::mojom::CameraPrivacySwitchState state) {}
virtual void OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState state) {}
};
// CameraEffectObserver is the interface to observe the change of camera
// effects, the observers will be notified with the new effect configurations.
class CAPTURE_EXPORT CameraEffectObserver : public base::CheckedObserver {
public:
~CameraEffectObserver() override = default;
// Expose the current camera effects to the observers. If the new_effect is
// null, it indicates that something goes wrong and the set camera effects
// request is rejected before the mojo call.
virtual void OnCameraEffectChanged(
const cros::mojom::EffectsConfigPtr& new_effects) {}
};
// CameraHalDispatcherImpl hosts the CameraHalDispatcher service for Plug-in VM
// host or camera tests to request the camera service.
//
// CameraHalClients connect to the CameraHalDisptcherImpl first, and
// CameraHalDispatcherImpl then creates and dispatches the Mojo channels between
// CameraHalServer and CameraHalClients to establish direct Mojo connections
// between the CameraHalServer and the CameraHalClients.
//
// CameraHalDispatcherImpl owns |proxy_thread_| which is the thread where the
// Mojo channel is bound and all communication through Mojo will happen.
//
// For general documentation about the CameraHalDispatcher Mojo interface see
// the comments in mojo/cros_camera_service.mojom.
class CAPTURE_EXPORT CameraHalDispatcherImpl final
: public cros::mojom::CameraHalDispatcher,
public cros::mojom::CrosCameraServiceObserver,
public chromeos::mojo_service_manager::mojom::ServiceProvider {
public:
using CameraEffectsControllerCallback =
base::RepeatingCallback<void(cros::mojom::EffectsConfigPtr,
cros::mojom::SetEffectResult)>;
using CameraEffectObserverCallback =
base::OnceCallback<void(cros::mojom::EffectsConfigPtr)>;
static CameraHalDispatcherImpl* GetInstance();
CameraHalDispatcherImpl(const CameraHalDispatcherImpl&) = delete;
CameraHalDispatcherImpl& operator=(const CameraHalDispatcherImpl&) = delete;
bool Start();
void AddClientObserver(CameraClientObserver* observer,
base::OnceCallback<void(int32_t)> result_callback);
bool IsStarted();
// Adds an observer that watches for active camera client changes. Observer
// would be immediately notified of the current list of active clients.
void AddActiveClientObserver(CameraActiveClientObserver* observer);
// Removes the observer. A previously-added observer must be removed before
// being destroyed.
void RemoveActiveClientObserver(CameraActiveClientObserver* observer);
// Removes the observers after a call by the subject and returns after
// the observers are removed.
void RemoveClientObservers(
std::vector<CameraClientObserver*> client_observers);
// Adds an observer to get notified when the camera privacy switch state
// changed. Please note that for some devices, the signal will only be
// detectable when the camera is currently on due to hardware limitations.
// Returns the map from device id to the current state of its camera HW
// privacy switch. Before receiving the first HW privacy switch event for a
// device, the map has no entry for that device. Otherwise, the map holds the
// latest reported state for each device.
base::flat_map<std::string, cros::mojom::CameraPrivacySwitchState>
AddCameraPrivacySwitchObserver(CameraPrivacySwitchObserver* observer);
// Removes the observer. A previously-added observer must be removed before
// being destroyed.
void RemoveCameraPrivacySwitchObserver(CameraPrivacySwitchObserver* observer);
// Adds an observer that watches for camera effect configuration change.
void AddCameraEffectObserver(CameraEffectObserver* observer);
// Removes the observer. A previously-added observer must be removed before
// being destroyed.
void RemoveCameraEffectObserver(CameraEffectObserver* observer);
// Gets the current camera software privacy switch state.
void GetCameraSWPrivacySwitchState(
cros::mojom::CrosCameraService::GetCameraSWPrivacySwitchStateCallback
callback);
// Sets the camera software privacy switch state.
void SetCameraSWPrivacySwitchState(
cros::mojom::CameraPrivacySwitchState state);
// Called by vm_permission_service to register the token used for
// pluginvm.
void RegisterPluginVmToken(const base::UnguessableToken& token);
void UnregisterPluginVmToken(const base::UnguessableToken& token);
// Called by CameraHalDispatcher.
void AddCameraIdToDeviceIdEntry(int32_t camera_id,
const std::string& device_id);
// CameraHalDispatcher implementations.
void RegisterClientWithToken(
mojo::PendingRemote<cros::mojom::CameraHalClient> client,
cros::mojom::CameraClientType type,
const base::UnguessableToken& auth_token,
RegisterClientWithTokenCallback callback) final;
// CrosCameraServiceObserver implementations.
void CameraDeviceActivityChange(int32_t camera_id,
bool opened,
cros::mojom::CameraClientType type) final;
void CameraPrivacySwitchStateChange(
cros::mojom::CameraPrivacySwitchState state,
int32_t camera_id) final;
void CameraSWPrivacySwitchStateChange(
cros::mojom::CameraPrivacySwitchState state) final;
void CameraEffectChange(cros::mojom::EffectsConfigPtr config) final;
void AutoFramingStateChange(cros::mojom::CameraAutoFramingState state) final;
base::UnguessableToken GetTokenForTrustedClient(
cros::mojom::CameraClientType type);
void SetAutoFramingState(cros::mojom::CameraAutoFramingState state);
void GetAutoFramingSupported(
cros::mojom::CrosCameraService::GetAutoFramingSupportedCallback callback);
// This function needs to be called first before `SetCameraEffects`.
void SetCameraEffectsControllerCallback(
CameraEffectsControllerCallback camera_effects__controller_callback);
// Sets camera effects through `SetCameraEffectsOnProxyThread`.
// This function should not be called by any client except
// `CameraEffectsController`. Clients should always use
// `CameraEffectsController` instead.
void SetCameraEffects(cros::mojom::EffectsConfigPtr config);
private:
friend struct base::DefaultSingletonTraits<CameraHalDispatcherImpl>;
// Allow the test to construct the class directly.
friend class CameraHalDispatcherImplTest;
class VCDInfoObserverImpl;
CameraHalDispatcherImpl();
~CameraHalDispatcherImpl() final;
bool StartThreads();
void GetCameraSWPrivacySwitchStateOnProxyThread(
cros::mojom::CrosCameraService::GetCameraSWPrivacySwitchStateCallback
callback);
void SetCameraSWPrivacySwitchStateOnProxyThread(
cros::mojom::CameraPrivacySwitchState state);
void AddClientObserverOnProxyThread(
CameraClientObserver* observer,
base::OnceCallback<void(int32_t)> result_callback,
base::WaitableEvent* added);
void EstablishMojoChannel(CameraClientObserver* client_observer);
// Handler for incoming Mojo connection requesting CameraHalDispatcher
// service.
void OnPeerConnected(mojo::ScopedMessagePipeHandle message_pipe);
// Mojo connection error handlers.
void OnCameraServiceConnectionError();
void OnCameraHalClientConnectionError(CameraClientObserver* client);
void OnGetCameraModule(
CameraClientObserver* client_observer,
mojo::PendingRemote<cros::mojom::CameraModule> camera_module);
// Cleans up everything about the observer
void CleanupClientOnProxyThread(CameraClientObserver* client_observer);
void RemoveClientObserversOnProxyThread(
std::vector<CameraClientObserver*> client_observers,
base::WaitableEvent* removed);
void RegisterClientWithTokenOnProxyThread(
mojo::PendingRemote<cros::mojom::CameraHalClient> client,
cros::mojom::CameraClientType type,
base::UnguessableToken token,
RegisterClientWithTokenCallback callback);
void SetAutoFramingStateOnProxyThread(
cros::mojom::CameraAutoFramingState state);
void GetAutoFramingSupportedOnProxyThread(
cros::mojom::CrosCameraService::GetAutoFramingSupportedCallback callback);
// Calls the `camera_hal_server_` to set the camera effects.
void SetCameraEffectsOnProxyThread(cros::mojom::EffectsConfigPtr config,
bool is_from_register);
// Calls the `camera_hal_server_` to set the initial camera effects.
void SetInitialCameraEffectsOnProxyThread(
cros::mojom::EffectsConfigPtr config);
// Called when camera_hal_server_->SetCameraEffect returns.
void OnSetCameraEffectsCompleteOnProxyThread(
cros::mojom::EffectsConfigPtr config,
bool is_from_register,
cros::mojom::SetEffectResult result);
void BindCameraServiceOnProxyThread(
mojo::PendingRemote<cros::mojom::CrosCameraService> camera_service);
void TryConnectToCameraService();
std::string GetDeviceIdFromCameraId(int32_t camera_id);
base::flat_set<std::string> GetDeviceIdsFromCameraIds(
base::flat_set<int32_t> camera_ids);
void StopOnProxyThread();
TokenManager* GetTokenManagerForTesting();
// chromeos::mojo_service_manager::mojom::ServiceProvider overrides.
void Request(
chromeos::mojo_service_manager::mojom::ProcessIdentityPtr identity,
mojo::ScopedMessagePipeHandle receiver) override;
base::Thread proxy_thread_;
scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
mojo::ReceiverSet<cros::mojom::CameraHalDispatcher> receiver_set_;
mojo::Remote<cros::mojom::CrosCameraService> camera_service_;
mojo::Receiver<cros::mojom::CrosCameraServiceObserver>
camera_service_observer_receiver_;
std::set<raw_ptr<CameraClientObserver, SetExperimental>> client_observers_;
TokenManager token_manager_;
base::Lock opened_camera_id_map_lock_;
base::flat_map<cros::mojom::CameraClientType, base::flat_set<int32_t>>
opened_camera_id_map_ GUARDED_BY(opened_camera_id_map_lock_);
scoped_refptr<base::ObserverListThreadSafe<CameraActiveClientObserver>>
active_client_observers_;
// |device_id_to_hw_privacy_switch_state_| can be accessed from the UI thread
// besides |proxy_thread_|.
base::Lock device_id_to_hw_privacy_switch_state_lock_;
base::flat_map<std::string, cros::mojom::CameraPrivacySwitchState>
device_id_to_hw_privacy_switch_state_
GUARDED_BY(device_id_to_hw_privacy_switch_state_lock_);
cros::mojom::CameraAutoFramingState current_auto_framing_state_ =
cros::mojom::CameraAutoFramingState::OFF;
cros::mojom::CrosCameraService::GetAutoFramingSupportedCallback
auto_framing_supported_callback_;
// Records current successfully set camera effects.
// Used inside RegisterServerWithToken.
cros::mojom::EffectsConfigPtr current_effects_;
// The initial state the camera effects should be set to
// when the camera server is registered.
cros::mojom::EffectsConfigPtr initial_effects_;
scoped_refptr<base::ObserverListThreadSafe<CameraPrivacySwitchObserver>>
privacy_switch_observers_;
scoped_refptr<base::ObserverListThreadSafe<CameraEffectObserver>>
camera_effect_observers_;
std::map<CameraClientObserver*, std::unique_ptr<CameraClientObserver>>
mojo_client_observers_;
// A map from camera id to |VideoCaptureDeviceDescriptor.device_id|, which is
// updated in CameraHalDelegate::GetDevicesInfo() and queried in
// GetDeviceIdFromCameraId().
base::Lock camera_id_to_device_id_lock_;
base::flat_map<int32_t, std::string> camera_id_to_device_id_
GUARDED_BY(camera_id_to_device_id_lock_);
// Receiver for mojo service manager service provider.
mojo::Receiver<chromeos::mojo_service_manager::mojom::ServiceProvider>
provider_receiver_{this};
std::unique_ptr<MojoServiceManagerObserver> mojo_service_manager_observer_;
std::unique_ptr<VCDInfoObserverImpl> vcd_info_observer_impl_;
base::WeakPtrFactory<CameraHalDispatcherImpl> weak_factory_{this};
};
} // namespace media
namespace base {
template <>
struct ScopedObservationTraits<media::CameraHalDispatcherImpl,
media::CameraEffectObserver> {
static void AddObserver(media::CameraHalDispatcherImpl* source,
media::CameraEffectObserver* observer) {
source->AddCameraEffectObserver(observer);
}
static void RemoveObserver(media::CameraHalDispatcherImpl* source,
media::CameraEffectObserver* observer) {
source->RemoveCameraEffectObserver(observer);
}
};
} // namespace base
#endif // MEDIA_CAPTURE_VIDEO_CHROMEOS_CAMERA_HAL_DISPATCHER_IMPL_H_