// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_MEDIA_CLIENT_IMPL_H_
#define CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_MEDIA_CLIENT_IMPL_H_
#include "ash/public/cpp/media_client.h"
#include "ash/public/cpp/media_controller.h"
#include "ash/system/privacy_hub/privacy_hub_notification.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "chrome/browser/ash/camera_mic/vm_camera_mic_manager.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom.h"
#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
#include "ui/base/accelerators/media_keys_listener.h"
using GetSourceInfosResult =
video_capture::mojom::VideoSourceProvider::GetSourceInfosResult;
class MediaClientImpl : public ash::MediaClient,
public ash::VmCameraMicManager::Observer,
public BrowserListObserver,
public MediaCaptureDevicesDispatcher::Observer,
public media::CameraPrivacySwitchObserver,
public media::CameraActiveClientObserver {
public:
MediaClientImpl();
MediaClientImpl(const MediaClientImpl&) = delete;
MediaClientImpl& operator=(const MediaClientImpl&) = delete;
~MediaClientImpl() override;
// Initializes and set as client for ash.
void Init();
// Tests can provide a mock mojo interface for the ash controller.
void InitForTesting(ash::MediaController* controller);
// Returns a pointer to the singleton MediaClient, or nullptr if none exists.
static MediaClientImpl* Get();
// ash::MediaClient:
void HandleMediaNextTrack() override;
void HandleMediaPlayPause() override;
void HandleMediaPlay() override;
void HandleMediaPause() override;
void HandleMediaStop() override;
void HandleMediaPrevTrack() override;
void HandleMediaSeekBackward() override;
void HandleMediaSeekForward() override;
void RequestCaptureState() override;
void SuspendMediaSessions() override;
// MediaCaptureDevicesDispatcher::Observer:
void OnRequestUpdate(int render_process_id,
int render_frame_id,
blink::mojom::MediaStreamType stream_type,
const content::MediaRequestState state) override;
// BrowserListObserver:
void OnBrowserSetLastActive(Browser* browser) override;
// ash::VmCameraMicManager::Observer
void OnVmCameraMicActiveChanged(ash::VmCameraMicManager* manager) override;
// media::CameraPrivacySwitchObserver:
void OnCameraHWPrivacySwitchStateChanged(
const std::string& device_id,
cros::mojom::CameraPrivacySwitchState state) override;
void OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState state) override;
// media::CameraActiveClientObserver:
void OnActiveClientChange(
cros::mojom::CameraClientType type,
bool is_active,
const base::flat_set<std::string>& device_ids) override;
// Enables/disables custom media key handling when |context| is the active
// browser. Media keys will be forwarded to |delegate|.
void EnableCustomMediaKeyHandler(content::BrowserContext* context,
ui::MediaKeysListener::Delegate* delegate);
void DisableCustomMediaKeyHandler(content::BrowserContext* context,
ui::MediaKeysListener::Delegate* delegate);
private:
friend class MediaClientAppUsingCameraTest;
using GetSourceCallback = base::OnceCallback<void(
GetSourceInfosResult,
const std::vector<media::VideoCaptureDeviceInfo>&)>;
// Passes a given callback to the GetSourcesInfos() method of the video source
// provider
void ProcessSourceInfos(GetSourceCallback callback);
// Sets |is_forcing_media_client_key_handling_| to true if
// |GetCurrentMediaKeyDelegate| returns a delegate. This will also mirror the
// value of |is_forcing_media_client_key_handling_| to Ash.
void UpdateForceMediaClientKeyHandling();
// Gets the current media key delegate that we should forward media keys to.
// This will be the delegate that is associated with the current active
// browser. If there is no delegate registered or there is no active browser
// then this will return |nullptr|.
ui::MediaKeysListener::Delegate* GetCurrentMediaKeyDelegate() const;
// Returns the media capture state for the current user at
// |user_index|. (Note that this isn't stable, see implementation comment on
// RequestCaptureState()).
ash::MediaCaptureState GetMediaCaptureStateByIndex(int user_index);
// Handles the media key action for the key with |code|. If there is a
// |GetCurrentMediaKeyDelegate| then the action will be forwarded to the
// delegate. Otherwise, we will forward the action to the extensions API.
void HandleMediaAction(ui::KeyboardCode code);
// Shows a notification informing the user that an app is trying to use the
// camera while the camera hardware privacy switch is turned on. If
// `resurface` is false the notification text will be updated but the
// notification won't be brought to the users attention again.
void ShowCameraOffNotification(const std::string& device_id,
const std::string& device_name,
bool resurface = true);
// Removes the camera notification for device with id `device_id` and returns
// iterator to the next device id in `devices_having_visible_notification_`.
base::flat_set<std::string>::iterator RemoveCameraOffNotificationForDevice(
const std::string& device_id);
void OnGetSourceInfosByCameraHWPrivacySwitchStateChanged(
const std::string& device_id,
cros::mojom::CameraPrivacySwitchState state,
GetSourceInfosResult,
const std::vector<media::VideoCaptureDeviceInfo>& devices);
void OnGetSourceInfosByActiveClientChanged(
const base::flat_set<std::string>& active_device_ids,
GetSourceInfosResult,
const std::vector<media::VideoCaptureDeviceInfo>& devices);
// Returns true if the device (camera) with id `device_id` is being actively
// used by a client.
bool IsDeviceActive(const std::string& device_id);
void OnGetSourceInfosByCameraSWPrivacySwitchStateChanged(
GetSourceInfosResult,
const std::vector<media::VideoCaptureDeviceInfo>& devices);
void OnGetCameraSWPrivacySwitchState(
cros::mojom::CameraPrivacySwitchState state);
raw_ptr<ash::MediaController> media_controller_ = nullptr;
base::flat_map<content::BrowserContext*, ui::MediaKeysListener::Delegate*>
media_key_delegates_;
// If true then ash will always forward media keys to |this| instead of trying
// to handle them first.
bool is_forcing_media_client_key_handling_ = false;
raw_ptr<content::BrowserContext, DanglingUntriaged> active_context_ = nullptr;
ash::MediaCaptureState vm_media_capture_state_ =
ash::MediaCaptureState::kNone;
// The most recent observed camera privacy switch state.
base::flat_map<std::string, cros::mojom::CameraPrivacySwitchState>
device_id_to_camera_privacy_switch_state_;
int active_camera_client_count_ = 0;
// Most recent time the notification that the camera privacy switch is on was
// shown.
base::TimeTicks camera_switch_notification_shown_timestamp_;
mojo::Remote<video_capture::mojom::VideoSourceProvider>
video_source_provider_remote_;
// Points each CameraClientType to a set which contains the id of the devices
// the CameraClientType is currently using.
base::flat_map<cros::mojom::CameraClientType, base::flat_set<std::string>>
devices_used_by_client_;
// Set of IDs of the camera devices having a visible notification in the
// message center.
base::flat_set<std::string> devices_having_visible_notification_;
// Stores the state of the camera software privacy switch state locally.
cros::mojom::CameraPrivacySwitchState camera_sw_privacy_switch_state_ =
cros::mojom::CameraPrivacySwitchState::UNKNOWN;
ash::PrivacyHubNotification notification_;
// Can be used to disable/enable the display of HW switch toasts.
bool hw_switch_toasts_disabled_ = false;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<MediaClientImpl> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_MEDIA_CLIENT_IMPL_H_