|  | // Copyright 2014 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 "chrome/browser/ui/ash/media_client.h" | 
|  |  | 
|  | #include "ash/public/interfaces/constants.mojom.h" | 
|  | #include "base/location.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/single_thread_task_runner.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "chrome/browser/chromeos/extensions/media_player_api.h" | 
|  | #include "chrome/browser/chromeos/extensions/media_player_event_router.h" | 
|  | #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h" | 
|  | #include "chrome/browser/profiles/profile_manager.h" | 
|  | #include "chrome/browser/ui/ash/chrome_shell_content_state.h" | 
|  | #include "chrome/browser/ui/browser.h" | 
|  | #include "chrome/browser/ui/browser_finder.h" | 
|  | #include "chrome/browser/ui/browser_list.h" | 
|  | #include "chrome/browser/ui/browser_window.h" | 
|  | #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | 
|  | #include "chrome/browser/ui/tabs/tab_strip_model.h" | 
|  | #include "components/user_manager/user_manager.h" | 
|  | #include "content/public/browser/media_session.h" | 
|  | #include "content/public/browser/render_view_host.h" | 
|  | #include "content/public/browser/web_contents.h" | 
|  | #include "content/public/common/service_manager_connection.h" | 
|  | #include "extensions/browser/app_window/app_window.h" | 
|  | #include "extensions/browser/app_window/app_window_registry.h" | 
|  | #include "extensions/browser/process_manager.h" | 
|  | #include "services/service_manager/public/cpp/connector.h" | 
|  |  | 
|  | using ash::mojom::MediaCaptureState; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | MediaClient* g_media_client_ = nullptr; | 
|  |  | 
|  | MediaCaptureState& operator|=(MediaCaptureState& lhs, MediaCaptureState rhs) { | 
|  | lhs = static_cast<MediaCaptureState>(static_cast<int>(lhs) | | 
|  | static_cast<int>(rhs)); | 
|  | return lhs; | 
|  | } | 
|  |  | 
|  | void GetMediaCaptureState(const MediaStreamCaptureIndicator* indicator, | 
|  | content::WebContents* web_contents, | 
|  | MediaCaptureState* media_state_out) { | 
|  | bool video = indicator->IsCapturingVideo(web_contents); | 
|  | bool audio = indicator->IsCapturingAudio(web_contents); | 
|  |  | 
|  | if (video) | 
|  | *media_state_out |= MediaCaptureState::VIDEO; | 
|  | if (audio) | 
|  | *media_state_out |= MediaCaptureState::AUDIO; | 
|  | } | 
|  |  | 
|  | void GetBrowserMediaCaptureState(const MediaStreamCaptureIndicator* indicator, | 
|  | const content::BrowserContext* context, | 
|  | MediaCaptureState* media_state_out) { | 
|  | const BrowserList* desktop_list = BrowserList::GetInstance(); | 
|  |  | 
|  | for (BrowserList::BrowserVector::const_iterator iter = desktop_list->begin(); | 
|  | iter != desktop_list->end(); ++iter) { | 
|  | TabStripModel* tab_strip_model = (*iter)->tab_strip_model(); | 
|  | for (int i = 0; i < tab_strip_model->count(); ++i) { | 
|  | content::WebContents* web_contents = tab_strip_model->GetWebContentsAt(i); | 
|  | if (web_contents->GetBrowserContext() != context) | 
|  | continue; | 
|  | GetMediaCaptureState(indicator, web_contents, media_state_out); | 
|  | if (*media_state_out == MediaCaptureState::AUDIO_VIDEO) | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void GetAppMediaCaptureState(const MediaStreamCaptureIndicator* indicator, | 
|  | content::BrowserContext* context, | 
|  | MediaCaptureState* media_state_out) { | 
|  | const extensions::AppWindowRegistry::AppWindowList& apps = | 
|  | extensions::AppWindowRegistry::Get(context)->app_windows(); | 
|  | for (extensions::AppWindowRegistry::AppWindowList::const_iterator iter = | 
|  | apps.begin(); | 
|  | iter != apps.end(); ++iter) { | 
|  | GetMediaCaptureState(indicator, (*iter)->web_contents(), media_state_out); | 
|  | if (*media_state_out == MediaCaptureState::AUDIO_VIDEO) | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void GetExtensionMediaCaptureState(const MediaStreamCaptureIndicator* indicator, | 
|  | content::BrowserContext* context, | 
|  | MediaCaptureState* media_state_out) { | 
|  | for (content::RenderFrameHost* host : | 
|  | extensions::ProcessManager::Get(context)->GetAllFrames()) { | 
|  | content::WebContents* web_contents = | 
|  | content::WebContents::FromRenderFrameHost(host); | 
|  | // RFH may not have web contents. | 
|  | if (!web_contents) | 
|  | continue; | 
|  | GetMediaCaptureState(indicator, web_contents, media_state_out); | 
|  | if (*media_state_out == MediaCaptureState::AUDIO_VIDEO) | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | MediaCaptureState GetMediaCaptureStateOfAllWebContents( | 
|  | content::BrowserContext* context) { | 
|  | if (!context) | 
|  | return MediaCaptureState::NONE; | 
|  |  | 
|  | scoped_refptr<MediaStreamCaptureIndicator> indicator = | 
|  | MediaCaptureDevicesDispatcher::GetInstance() | 
|  | ->GetMediaStreamCaptureIndicator(); | 
|  |  | 
|  | MediaCaptureState media_state = MediaCaptureState::NONE; | 
|  | // Browser windows | 
|  | GetBrowserMediaCaptureState(indicator.get(), context, &media_state); | 
|  | if (media_state == MediaCaptureState::AUDIO_VIDEO) | 
|  | return MediaCaptureState::AUDIO_VIDEO; | 
|  |  | 
|  | // App windows | 
|  | GetAppMediaCaptureState(indicator.get(), context, &media_state); | 
|  | if (media_state == MediaCaptureState::AUDIO_VIDEO) | 
|  | return MediaCaptureState::AUDIO_VIDEO; | 
|  |  | 
|  | // Extensions | 
|  | GetExtensionMediaCaptureState(indicator.get(), context, &media_state); | 
|  |  | 
|  | return media_state; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | MediaClient::MediaClient() : binding_(this), weak_ptr_factory_(this) { | 
|  | MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this); | 
|  |  | 
|  | service_manager::Connector* connector = | 
|  | content::ServiceManagerConnection::GetForProcess()->GetConnector(); | 
|  | connector->BindInterface(ash::mojom::kServiceName, &media_controller_); | 
|  |  | 
|  | // Register this object as the client interface implementation. | 
|  | ash::mojom::MediaClientAssociatedPtrInfo ptr_info; | 
|  | binding_.Bind(mojo::MakeRequest(&ptr_info)); | 
|  | media_controller_->SetClient(std::move(ptr_info)); | 
|  |  | 
|  | DCHECK(!g_media_client_); | 
|  | g_media_client_ = this; | 
|  | } | 
|  |  | 
|  | MediaClient::~MediaClient() { | 
|  | g_media_client_ = nullptr; | 
|  | MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this); | 
|  | } | 
|  |  | 
|  | // static | 
|  | MediaClient* MediaClient::Get() { | 
|  | return g_media_client_; | 
|  | } | 
|  |  | 
|  | void MediaClient::HandleMediaNextTrack() { | 
|  | extensions::MediaPlayerAPI::Get(ProfileManager::GetActiveUserProfile()) | 
|  | ->media_player_event_router() | 
|  | ->NotifyNextTrack(); | 
|  | } | 
|  |  | 
|  | void MediaClient::HandleMediaPlayPause() { | 
|  | // If there is an active browser, then instead of dispatching this event, | 
|  | // handle active tab's media session play pause. | 
|  | Browser* browser = chrome::FindBrowserWithActiveWindow(); | 
|  | if (browser) { | 
|  | content::MediaSession* media_session = content::MediaSession::Get( | 
|  | browser->tab_strip_model()->GetActiveWebContents()); | 
|  | if (media_session->IsControllable()) { | 
|  | if (media_session->IsActuallyPaused()) | 
|  | media_session->Resume(content::MediaSession::SuspendType::UI); | 
|  | else | 
|  | media_session->Suspend(content::MediaSession::SuspendType::UI); | 
|  | return; | 
|  | } | 
|  | } | 
|  | extensions::MediaPlayerAPI::Get(ProfileManager::GetActiveUserProfile()) | 
|  | ->media_player_event_router() | 
|  | ->NotifyTogglePlayState(); | 
|  | } | 
|  |  | 
|  | void MediaClient::HandleMediaPrevTrack() { | 
|  | extensions::MediaPlayerAPI::Get(ProfileManager::GetActiveUserProfile()) | 
|  | ->media_player_event_router() | 
|  | ->NotifyPrevTrack(); | 
|  | } | 
|  |  | 
|  | void MediaClient::RequestCaptureState() { | 
|  | // TODO(erg): Ash doesn't have stable user indexes. Given the asynchronous | 
|  | // nature of sending messages over mojo pipes, this could theoretically cause | 
|  | // bad data, as one side thinks the vector is [user1, user2] while the other | 
|  | // thinks [user2, user1]. However, since parts of this system are already | 
|  | // asynchronous (see OnRequestUpdate's PostTask()), we're not worrying about | 
|  | // this right now. | 
|  | std::vector<MediaCaptureState> state; | 
|  | for (uint32_t i = 0; | 
|  | i < user_manager::UserManager::Get()->GetLoggedInUsers().size(); ++i) { | 
|  | state.push_back( | 
|  | GetMediaCaptureStateByIndex(static_cast<ash::UserIndex>(i))); | 
|  | } | 
|  |  | 
|  | media_controller_->NotifyCaptureState(std::move(state)); | 
|  | } | 
|  |  | 
|  | void MediaClient::SuspendMediaSessions() { | 
|  | for (auto* web_contents : AllTabContentses()) { | 
|  | content::MediaSession::Get(web_contents) | 
|  | ->Suspend(content::MediaSession::SuspendType::SYSTEM); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MediaClient::OnRequestUpdate(int render_process_id, | 
|  | int render_frame_id, | 
|  | content::MediaStreamType stream_type, | 
|  | const content::MediaRequestState state) { | 
|  | DCHECK(base::MessageLoopForUI::IsCurrent()); | 
|  | // The PostTask is necessary because the state of MediaStreamCaptureIndicator | 
|  | // gets updated after this. | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&MediaClient::RequestCaptureState, | 
|  | weak_ptr_factory_.GetWeakPtr())); | 
|  | } | 
|  |  | 
|  | MediaCaptureState MediaClient::GetMediaCaptureStateByIndex(int user_index) { | 
|  | content::BrowserContext* context = | 
|  | ChromeShellContentState::GetInstance()->GetBrowserContextByIndex( | 
|  | user_index); | 
|  | return GetMediaCaptureStateOfAllWebContents(context); | 
|  | } |