OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/browser/renderer_host/media/render_frame_audio_output_service.
h" |
| 6 |
| 7 #include <cmath> |
| 8 #include <limits> |
| 9 #include <utility> |
| 10 |
| 11 #include "base/bind.h" |
| 12 #include "base/memory/shared_memory.h" |
| 13 #include "base/memory/shared_memory_handle.h" |
| 14 #include "base/run_loop.h" |
| 15 #include "base/sync_socket.h" |
| 16 #include "content/browser/audio_manager_thread.h" |
| 17 #include "content/browser/renderer_host/media/audio_output_service_context_impl.
h" |
| 18 #include "content/browser/renderer_host/media/media_stream_manager.h" |
| 19 #include "content/common/media/audio_output.mojom.h" |
| 20 #include "content/public/browser/browser_thread.h" |
| 21 #include "content/public/test/mock_render_process_host.h" |
| 22 #include "content/public/test/test_browser_context.h" |
| 23 #include "content/public/test/test_browser_thread_bundle.h" |
| 24 #include "media/audio/audio_output_controller.h" |
| 25 #include "media/audio/fake_audio_log_factory.h" |
| 26 #include "media/audio/fake_audio_manager.h" |
| 27 #include "media/base/audio_parameters.h" |
| 28 #include "media/base/media_switches.h" |
| 29 #include "mojo/public/cpp/bindings/binding.h" |
| 30 #include "mojo/public/cpp/system/platform_handle.h" |
| 31 #include "testing/gmock/include/gmock/gmock.h" |
| 32 #include "testing/gtest/include/gtest/gtest.h" |
| 33 |
| 34 namespace content { |
| 35 |
| 36 namespace { |
| 37 |
| 38 using testing::_; |
| 39 using testing::StrictMock; |
| 40 using testing::Return; |
| 41 using testing::Test; |
| 42 using AudioOutputService = mojom::RendererAudioOutputService; |
| 43 using AudioOutputServicePtr = mojo::InterfacePtr<AudioOutputService>; |
| 44 using AudioOutputServiceRequest = mojo::InterfaceRequest<AudioOutputService>; |
| 45 using AudioOutput = mojom::AudioOutput; |
| 46 using AudioOutputPtr = mojo::InterfacePtr<AudioOutput>; |
| 47 using AudioOutputRequest = mojo::InterfaceRequest<AudioOutput>; |
| 48 |
| 49 const int kRenderProcessId = 42; |
| 50 const int kRenderFrameId = 24; |
| 51 const char kSecurityOrigin[] = "http://localhost"; |
| 52 const char kSalt[] = "salt"; |
| 53 const media::AudioParameters params( |
| 54 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| 55 media::CHANNEL_LAYOUT_MONO, |
| 56 44100 /*sample frequency*/, |
| 57 16 /*bits per sample*/, |
| 58 441 /*10 ms buffers*/); |
| 59 |
| 60 class MockAudioOutputDelegate : public AudioOutputDelegate { |
| 61 MOCK_CONST_METHOD0(GetController, |
| 62 scoped_refptr<media::AudioOutputController>()); |
| 63 MOCK_CONST_METHOD0(GetStreamId, int()); |
| 64 MOCK_METHOD0(OnPlayStream, void()); |
| 65 MOCK_METHOD0(OnPauseStream, void()); |
| 66 MOCK_METHOD1(OnSetVolume, void(double)); |
| 67 }; |
| 68 |
| 69 class MockContext : public AudioOutputServiceContext { |
| 70 public: |
| 71 explicit MockContext(bool auth_ok, int render_process_id = kRenderProcessId) |
| 72 : render_process_id_(render_process_id), |
| 73 salt_(kSalt), |
| 74 auth_ok_(auth_ok) {} |
| 75 |
| 76 ~MockContext() { EXPECT_EQ(nullptr, delegate_); } |
| 77 |
| 78 int GetRenderProcessId() const override { return render_process_id_; } |
| 79 |
| 80 void RequestDeviceAuthorization( |
| 81 int render_frame_id, |
| 82 int session_id, |
| 83 const std::string& device_id, |
| 84 const url::Origin& security_origin, |
| 85 AuthorizationCompletedCallback cb) const override { |
| 86 EXPECT_EQ(render_frame_id, kRenderFrameId); |
| 87 EXPECT_EQ(session_id, 0); |
| 88 if (auth_ok_) { |
| 89 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 90 FROM_HERE, |
| 91 base::Bind(cb, media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, |
| 92 false, media::AudioParameters::UnavailableDeviceParams(), |
| 93 "default")); |
| 94 return; |
| 95 } |
| 96 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 97 FROM_HERE, |
| 98 base::Bind(cb, media::OutputDeviceStatus:: |
| 99 OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED, |
| 100 false, media::AudioParameters::UnavailableDeviceParams(), |
| 101 "default")); |
| 102 } |
| 103 |
| 104 const std::string& GetSalt() const override { return salt_; } |
| 105 |
| 106 void PrepareDelegateForCreation( |
| 107 std::unique_ptr<AudioOutputDelegate> delegate) { |
| 108 EXPECT_EQ(nullptr, delegate_); |
| 109 delegate_ = std::move(delegate); |
| 110 } |
| 111 |
| 112 std::unique_ptr<AudioOutputDelegate> CreateDelegate( |
| 113 const std::string& unique_device_id, |
| 114 int render_frame_id, |
| 115 AudioOutputDelegate::EventHandler* handler, |
| 116 const media::AudioParameters& params) override { |
| 117 EXPECT_NE(nullptr, delegate_); |
| 118 return std::move(delegate_); |
| 119 } |
| 120 |
| 121 MOCK_METHOD1(OnServiceFinished, |
| 122 void(mojom::RendererAudioOutputService* service)); |
| 123 |
| 124 private: |
| 125 int render_process_id_ = kRenderProcessId; |
| 126 const std::string salt_; |
| 127 const bool auth_ok_; |
| 128 std::unique_ptr<AudioOutputDelegate> delegate_; |
| 129 }; |
| 130 |
| 131 void AuthCallback(media::OutputDeviceStatus* status_out, |
| 132 media::AudioParameters* params_out, |
| 133 std::string* id_out, |
| 134 media::OutputDeviceStatus status, |
| 135 const media::AudioParameters& params, |
| 136 const std::string& id) { |
| 137 *status_out = status; |
| 138 *params_out = params; |
| 139 *id_out = id; |
| 140 } |
| 141 |
| 142 } // namespace |
| 143 |
| 144 class RenderFrameAudioOutputServiceTest : public Test { |
| 145 public: |
| 146 RenderFrameAudioOutputServiceTest() {} |
| 147 |
| 148 ~RenderFrameAudioOutputServiceTest() override {} |
| 149 |
| 150 private: |
| 151 TestBrowserThreadBundle thread_bundle_; |
| 152 }; |
| 153 |
| 154 TEST_F(RenderFrameAudioOutputServiceTest, AuthWithoutStreamRequest) { |
| 155 AudioOutputServicePtr service_ptr; |
| 156 auto service_context_ = base::MakeUnique<MockContext>(true); |
| 157 auto service_ = base::MakeUnique<RenderFrameAudioOutputService>( |
| 158 service_context_.get(), kRenderFrameId, mojo::MakeRequest(&service_ptr)); |
| 159 |
| 160 media::OutputDeviceStatus status; |
| 161 media::AudioParameters params2; |
| 162 std::string id; |
| 163 service_ptr->RequestDeviceAuthorization( |
| 164 nullptr, /*session_id*/ 0, "default", url::Origin(GURL(kSecurityOrigin)), |
| 165 base::Bind(&AuthCallback, base::Unretained(&status), |
| 166 base::Unretained(¶ms2), base::Unretained(&id))); |
| 167 base::RunLoop().RunUntilIdle(); |
| 168 EXPECT_EQ(status, media::OUTPUT_DEVICE_STATUS_OK); |
| 169 EXPECT_TRUE(id.empty()); |
| 170 } |
| 171 |
| 172 TEST_F(RenderFrameAudioOutputServiceTest, AuthWithStreamRequest) { |
| 173 AudioOutputServicePtr service_ptr; |
| 174 AudioOutputPtr output_ptr; |
| 175 auto service_context = base::MakeUnique<MockContext>(true); |
| 176 service_context->PrepareDelegateForCreation( |
| 177 base::MakeUnique<MockAudioOutputDelegate>()); |
| 178 auto service = base::MakeUnique<RenderFrameAudioOutputService>( |
| 179 service_context.get(), kRenderFrameId, mojo::MakeRequest(&service_ptr)); |
| 180 |
| 181 media::OutputDeviceStatus status; |
| 182 media::AudioParameters params2; |
| 183 std::string id; |
| 184 service_ptr->RequestDeviceAuthorization( |
| 185 mojo::MakeRequest<AudioOutput>(&output_ptr), /*session_id*/ 0, "default", |
| 186 url::Origin(GURL(kSecurityOrigin)), |
| 187 base::Bind(&AuthCallback, base::Unretained(&status), |
| 188 base::Unretained(¶ms2), base::Unretained(&id))); |
| 189 base::RunLoop().RunUntilIdle(); |
| 190 EXPECT_EQ(status, media::OUTPUT_DEVICE_STATUS_OK); |
| 191 EXPECT_TRUE(id.empty()); |
| 192 |
| 193 output_ptr->Start(params, |
| 194 base::Bind([](mojo::ScopedSharedBufferHandle handle1, |
| 195 mojo::ScopedHandle handle2) {})); |
| 196 base::RunLoop().RunUntilIdle(); |
| 197 output_ptr.reset(); |
| 198 } |
| 199 |
| 200 TEST_F(RenderFrameAudioOutputServiceTest, AuthWithStreamRequestDenied) { |
| 201 AudioOutputServicePtr service_ptr; |
| 202 AudioOutputPtr output_ptr; |
| 203 auto service_context = base::MakeUnique<MockContext>(false); |
| 204 auto service = base::MakeUnique<RenderFrameAudioOutputService>( |
| 205 service_context.get(), kRenderFrameId, mojo::MakeRequest(&service_ptr)); |
| 206 |
| 207 media::OutputDeviceStatus status; |
| 208 media::AudioParameters params2; |
| 209 std::string id; |
| 210 service_ptr->RequestDeviceAuthorization( |
| 211 mojo::MakeRequest<AudioOutput>(&output_ptr), /*session_id*/ 0, "default", |
| 212 url::Origin(GURL(kSecurityOrigin)), |
| 213 base::Bind(&AuthCallback, base::Unretained(&status), |
| 214 base::Unretained(¶ms2), base::Unretained(&id))); |
| 215 base::RunLoop().RunUntilIdle(); |
| 216 EXPECT_EQ(status, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED); |
| 217 EXPECT_TRUE(id.empty()); |
| 218 |
| 219 output_ptr->Start(params, |
| 220 base::Bind([](mojo::ScopedSharedBufferHandle handle1, |
| 221 mojo::ScopedHandle handle2) {})); |
| 222 base::RunLoop().RunUntilIdle(); |
| 223 output_ptr.reset(); |
| 224 } |
| 225 |
| 226 TEST_F(RenderFrameAudioOutputServiceTest, OutOfRangeSessionId_BadMessage) { |
| 227 // This test checks that we get a bad message if session_id is too large |
| 228 // to fit in an integer. This ensures that we don't overflow when casting the |
| 229 // int64_t to an int |
| 230 if (sizeof(int) >= sizeof(int64_t)) { |
| 231 // In this case, any int64_t would fit in an int, and the case we are |
| 232 // checking for is impossible. |
| 233 return; |
| 234 } |
| 235 |
| 236 TestBrowserContext browser_context; |
| 237 MockRenderProcessHost rph(&browser_context); |
| 238 |
| 239 AudioOutputServicePtr service_ptr; |
| 240 auto service_context = base::MakeUnique<MockContext>(true, rph.GetID()); |
| 241 auto service = base::MakeUnique<RenderFrameAudioOutputService>( |
| 242 service_context.get(), kRenderFrameId, mojo::MakeRequest(&service_ptr)); |
| 243 EXPECT_CALL(*service_context, OnServiceFinished(service.get())); |
| 244 |
| 245 int64_t session_id = std::numeric_limits<int>::max(); |
| 246 ++session_id; |
| 247 service_ptr->RequestDeviceAuthorization( |
| 248 nullptr, session_id, "default", url::Origin(GURL(kSecurityOrigin)), |
| 249 base::Bind([](media::OutputDeviceStatus, const media::AudioParameters&, |
| 250 const std::string&) { EXPECT_FALSE(true); })); |
| 251 base::RunLoop().RunUntilIdle(); |
| 252 EXPECT_EQ(1, rph.bad_msg_count()); |
| 253 } |
| 254 |
| 255 } // namespace content |
OLD | NEW |