Commit 1a9ad3cc authored by Sergey Ulanov's avatar Sergey Ulanov Committed by Commit Bot

[Fuchsia] Redirect AudioCapturer service in cast_runner to the agent

Now CastRunner creates a FilteredServiceDirectory to pass to the web
Context. It then intercepts requests for AudioCapturer and redirects
them to the agent. This is necessary to allow agents to process the
microphone audio stream used by the app.

Bug: 1059836
Change-Id: If46d70f294b43f831856b179bc382b8f48bf3644
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2095398
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Auto-Submit: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarDavid Dorwin <ddorwin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#751243}
parent 2b285d02
...@@ -57,6 +57,8 @@ source_set("cast_runner_core") { ...@@ -57,6 +57,8 @@ source_set("cast_runner_core") {
"cast/api_bindings_client.h", "cast/api_bindings_client.h",
"cast/application_controller_impl.cc", "cast/application_controller_impl.cc",
"cast/application_controller_impl.h", "cast/application_controller_impl.h",
"cast/audio_capturer_redirect.cc",
"cast/audio_capturer_redirect.h",
"cast/cast_component.cc", "cast/cast_component.cc",
"cast/cast_component.h", "cast/cast_component.h",
"cast/cast_runner.cc", "cast/cast_runner.cc",
......
// 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.
#include "fuchsia/runners/cast/audio_capturer_redirect.h"
#include <lib/sys/cpp/component_context.h>
#include <lib/sys/cpp/service_directory.h>
#include "base/fuchsia/default_context.h"
AudioCapturerRedirect::AudioCapturerRedirect(
sys::OutgoingDirectory* outgoing_directory,
CreateCapturerCallback create_capturer_callback)
: binding_(outgoing_directory, this),
create_capturer_callback_(std::move(create_capturer_callback)),
system_audio_(base::fuchsia::ComponentContextForCurrentProcess()
->svc()
->Connect<fuchsia::media::Audio>()) {}
AudioCapturerRedirect::~AudioCapturerRedirect() = default;
void AudioCapturerRedirect::CreateAudioRenderer(
fidl::InterfaceRequest<fuchsia::media::AudioRenderer>
audio_renderer_request) {
system_audio_->CreateAudioRenderer(std::move(audio_renderer_request));
}
void AudioCapturerRedirect::CreateAudioCapturer(
fidl::InterfaceRequest<fuchsia::media::AudioCapturer>
audio_capturer_request,
bool loopback) {
// Loopback capture is not supported.
if (loopback) {
NOTREACHED();
return;
}
create_capturer_callback_.Run(std::move(audio_capturer_request));
}
void AudioCapturerRedirect::SetSystemMute(bool muted) {
system_audio_->SetSystemMute(muted);
}
void AudioCapturerRedirect::SetSystemGain(float gain_db) {
system_audio_->SetSystemGain(gain_db);
}
// 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 FUCHSIA_RUNNERS_CAST_AUDIO_CAPTURER_REDIRECT_H_
#define FUCHSIA_RUNNERS_CAST_AUDIO_CAPTURER_REDIRECT_H_
#include <fuchsia/media/cpp/fidl.h>
#include "base/callback.h"
#include "base/fuchsia/scoped_service_binding.h"
// fuchsia::media::Audio implementation that redirects CreateAudioCapturer()
// calls to a callback.
// TODO(fxb/47249) Remove this once AudioCapturerFactory is defined and
// implemented.
class AudioCapturerRedirect : public fuchsia::media::Audio {
public:
// Callback to be called for CreateAudioRenderer().
using CreateCapturerCallback = base::RepeatingCallback<void(
fidl::InterfaceRequest<fuchsia::media::AudioCapturer> request)>;
// Publishes fuchsia.media.Audio to |outgoing_directory| The specified
// |create_capturer_callback|. will be called every time CreateAudioCapturer()
// is called. All other calls are redirected to /svc/fuchsia.media.Audio.
AudioCapturerRedirect(sys::OutgoingDirectory* outgoing_directory,
CreateCapturerCallback create_capturer_callback);
~AudioCapturerRedirect() final;
private:
// fuchsia::media::Audio implementation.
void CreateAudioRenderer(fidl::InterfaceRequest<fuchsia::media::AudioRenderer>
audio_renderer_request) final;
void CreateAudioCapturer(fidl::InterfaceRequest<fuchsia::media::AudioCapturer>
audio_capturer_request,
bool loopback) final;
void SetSystemMute(bool muted) final;
void SetSystemGain(float gain_db) final;
base::fuchsia::ScopedServiceBinding<fuchsia::media::Audio> binding_;
CreateCapturerCallback create_capturer_callback_;
fuchsia::media::AudioPtr system_audio_;
};
#endif // FUCHSIA_RUNNERS_CAST_AUDIO_CAPTURER_REDIRECT_H_
...@@ -65,6 +65,12 @@ class CastComponent : public WebComponent, ...@@ -65,6 +65,12 @@ class CastComponent : public WebComponent,
on_headless_disconnect_cb_ = std::move(on_headless_disconnect_cb); on_headless_disconnect_cb_ = std::move(on_headless_disconnect_cb);
} }
const chromium::cast::ApplicationConfig& application_config() {
return application_config_;
}
cr_fuchsia::AgentManager* agent_manager() { return agent_manager_.get(); }
private: private:
FRIEND_TEST_ALL_PREFIXES(HeadlessCastRunnerIntegrationTest, Headless); FRIEND_TEST_ALL_PREFIXES(HeadlessCastRunnerIntegrationTest, Headless);
......
...@@ -12,13 +12,42 @@ ...@@ -12,13 +12,42 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/fuchsia/file_utils.h" #include "base/fuchsia/file_utils.h"
#include "base/fuchsia/filtered_service_directory.h"
#include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h" #include "base/logging.h"
#include "fuchsia/base/agent_manager.h" #include "fuchsia/base/agent_manager.h"
#include "fuchsia/runners/cast/audio_capturer_redirect.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace { namespace {
// List of services provided to the WebEngine context.
// All services must be listed in cast_runner.cmx.
static constexpr const char* kServices[] = {
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",
"fuchsia.media.SessionAudioConsumerFactory",
"fuchsia.media.drm.PlayReady",
"fuchsia.media.drm.Widevine",
"fuchsia.mediacodec.CodecFactory",
"fuchsia.memorypressure.Provider",
"fuchsia.net.NameLookup",
"fuchsia.netstack.Netstack",
"fuchsia.posix.socket.Provider",
"fuchsia.process.Launcher",
"fuchsia.sysmem.Allocator",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.input.ImeVisibilityService",
"fuchsia.ui.scenic.Scenic",
"fuchsia.vulkan.loader.Loader",
// Redirected to the agent.
// fuchsia.media.Audio
};
bool AreCastComponentParamsValid( bool AreCastComponentParamsValid(
const CastComponent::CastComponentParams& params) { const CastComponent::CastComponentParams& params) {
return !params.app_config.IsEmpty() && return !params.app_config.IsEmpty() &&
...@@ -65,13 +94,30 @@ fuchsia::web::CreateContextParams BuildCreateContextParamsForIsolatedRunners( ...@@ -65,13 +94,30 @@ fuchsia::web::CreateContextParams BuildCreateContextParamsForIsolatedRunners(
return output; return output;
} }
bool IsPermissionGrantedInAppConfig(
const chromium::cast::ApplicationConfig& app_config,
fuchsia::web::PermissionType permission_type) {
if (app_config.has_permissions()) {
for (auto& permission : app_config.permissions()) {
if (permission.has_type() && permission.type() == permission_type)
return true;
}
}
return false;
}
} // namespace } // namespace
const char CastRunner::kAgentComponentUrl[] =
"fuchsia-pkg://fuchsia.com/cast_agent#meta/cast_agent.cmx";
CastRunner::CastRunner(fuchsia::web::CreateContextParams create_context_params, CastRunner::CastRunner(fuchsia::web::CreateContextParams create_context_params,
sys::OutgoingDirectory* outgoing_directory) sys::OutgoingDirectory* outgoing_directory)
: WebContentRunner(std::move(create_context_params), outgoing_directory), : WebContentRunner(std::move(create_context_params), outgoing_directory),
common_create_context_params_( common_create_context_params_(
BuildCreateContextParamsForIsolatedRunners(create_params_)) {} BuildCreateContextParamsForIsolatedRunners(create_params_)) {
InitializeServiceDirectory();
}
CastRunner::CastRunner(OnDestructionCallback on_destruction_callback, CastRunner::CastRunner(OnDestructionCallback on_destruction_callback,
fuchsia::web::ContextPtr context, fuchsia::web::ContextPtr context,
...@@ -151,16 +197,15 @@ void CastRunner::StartComponent( ...@@ -151,16 +197,15 @@ void CastRunner::StartComponent(
void CastRunner::DestroyComponent(WebComponent* component) { void CastRunner::DestroyComponent(WebComponent* component) {
WebContentRunner::DestroyComponent(component); WebContentRunner::DestroyComponent(component);
if (component == audio_capturer_component_)
audio_capturer_component_ = nullptr;
if (on_destruction_callback_) { if (on_destruction_callback_) {
// |this| may be deleted and should not be used after this line. // |this| may be deleted and should not be used after this line.
std::move(on_destruction_callback_).Run(this); std::move(on_destruction_callback_).Run(this);
return;
} }
} }
const char CastRunner::kAgentComponentUrl[] =
"fuchsia-pkg://fuchsia.com/cast_agent#meta/cast_agent.cmx";
void CastRunner::GetConfigCallback( void CastRunner::GetConfigCallback(
CastComponent::CastComponentParams* pending_component, CastComponent::CastComponentParams* pending_component,
chromium::cast::ApplicationConfig app_config) { chromium::cast::ApplicationConfig app_config) {
...@@ -227,6 +272,26 @@ void CastRunner::GetConfigCallback( ...@@ -227,6 +272,26 @@ void CastRunner::GetConfigCallback(
}); });
} }
void CastRunner::InitializeServiceDirectory() {
service_directory_ =
std::make_unique<base::fuchsia::FilteredServiceDirectory>(
base::fuchsia::ComponentContextForCurrentProcess()->svc().get());
for (auto* name : kServices) {
service_directory_->AddService(name);
}
// Create AudioCapturerRedirect to intercept CreateAudioCapturer() requests.
audio_capturer_redirect_ = std::make_unique<AudioCapturerRedirect>(
service_directory_->outgoing_directory(),
base::BindRepeating(&CastRunner::CreateAudioCapturer,
base::Unretained(this)));
fidl::InterfaceHandle<fuchsia::io::Directory> client_handle;
service_directory_->ConnectClient(client_handle.NewRequest());
create_params_.set_service_directory(std::move(client_handle));
}
void CastRunner::MaybeStartComponent( void CastRunner::MaybeStartComponent(
CastComponent::CastComponentParams* pending_component_params) { CastComponent::CastComponentParams* pending_component_params) {
if (!AreCastComponentParamsValid(*pending_component_params)) if (!AreCastComponentParamsValid(*pending_component_params))
...@@ -264,6 +329,13 @@ void CastRunner::CreateAndRegisterCastComponent( ...@@ -264,6 +329,13 @@ void CastRunner::CreateAndRegisterCastComponent(
cast_component->StartComponent(); cast_component->StartComponent();
cast_component->LoadUrl(std::move(app_url), cast_component->LoadUrl(std::move(app_url),
std::vector<fuchsia::net::http::Header>()); std::vector<fuchsia::net::http::Header>());
if (IsPermissionGrantedInAppConfig(
cast_component->application_config(),
fuchsia::web::PermissionType::MICROPHONE)) {
audio_capturer_component_ = cast_component.get();
}
RegisterComponent(std::move(cast_component)); RegisterComponent(std::move(cast_component));
} }
...@@ -275,14 +347,17 @@ CastRunner* CastRunner::CreateChildRunnerForIsolatedComponent( ...@@ -275,14 +347,17 @@ CastRunner* CastRunner::CreateChildRunnerForIsolatedComponent(
fuchsia::web::CreateContextParams isolated_context_params; fuchsia::web::CreateContextParams isolated_context_params;
zx_status_t status = zx_status_t status =
common_create_context_params_.Clone(&isolated_context_params); common_create_context_params_.Clone(&isolated_context_params);
isolated_context_params.set_service_directory(base::fuchsia::OpenDirectory(
base::FilePath(base::fuchsia::kServiceDirectoryPath)));
CHECK(isolated_context_params.service_directory());
if (status != ZX_OK) { if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "clone"; ZX_LOG(ERROR, status) << "clone";
return nullptr; return nullptr;
} }
// Service redirection is not necessary for isolated context. Pass default
// /svc as is, without overriding any services.
isolated_context_params.set_service_directory(base::fuchsia::OpenDirectory(
base::FilePath(base::fuchsia::kServiceDirectoryPath)));
DCHECK(isolated_context_params.service_directory());
isolated_context_params.set_content_directories( isolated_context_params.set_content_directories(
std::move(*params->app_config std::move(*params->app_config
.mutable_content_directories_for_isolated_application())); .mutable_content_directories_for_isolated_application()));
...@@ -314,3 +389,15 @@ void CastRunner::OnChildRunnerDestroyed(CastRunner* runner) { ...@@ -314,3 +389,15 @@ void CastRunner::OnChildRunnerDestroyed(CastRunner* runner) {
size_t CastRunner::GetChildCastRunnerCountForTest() { size_t CastRunner::GetChildCastRunnerCountForTest() {
return isolated_runners_.size(); return isolated_runners_.size();
} }
void CastRunner::CreateAudioCapturer(
fidl::InterfaceRequest<fuchsia::media::AudioCapturer> request) {
if (!audio_capturer_component_)
return;
auto audio =
audio_capturer_component_->agent_manager()
->ConnectToAgentService<fuchsia::media::Audio>(
audio_capturer_component_->application_config().agent_url());
audio->CreateAudioCapturer(std::move(request), /*loopback=*/false);
}
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
"features": [ "features": [
"config-data" "config-data"
], ],
"$comment": "Not all services are passed to WebEngine, see cast_runner.cc",
"services": [ "services": [
"chromium.cast.ApplicationConfigManager", "chromium.cast.ApplicationConfigManager",
"fuchsia.accessibility.semantics.SemanticsManager", "fuchsia.accessibility.semantics.SemanticsManager",
......
...@@ -20,6 +20,14 @@ ...@@ -20,6 +20,14 @@
#include "fuchsia/runners/cast/cast_component.h" #include "fuchsia/runners/cast/cast_component.h"
#include "fuchsia/runners/common/web_content_runner.h" #include "fuchsia/runners/common/web_content_runner.h"
namespace base {
namespace fuchsia {
class FilteredServiceDirectory;
} // namespace fuchsia
} // namespace base
class AudioCapturerRedirect;
// sys::Runner which instantiates Cast activities specified via cast/casts URIs. // sys::Runner which instantiates Cast activities specified via cast/casts URIs.
class CastRunner : public WebContentRunner { class CastRunner : public WebContentRunner {
public: public:
...@@ -60,6 +68,10 @@ class CastRunner : public WebContentRunner { ...@@ -60,6 +68,10 @@ class CastRunner : public WebContentRunner {
fuchsia::web::ContextPtr context, fuchsia::web::ContextPtr context,
bool is_headless); bool is_headless);
// Initializes the service directory that's passed to the web context. Must be
// called during initialization, before the context is created.
void InitializeServiceDirectory();
// Starts a component once all configuration data is available. // Starts a component once all configuration data is available.
void MaybeStartComponent( void MaybeStartComponent(
CastComponent::CastComponentParams* pending_component_params); CastComponent::CastComponentParams* pending_component_params);
...@@ -81,6 +93,10 @@ class CastRunner : public WebContentRunner { ...@@ -81,6 +93,10 @@ class CastRunner : public WebContentRunner {
CastRunner* CreateChildRunnerForIsolatedComponent( CastRunner* CreateChildRunnerForIsolatedComponent(
CastComponent::CastComponentParams* params); CastComponent::CastComponentParams* params);
// Callback for |audio_capturer_redirect_|.
void CreateAudioCapturer(
fidl::InterfaceRequest<fuchsia::media::AudioCapturer> request);
// Holds StartComponent() requests while the ApplicationConfig is being // Holds StartComponent() requests while the ApplicationConfig is being
// fetched from the ApplicationConfigManager. // fetched from the ApplicationConfigManager.
base::flat_set<std::unique_ptr<CastComponent::CastComponentParams>, base::flat_set<std::unique_ptr<CastComponent::CastComponentParams>,
...@@ -97,6 +113,12 @@ class CastRunner : public WebContentRunner { ...@@ -97,6 +113,12 @@ class CastRunner : public WebContentRunner {
// Manages isolated CastRunners owned by |this| instance. // Manages isolated CastRunners owned by |this| instance.
base::flat_set<std::unique_ptr<CastRunner>, base::UniquePtrComparator> base::flat_set<std::unique_ptr<CastRunner>, base::UniquePtrComparator>
isolated_runners_; isolated_runners_;
std::unique_ptr<base::fuchsia::FilteredServiceDirectory> service_directory_;
std::unique_ptr<AudioCapturerRedirect> audio_capturer_redirect_;
// Last component that was created with permission to access MICROPHONE.
CastComponent* audio_capturer_component_ = nullptr;
}; };
#endif // FUCHSIA_RUNNERS_CAST_CAST_RUNNER_H_ #endif // FUCHSIA_RUNNERS_CAST_CAST_RUNNER_H_
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "fuchsia/base/test_devtools_list_fetcher.h" #include "fuchsia/base/test_devtools_list_fetcher.h"
#include "fuchsia/base/test_navigation_listener.h" #include "fuchsia/base/test_navigation_listener.h"
#include "fuchsia/base/url_request_rewrite_test_util.h" #include "fuchsia/base/url_request_rewrite_test_util.h"
#include "fuchsia/runners/cast/audio_capturer_redirect.h"
#include "fuchsia/runners/cast/cast_runner.h" #include "fuchsia/runners/cast/cast_runner.h"
#include "fuchsia/runners/cast/fake_application_config_manager.h" #include "fuchsia/runners/cast/fake_application_config_manager.h"
#include "fuchsia/runners/cast/test_api_bindings.h" #include "fuchsia/runners/cast/test_api_bindings.h"
...@@ -159,6 +160,9 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase { ...@@ -159,6 +160,9 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase {
FakeComponentState(const FakeComponentState&) = delete; FakeComponentState(const FakeComponentState&) = delete;
FakeComponentState& operator=(const FakeComponentState&) = delete; FakeComponentState& operator=(const FakeComponentState&) = delete;
// Make outgoing_directory() public.
using ComponentStateBase::outgoing_directory;
FakeApplicationContext* application_context() { FakeApplicationContext* application_context() {
return &application_context_; return &application_context_;
} }
...@@ -254,6 +258,10 @@ class CastRunnerIntegrationTest : public testing::Test { ...@@ -254,6 +258,10 @@ class CastRunnerIntegrationTest : public testing::Test {
component_url, &app_config_manager_, &api_bindings_, component_url, &app_config_manager_, &api_bindings_,
&url_request_rewrite_rules_provider_); &url_request_rewrite_rules_provider_);
component_state_ = component_state.get(); component_state_ = component_state.get();
if (init_component_state_callback_)
std::move(init_component_state_callback_).Run(component_state_);
return component_state; return component_state;
} }
...@@ -366,6 +374,8 @@ class CastRunnerIntegrationTest : public testing::Test { ...@@ -366,6 +374,8 @@ class CastRunnerIntegrationTest : public testing::Test {
FakeComponentState* component_state_ = nullptr; FakeComponentState* component_state_ = nullptr;
CastComponent* cast_component_ = nullptr; CastComponent* cast_component_ = nullptr;
base::OnceCallback<void(FakeComponentState*)> init_component_state_callback_;
// ServiceDirectory into which the CastRunner will publish itself. // ServiceDirectory into which the CastRunner will publish itself.
sys::OutgoingDirectory outgoing_directory_; sys::OutgoingDirectory outgoing_directory_;
...@@ -690,6 +700,37 @@ TEST_F(CastRunnerIntegrationTest, ApplicationConfigAgentUrlRewriteOptional) { ...@@ -690,6 +700,37 @@ TEST_F(CastRunnerIntegrationTest, ApplicationConfigAgentUrlRewriteOptional) {
EXPECT_FALSE(component_state_->url_request_rules_provider_has_clients()); EXPECT_FALSE(component_state_->url_request_rules_provider_has_clients());
} }
TEST_F(CastRunnerIntegrationTest, MicRedirect) {
GURL app_url = test_server_.GetURL("/mic.html");
auto app_config =
FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
fuchsia::web::PermissionDescriptor mic_permission;
mic_permission.set_type(fuchsia::web::PermissionType::MICROPHONE);
app_config.mutable_permissions()->push_back(std::move(mic_permission));
app_config_manager_.AddAppConfig(std::move(app_config));
base::RunLoop run_loop;
std::unique_ptr<AudioCapturerRedirect> redirect;
init_component_state_callback_ = base::BindOnce(
[](std::unique_ptr<AudioCapturerRedirect>* redirect,
base::OnceClosure quit_closure, FakeComponentState* component_state) {
*redirect = std::make_unique<AudioCapturerRedirect>(
component_state->outgoing_directory(),
base::BindRepeating(
[](base::OnceClosure quit_closure,
fidl::InterfaceRequest<fuchsia::media::AudioCapturer>
request) { std::move(quit_closure).Run(); },
base::Passed(std::move(quit_closure))));
},
&redirect, base::Passed(run_loop.QuitClosure()));
CreateComponentContextAndStartComponent();
run_loop.Run();
}
class HeadlessCastRunnerIntegrationTest : public CastRunnerIntegrationTest { class HeadlessCastRunnerIntegrationTest : public CastRunnerIntegrationTest {
public: public:
HeadlessCastRunnerIntegrationTest() HeadlessCastRunnerIntegrationTest()
......
<script>
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then((stream) => { document.title = 'done'; });
</script>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment