Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: chrome/browser/extensions/api/audio_modem/audio_modem_api.cc

Issue 2131993002: Delete the audio modem and copresence private APIs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@eol
Patch Set: Sync again Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 "chrome/browser/extensions/api/audio_modem/audio_modem_api.h"
6
7 #include <stdint.h>
8
9 #include <map>
10 #include <memory>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include "base/base64.h"
16 #include "base/bind.h"
17 #include "base/guid.h"
18 #include "base/lazy_instance.h"
19 #include "base/logging.h"
20 #include "base/memory/ptr_util.h"
21 #include "base/strings/string_util.h"
22 #include "base/timer/timer.h"
23 #include "chrome/browser/copresence/chrome_whispernet_client.h"
24 #include "chrome/common/extensions/api/audio_modem.h"
25 #include "extensions/browser/event_router.h"
26
27 // TODO(ckehoe): Implement transmit fail checking.
28
29 namespace extensions {
30
31 using api::audio_modem::AUDIOBAND_AUDIBLE;
32 using api::audio_modem::AUDIOBAND_INAUDIBLE;
33 using api::audio_modem::Audioband;
34 using api::audio_modem::STATUS_CODERERROR;
35 using api::audio_modem::STATUS_INUSE;
36 using api::audio_modem::STATUS_INVALIDREQUEST;
37 using api::audio_modem::STATUS_SUCCESS;
38 using api::audio_modem::ReceivedToken;
39 using api::audio_modem::RequestParams;
40 using api::audio_modem::Status;
41
42 namespace Transmit = api::audio_modem::Transmit;
43 namespace StopTransmit = api::audio_modem::StopTransmit;
44 namespace Receive = api::audio_modem::Receive;
45 namespace StopReceive = api::audio_modem::StopReceive;
46 namespace OnReceived = api::audio_modem::OnReceived;
47
48 using audio_modem::AUDIBLE;
49 using audio_modem::AudioToken;
50 using audio_modem::AudioType;
51 using audio_modem::INAUDIBLE;
52 using audio_modem::TokenParameters;
53
54 namespace {
55
56 const char kInitFailedError[] = "The audio modem is not available. "
57 "Failed to initialize the token encoder/decoder.";
58 const char kInvalidTokenLengthError[] =
59 "The token length must be greater than zero.";
60 const char kIncorrectTokenLengthError[] =
61 "The token provided did not match the declared token length.";
62 const char kInvalidTimeoutError[] =
63 "Transmit and receive timeouts must be greater than zero.";
64
65 const int kMaxTransmitTimeout = 10 * 60 * 1000; // 10 minutes
66 const int kMaxReceiveTimeout = 60 * 60 * 1000; // 1 hour
67
68 base::LazyInstance<BrowserContextKeyedAPIFactory<AudioModemAPI>>
69 g_factory = LAZY_INSTANCE_INITIALIZER;
70
71 AudioType AudioTypeForBand(Audioband band) {
72 switch (band) {
73 case AUDIOBAND_AUDIBLE:
74 return AUDIBLE;
75 case AUDIOBAND_INAUDIBLE:
76 return INAUDIBLE;
77 default:
78 NOTREACHED();
79 return audio_modem::AUDIO_TYPE_UNKNOWN;
80 }
81 }
82
83 TokenParameters TokenParamsForEncoding(
84 const api::audio_modem::TokenEncoding& encoding) {
85 return TokenParameters(encoding.token_length,
86 encoding.crc ? *encoding.crc : false,
87 encoding.parity ? *encoding.parity : true);
88 }
89
90 const std::string DecodeBase64Token(std::string encoded_token) {
91 // Make sure the token is padded correctly.
92 while (encoded_token.size() % 4 > 0)
93 encoded_token += "=";
94
95 // Decode and return the token.
96 std::string raw_token;
97 bool decode_success = base::Base64Decode(encoded_token, &raw_token);
98 DCHECK(decode_success);
99 return raw_token;
100 }
101
102 } // namespace
103
104
105 // Public functions.
106
107 AudioModemAPI::AudioModemAPI(content::BrowserContext* context)
108 : AudioModemAPI(context,
109 base::WrapUnique(new ChromeWhispernetClient(context)),
110 audio_modem::Modem::Create()) {}
111
112 AudioModemAPI::AudioModemAPI(
113 content::BrowserContext* context,
114 std::unique_ptr<audio_modem::WhispernetClient> whispernet_client,
115 std::unique_ptr<audio_modem::Modem> modem)
116 : browser_context_(context),
117 whispernet_client_(std::move(whispernet_client)),
118 modem_(std::move(modem)),
119 init_failed_(false) {
120 // We own these objects, so these callbacks will not outlive us.
121 whispernet_client_->Initialize(
122 base::Bind(&AudioModemAPI::WhispernetInitComplete,
123 base::Unretained(this)));
124 modem_->Initialize(whispernet_client_.get(),
125 base::Bind(&AudioModemAPI::TokensReceived,
126 base::Unretained(this)));
127 }
128
129 AudioModemAPI::~AudioModemAPI() {
130 for (const auto& timer_entry : receive_timers_[0])
131 delete timer_entry.second;
132 for (const auto& timer_entry : receive_timers_[1])
133 delete timer_entry.second;
134 }
135
136 Status AudioModemAPI::StartTransmit(const std::string& app_id,
137 const RequestParams& params,
138 const std::string& token) {
139 AudioType audio_type = AudioTypeForBand(params.band);
140 if (transmitters_[audio_type].empty())
141 transmitters_[audio_type] = app_id;
142 else if (transmitters_[audio_type] != app_id)
143 return STATUS_INUSE;
144
145 DVLOG(3) << "Starting transmit for app " << app_id;
146
147 std::string encoded_token;
148 base::Base64Encode(token, &encoded_token);
149 base::RemoveChars(encoded_token, "=", &encoded_token);
150
151 modem_->SetTokenParams(audio_type, TokenParamsForEncoding(params.encoding));
152 modem_->SetToken(audio_type, encoded_token);
153 modem_->StartPlaying(audio_type);
154
155 transmit_timers_[audio_type].Start(
156 FROM_HERE,
157 base::TimeDelta::FromMilliseconds(params.timeout_millis),
158 base::Bind(base::IgnoreResult(&AudioModemAPI::StopTransmit),
159 base::Unretained(this),
160 app_id,
161 audio_type));
162 return STATUS_SUCCESS;
163 }
164
165 Status AudioModemAPI::StopTransmit(const std::string& app_id,
166 AudioType audio_type) {
167 if (transmitters_[audio_type] != app_id)
168 return STATUS_INVALIDREQUEST;
169
170 DVLOG(3) << "Stopping transmit for app " << app_id;
171 transmitters_[audio_type].clear();
172 modem_->StopPlaying(audio_type);
173 return STATUS_SUCCESS;
174 }
175
176 void AudioModemAPI::StartReceive(const std::string& app_id,
177 const RequestParams& params) {
178 DVLOG(3) << "Starting receive for app " << app_id;
179
180 AudioType audio_type = AudioTypeForBand(params.band);
181 modem_->SetTokenParams(audio_type, TokenParamsForEncoding(params.encoding));
182 modem_->StartRecording(audio_type);
183
184 if (receive_timers_[audio_type].count(app_id) == 0)
185 receive_timers_[audio_type][app_id] = new base::OneShotTimer;
186 DCHECK(receive_timers_[audio_type][app_id]);
187 receive_timers_[audio_type][app_id]->Start(
188 FROM_HERE,
189 base::TimeDelta::FromMilliseconds(params.timeout_millis),
190 base::Bind(base::IgnoreResult(&AudioModemAPI::StopReceive),
191 base::Unretained(this),
192 app_id,
193 audio_type));
194 }
195
196 Status AudioModemAPI::StopReceive(const std::string& app_id,
197 AudioType audio_type) {
198 if (receive_timers_[audio_type].count(app_id) == 0)
199 return STATUS_INVALIDREQUEST;
200
201 DCHECK(receive_timers_[audio_type][app_id]);
202 delete receive_timers_[audio_type][app_id];
203 receive_timers_[audio_type].erase(app_id);
204
205 DVLOG(3) << "Stopping receive for app " << app_id;
206 if (receive_timers_[audio_type].empty())
207 modem_->StopRecording(audio_type);
208 return STATUS_SUCCESS;
209 }
210
211 // static
212 BrowserContextKeyedAPIFactory<AudioModemAPI>*
213 AudioModemAPI::GetFactoryInstance() {
214 return g_factory.Pointer();
215 }
216
217
218 // Private functions.
219
220 void AudioModemAPI::WhispernetInitComplete(bool success) {
221 if (success) {
222 VLOG(2) << "Whispernet initialized successfully.";
223 } else {
224 LOG(ERROR) << "Failed to initialize Whispernet!";
225 init_failed_ = true;
226 }
227 }
228
229 void AudioModemAPI::TokensReceived(const std::vector<AudioToken>& tokens) {
230 // Distribute the tokens to the appropriate app(s).
231 std::list<ReceivedToken> all_tokens;
232 std::map<std::string, std::vector<ReceivedToken*>> tokens_by_app;
233 for (const AudioToken& token : tokens) {
234 ReceivedToken api_token;
235 const std::string& raw_token = DecodeBase64Token(token.token);
236 api_token.token.assign(raw_token.c_str(),
237 raw_token.c_str() + raw_token.size());
238 api_token.band = token.audible ? AUDIOBAND_AUDIBLE : AUDIOBAND_INAUDIBLE;
239 all_tokens.push_back(std::move(api_token));
240 for (const auto& receiver :
241 receive_timers_[token.audible ? AUDIBLE : INAUDIBLE]) {
242 tokens_by_app[receiver.first].push_back(&all_tokens.back());
243 }
244 }
245
246 // Send events to the appropriate app(s).
247 for (const auto& app_entry : tokens_by_app) {
248 const std::string& app_id = app_entry.first;
249 const auto& app_tokens = app_entry.second;
250 if (app_id.empty())
251 continue;
252
253 // Construct the event arguments by hand because a given token can be
254 // present for multiple listeners, so constructing a
255 // std::vector<ReceivedToken> for each is inefficient.
256 std::unique_ptr<base::ListValue> tokens_value(new base::ListValue());
257 for (const ReceivedToken* token : app_tokens)
258 tokens_value->Append(token->ToValue());
259 std::unique_ptr<base::ListValue> args(new base::ListValue());
260 args->Append(std::move(tokens_value));
261
262 EventRouter::Get(browser_context_)
263 ->DispatchEventToExtension(
264 app_id, base::WrapUnique(new Event(events::AUDIO_MODEM_ON_RECEIVED,
265 OnReceived::kEventName,
266 std::move(args))));
267 }
268 }
269
270
271 // Functions outside the API scope.
272
273 template <>
274 void
275 BrowserContextKeyedAPIFactory<AudioModemAPI>::DeclareFactoryDependencies() {
276 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
277 }
278
279 ExtensionFunction::ResponseAction AudioModemTransmitFunction::Run() {
280 std::unique_ptr<Transmit::Params> params(Transmit::Params::Create(*args_));
281 EXTENSION_FUNCTION_VALIDATE(params.get());
282 AudioModemAPI* api =
283 AudioModemAPI::GetFactoryInstance()->Get(browser_context());
284 if (api->init_failed()) {
285 return RespondNow(ErrorWithArguments(
286 Transmit::Results::Create(STATUS_CODERERROR),
287 kInitFailedError));
288 }
289
290 // Check the token length.
291 int token_length = params->params.encoding.token_length;
292 if (token_length <= 0) {
293 return RespondNow(ErrorWithArguments(
294 Transmit::Results::Create(STATUS_INVALIDREQUEST),
295 kInvalidTokenLengthError));
296 }
297 const char* token = params->token.data();
298 std::string token_str(token, params->token.size());
299 if (static_cast<int>(token_str.size()) != token_length) {
300 return RespondNow(ErrorWithArguments(
301 Transmit::Results::Create(STATUS_INVALIDREQUEST),
302 kIncorrectTokenLengthError));
303 }
304
305 // Check the timeout.
306 int64_t timeout_millis = params->params.timeout_millis;
307 if (timeout_millis <= 0) {
308 return RespondNow(ErrorWithArguments(
309 Transmit::Results::Create(STATUS_INVALIDREQUEST),
310 kInvalidTimeoutError));
311 }
312 if (timeout_millis > kMaxTransmitTimeout)
313 timeout_millis = kMaxTransmitTimeout;
314
315 // Start transmission.
316 Status status = api->StartTransmit(extension_id(), params->params, token_str);
317 return RespondNow(ArgumentList(Transmit::Results::Create(status)));
318 }
319
320 ExtensionFunction::ResponseAction AudioModemStopTransmitFunction::Run() {
321 std::unique_ptr<StopTransmit::Params> params(
322 StopTransmit::Params::Create(*args_));
323 EXTENSION_FUNCTION_VALIDATE(params.get());
324
325 Status status = AudioModemAPI::GetFactoryInstance()->Get(browser_context())
326 ->StopTransmit(extension_id(), AudioTypeForBand(params->band));
327 return RespondNow(ArgumentList(StopTransmit::Results::Create(status)));
328 }
329
330 ExtensionFunction::ResponseAction AudioModemReceiveFunction::Run() {
331 std::unique_ptr<Receive::Params> params(Receive::Params::Create(*args_));
332 EXTENSION_FUNCTION_VALIDATE(params.get());
333 AudioModemAPI* api =
334 AudioModemAPI::GetFactoryInstance()->Get(browser_context());
335 if (api->init_failed()) {
336 return RespondNow(ErrorWithArguments(
337 Transmit::Results::Create(STATUS_CODERERROR),
338 kInitFailedError));
339 }
340
341 // Check the timeout.
342 int64_t timeout_millis = params->params.timeout_millis;
343 if (timeout_millis <= 0) {
344 return RespondNow(ErrorWithArguments(
345 Receive::Results::Create(STATUS_INVALIDREQUEST),
346 kInvalidTimeoutError));
347 }
348 if (timeout_millis > kMaxReceiveTimeout)
349 timeout_millis = kMaxReceiveTimeout;
350
351 // Start receiving.
352 api->StartReceive(extension_id(), params->params);
353 return RespondNow(ArgumentList(Receive::Results::Create(STATUS_SUCCESS)));
354 }
355
356 ExtensionFunction::ResponseAction AudioModemStopReceiveFunction::Run() {
357 std::unique_ptr<StopReceive::Params> params(
358 StopReceive::Params::Create(*args_));
359 EXTENSION_FUNCTION_VALIDATE(params.get());
360
361 Status status = AudioModemAPI::GetFactoryInstance()->Get(browser_context())
362 ->StopReceive(extension_id(), AudioTypeForBand(params->band));
363 return RespondNow(ArgumentList(StopReceive::Results::Create(status)));
364 }
365
366 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698