1 // Copyright 2014 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.
5 #include "chrome/browser/copresence/chrome_whispernet_client.h"
7 #include "base/stl_util.h"
8 #include "chrome/browser/copresence/chrome_whispernet_config.h"
9 #include "chrome/browser/extensions/api/copresence_private/copresence_private_api.h"
10 #include "chrome/browser/extensions/component_loader.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/common/extensions/api/copresence_private.h"
13 #include "content/public/browser/browser_context.h"
14 #include "extensions/browser/event_router.h"
15 #include "extensions/browser/extension_system.h"
16 #include "grit/browser_resources.h"
17 #include "media/audio/audio_manager.h"
18 #include "media/audio/audio_manager_base.h"
19 #include "media/audio/audio_parameters.h"
21 using audio_modem::AUDIBLE
;
22 using audio_modem::AudioType
;
23 using audio_modem::BOTH
;
24 using audio_modem::INAUDIBLE
;
25 using audio_modem::SamplesCallback
;
26 using audio_modem::SuccessCallback
;
27 using audio_modem::TokensCallback
;
28 using audio_modem::TokenParameters
;
30 using extensions::api::copresence_private::AudioParameters
;
31 using extensions::api::copresence_private::DecodeSamplesParameters
;
32 using extensions::api::copresence_private::EncodeTokenParameters
;
33 using ApiTokenParams
= extensions::api::copresence_private::TokenParameters
;
35 namespace OnConfigAudio
=
36 extensions::api::copresence_private::OnConfigAudio
;
37 namespace OnDecodeSamplesRequest
=
38 extensions::api::copresence_private::OnDecodeSamplesRequest
;
39 namespace OnEncodeTokenRequest
=
40 extensions::api::copresence_private::OnEncodeTokenRequest
;
42 using extensions::Event
;
46 AudioParamData
GetDefaultAudioConfig() {
47 media::AudioParameters params
=
48 media::AudioManager::Get()->GetInputStreamParameters(
49 media::AudioManagerBase::kDefaultDeviceId
);
51 AudioParamData config_data
= {};
53 config_data
.audio_dtmf
.coder_sample_rate
=
54 config_data
.audio_dsss
.coder_sample_rate
=
55 audio_modem::kDefaultSampleRate
;
57 config_data
.audio_dtmf
.recording_sample_rate
=
58 config_data
.audio_dsss
.recording_sample_rate
= params
.sample_rate();
60 config_data
.audio_dtmf
.num_repetitions_to_play
=
61 config_data
.audio_dsss
.num_repetitions_to_play
=
62 audio_modem::kDefaultRepetitions
;
64 config_data
.audio_dsss
.upsampling_factor
= audio_modem::kDefaultBitsPerSample
;
65 config_data
.audio_dsss
.desired_carrier_frequency
=
66 audio_modem::kDefaultCarrierFrequency
;
68 config_data
.recording_channels
= params
.channels();
73 // ApiTokenParams is not copyable, so we must take it as an output argument.
74 // TODO(ckehoe): Pass protos to Whispernet to avoid all these conversions.
75 void ConvertTokenParams(const TokenParameters
& in
, ApiTokenParams
* out
) {
76 out
->length
= in
.length
;
78 out
->parity
= in
.parity
;
84 const char ChromeWhispernetClient::kWhispernetProxyExtensionId
[] =
85 "bpfmnplchembfbdgieamdodgaencleal";
90 ChromeWhispernetClient::ChromeWhispernetClient(
91 content::BrowserContext
* browser_context
)
92 : browser_context_(browser_context
),
93 event_router_(extensions::EventRouter::Get(browser_context
)),
94 extension_loaded_(false) {
95 DCHECK(browser_context_
);
98 ChromeWhispernetClient::~ChromeWhispernetClient() {}
100 void ChromeWhispernetClient::Initialize(
101 const SuccessCallback
& init_callback
) {
102 DVLOG(3) << "Initializing whispernet proxy client.";
104 DCHECK(!init_callback
.is_null());
105 init_callback_
= init_callback
;
107 ExtensionService
* extension_service
=
108 extensions::ExtensionSystem::Get(browser_context_
)->extension_service();
109 CHECK(extension_service
);
111 extensions::ComponentLoader
* loader
= extension_service
->component_loader();
113 if (!loader
->Exists(kWhispernetProxyExtensionId
)) {
114 DVLOG(3) << "Loading Whispernet proxy.";
115 loader
->Add(IDR_WHISPERNET_PROXY_MANIFEST
,
116 base::FilePath(FILE_PATH_LITERAL("whispernet_proxy")));
119 client_id_
= extensions::CopresencePrivateService::GetFactoryInstance()
120 ->Get(browser_context_
)->RegisterWhispernetClient(this);
121 AudioConfiguration(GetDefaultAudioConfig());
124 void ChromeWhispernetClient::EncodeToken(
125 const std::string
& token_str
,
127 const TokenParameters token_params
[2]) {
128 DCHECK(type
== AUDIBLE
|| type
== INAUDIBLE
);
130 EncodeTokenParameters params
;
131 params
.token
.token
= token_str
;
132 params
.token
.audible
= (type
== AUDIBLE
);
133 ConvertTokenParams(token_params
[type
], ¶ms
.token_params
);
135 SendEventIfLoaded(make_scoped_ptr(new Event(
136 extensions::events::COPRESENCE_PRIVATE_ON_ENCODE_TOKEN_REQUEST
,
137 OnEncodeTokenRequest::kEventName
,
138 OnEncodeTokenRequest::Create(client_id_
, params
), browser_context_
)));
141 void ChromeWhispernetClient::DecodeSamples(
143 const std::string
& samples
,
144 const TokenParameters token_params
[2]) {
145 DecodeSamplesParameters params
;
146 params
.samples
.assign(samples
.begin(), samples
.end());
147 params
.decode_audible
= (type
== AUDIBLE
|| type
== BOTH
);
148 params
.decode_inaudible
= (type
== INAUDIBLE
|| type
== BOTH
);
149 ConvertTokenParams(token_params
[AUDIBLE
], ¶ms
.audible_token_params
);
150 ConvertTokenParams(token_params
[INAUDIBLE
], ¶ms
.inaudible_token_params
);
152 SendEventIfLoaded(make_scoped_ptr(new Event(
153 extensions::events::COPRESENCE_PRIVATE_ON_DECODE_SAMPLES_REQUEST
,
154 OnDecodeSamplesRequest::kEventName
,
155 OnDecodeSamplesRequest::Create(client_id_
, params
), browser_context_
)));
158 void ChromeWhispernetClient::RegisterTokensCallback(
159 const TokensCallback
& tokens_callback
) {
160 tokens_callback_
= tokens_callback
;
163 void ChromeWhispernetClient::RegisterSamplesCallback(
164 const SamplesCallback
& samples_callback
) {
165 samples_callback_
= samples_callback
;
168 TokensCallback
ChromeWhispernetClient::GetTokensCallback() {
169 return tokens_callback_
;
172 SamplesCallback
ChromeWhispernetClient::GetSamplesCallback() {
173 return samples_callback_
;
176 SuccessCallback
ChromeWhispernetClient::GetInitializedCallback() {
177 return base::Bind(&ChromeWhispernetClient::OnExtensionLoaded
,
178 base::Unretained(this));
182 // Private functions.
184 void ChromeWhispernetClient::AudioConfiguration(const AudioParamData
& params
) {
185 AudioParameters audio_params
;
187 // We serialize AudioConfigData to a string and send it to the whispernet
189 const size_t params_size
= sizeof(params
);
190 audio_params
.param_data
.resize(params_size
);
191 memcpy(vector_as_array(&audio_params
.param_data
), ¶ms
, params_size
);
193 DVLOG(3) << "Configuring audio for client " << client_id_
;
194 SendEventIfLoaded(make_scoped_ptr(new Event(
195 extensions::events::COPRESENCE_PRIVATE_ON_CONFIG_AUDIO
,
196 OnConfigAudio::kEventName
,
197 OnConfigAudio::Create(client_id_
, audio_params
), browser_context_
)));
200 void ChromeWhispernetClient::SendEventIfLoaded(
201 scoped_ptr
<extensions::Event
> event
) {
202 DCHECK(event_router_
);
204 if (extension_loaded_
) {
205 event_router_
->DispatchEventToExtension(kWhispernetProxyExtensionId
,
208 DVLOG(2) << "Queueing event " << event
->event_name
209 << " for client " << client_id_
;
210 queued_events_
.push_back(event
.release());
214 void ChromeWhispernetClient::OnExtensionLoaded(bool success
) {
215 DCHECK(!init_callback_
.is_null());
216 init_callback_
.Run(success
);
218 DVLOG(3) << "Sending " << queued_events_
.size()
219 << " queued requests to whispernet from client "
222 // In this loop, ownership of each Event is passed to a scoped_ptr instead.
223 // Thus we can just discard the pointers at the end.
224 DCHECK(event_router_
);
225 for (Event
* event
: queued_events_
) {
226 event_router_
->DispatchEventToExtension(kWhispernetProxyExtensionId
,
227 make_scoped_ptr(event
));
229 queued_events_
.weak_clear();
231 extension_loaded_
= true;