| OLD | NEW |
| (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 "components/copresence/rpc/rpc_handler.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/command_line.h" | |
| 14 #include "base/logging.h" | |
| 15 #include "base/memory/ptr_util.h" | |
| 16 #include "base/strings/string_util.h" | |
| 17 #include "base/strings/stringprintf.h" | |
| 18 // TODO(ckehoe): time.h includes windows.h, which #defines DeviceCapabilities | |
| 19 // to DeviceCapabilitiesW. This breaks the pb.h headers below. For now, | |
| 20 // we fix this with an #undef. | |
| 21 #include "base/time/time.h" | |
| 22 #include "build/build_config.h" | |
| 23 #if defined(OS_WIN) | |
| 24 #undef DeviceCapabilities | |
| 25 #endif | |
| 26 | |
| 27 #include "components/audio_modem/public/audio_modem_types.h" | |
| 28 #include "components/copresence/copresence_state_impl.h" | |
| 29 #include "components/copresence/copresence_switches.h" | |
| 30 #include "components/copresence/handlers/directive_handler.h" | |
| 31 #include "components/copresence/handlers/gcm_handler.h" | |
| 32 #include "components/copresence/proto/codes.pb.h" | |
| 33 #include "components/copresence/proto/data.pb.h" | |
| 34 #include "components/copresence/proto/rpcs.pb.h" | |
| 35 #include "components/copresence/public/copresence_constants.h" | |
| 36 #include "components/copresence/public/copresence_delegate.h" | |
| 37 #include "components/copresence/rpc/http_post.h" | |
| 38 #include "net/http/http_status_code.h" | |
| 39 | |
| 40 using google::protobuf::MessageLite; | |
| 41 | |
| 42 using audio_modem::AUDIBLE; | |
| 43 using audio_modem::AudioToken; | |
| 44 using audio_modem::INAUDIBLE; | |
| 45 | |
| 46 // TODO(ckehoe): Return error messages for bad requests. | |
| 47 | |
| 48 namespace copresence { | |
| 49 | |
| 50 const char RpcHandler::kReportRequestRpcName[] = "report"; | |
| 51 | |
| 52 namespace { | |
| 53 | |
| 54 const int kTokenLoggingSuffix = 5; | |
| 55 const int kInvalidTokenExpiryTimeMinutes = 10; | |
| 56 const int kMaxInvalidTokens = 10000; | |
| 57 const char kRegisterDeviceRpcName[] = "registerdevice"; | |
| 58 const char kDefaultCopresenceServer[] = | |
| 59 "https://www.googleapis.com/copresence/v2/copresence"; | |
| 60 | |
| 61 // UrlSafe is defined as: | |
| 62 // '/' represented by a '_' and '+' represented by a '-' | |
| 63 // TODO(rkc): Move this to the wrapper. | |
| 64 std::string ToUrlSafe(std::string token) { | |
| 65 base::ReplaceChars(token, "+", "-", &token); | |
| 66 base::ReplaceChars(token, "/", "_", &token); | |
| 67 return token; | |
| 68 } | |
| 69 | |
| 70 // Logging | |
| 71 | |
| 72 // Checks for a copresence error. If there is one, logs it and returns true. | |
| 73 bool IsErrorStatus(const Status& status) { | |
| 74 if (status.code() != OK) { | |
| 75 LOG(ERROR) << "Copresence error code " << status.code() | |
| 76 << (status.message().empty() ? "" : ": " + status.message()); | |
| 77 } | |
| 78 return status.code() != OK; | |
| 79 } | |
| 80 | |
| 81 void LogIfErrorStatus(const util::error::Code& code, | |
| 82 const std::string& context) { | |
| 83 LOG_IF(ERROR, code != util::error::OK) | |
| 84 << context << " error " << code << ". See " | |
| 85 << "cs/google3/util/task/codes.proto for more info."; | |
| 86 } | |
| 87 | |
| 88 // If any errors occurred, logs them and returns true. | |
| 89 bool ReportErrorLogged(const ReportResponse& response) { | |
| 90 bool result = IsErrorStatus(response.header().status()); | |
| 91 | |
| 92 // The Report fails or succeeds as a unit. If any responses had errors, | |
| 93 // the header will too. Thus we don't need to propagate individual errors. | |
| 94 if (response.has_update_signals_response()) | |
| 95 LogIfErrorStatus(response.update_signals_response().status(), "Update"); | |
| 96 if (response.has_manage_messages_response()) | |
| 97 LogIfErrorStatus(response.manage_messages_response().status(), "Publish"); | |
| 98 if (response.has_manage_subscriptions_response()) { | |
| 99 LogIfErrorStatus(response.manage_subscriptions_response().status(), | |
| 100 "Subscribe"); | |
| 101 } | |
| 102 | |
| 103 return result; | |
| 104 } | |
| 105 | |
| 106 const std::string LoggingStrForToken(const std::string& auth_token) { | |
| 107 if (auth_token.empty()) | |
| 108 return "anonymous"; | |
| 109 | |
| 110 std::string token_suffix = auth_token.substr( | |
| 111 auth_token.length() - kTokenLoggingSuffix, kTokenLoggingSuffix); | |
| 112 return "token ..." + token_suffix; | |
| 113 } | |
| 114 | |
| 115 | |
| 116 // Request construction | |
| 117 | |
| 118 template <typename T> | |
| 119 BroadcastScanConfiguration GetBroadcastScanConfig(const T& msg) { | |
| 120 if (msg.has_token_exchange_strategy() && | |
| 121 msg.token_exchange_strategy().has_broadcast_scan_configuration()) { | |
| 122 return msg.token_exchange_strategy().broadcast_scan_configuration(); | |
| 123 } | |
| 124 return BROADCAST_SCAN_CONFIGURATION_UNKNOWN; | |
| 125 } | |
| 126 | |
| 127 std::unique_ptr<DeviceState> GetDeviceCapabilities( | |
| 128 const ReportRequest& request) { | |
| 129 std::unique_ptr<DeviceState> state(new DeviceState); | |
| 130 | |
| 131 TokenTechnology* ultrasound = | |
| 132 state->mutable_capabilities()->add_token_technology(); | |
| 133 ultrasound->set_medium(AUDIO_ULTRASOUND_PASSBAND); | |
| 134 ultrasound->add_instruction_type(TRANSMIT); | |
| 135 ultrasound->add_instruction_type(RECEIVE); | |
| 136 | |
| 137 TokenTechnology* audible = | |
| 138 state->mutable_capabilities()->add_token_technology(); | |
| 139 audible->set_medium(AUDIO_AUDIBLE_DTMF); | |
| 140 audible->add_instruction_type(TRANSMIT); | |
| 141 audible->add_instruction_type(RECEIVE); | |
| 142 | |
| 143 return state; | |
| 144 } | |
| 145 | |
| 146 // TODO(ckehoe): We're keeping this code in a separate function for now | |
| 147 // because we get a version string from Chrome, but the proto expects | |
| 148 // an int64_t version. We should probably change the version proto | |
| 149 // to handle a more detailed version. | |
| 150 ClientVersion* CreateVersion(const std::string& client, | |
| 151 const std::string& version_name) { | |
| 152 ClientVersion* version = new ClientVersion; | |
| 153 version->set_client(client); | |
| 154 version->set_version_name(version_name); | |
| 155 return version; | |
| 156 } | |
| 157 | |
| 158 void AddTokenToRequest(const AudioToken& token, ReportRequest* request) { | |
| 159 TokenObservation* token_observation = | |
| 160 request->mutable_update_signals_request()->add_token_observation(); | |
| 161 token_observation->set_token_id(ToUrlSafe(token.token)); | |
| 162 | |
| 163 TokenSignals* signals = token_observation->add_signals(); | |
| 164 signals->set_medium(token.audible ? AUDIO_AUDIBLE_DTMF | |
| 165 : AUDIO_ULTRASOUND_PASSBAND); | |
| 166 signals->set_observed_time_millis(base::Time::Now().ToJsTime()); | |
| 167 } | |
| 168 | |
| 169 } // namespace | |
| 170 | |
| 171 | |
| 172 // Public functions. | |
| 173 | |
| 174 RpcHandler::RpcHandler(CopresenceDelegate* delegate, | |
| 175 DirectiveHandler* directive_handler, | |
| 176 CopresenceStateImpl* state, | |
| 177 GCMHandler* gcm_handler, | |
| 178 const MessagesCallback& new_messages_callback, | |
| 179 const PostCallback& server_post_callback) | |
| 180 : delegate_(delegate), | |
| 181 directive_handler_(directive_handler), | |
| 182 state_(state), | |
| 183 gcm_handler_(gcm_handler), | |
| 184 new_messages_callback_(new_messages_callback), | |
| 185 server_post_callback_(server_post_callback), | |
| 186 invalid_audio_token_cache_( | |
| 187 base::TimeDelta::FromMinutes(kInvalidTokenExpiryTimeMinutes), | |
| 188 kMaxInvalidTokens) { | |
| 189 DCHECK(delegate_); | |
| 190 DCHECK(directive_handler_); | |
| 191 // |gcm_handler_| is optional. | |
| 192 | |
| 193 if (server_post_callback_.is_null()) { | |
| 194 server_post_callback_ = | |
| 195 base::Bind(&RpcHandler::SendHttpPost, base::Unretained(this)); | |
| 196 } | |
| 197 | |
| 198 if (gcm_handler_) { | |
| 199 gcm_handler_->GetGcmId( | |
| 200 base::Bind(&RpcHandler::RegisterGcmId, base::Unretained(this))); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 RpcHandler::~RpcHandler() { | |
| 205 // TODO(ckehoe): Cancel the GCM callback? | |
| 206 for (HttpPost* post : pending_posts_) | |
| 207 delete post; | |
| 208 } | |
| 209 | |
| 210 void RpcHandler::SendReportRequest(std::unique_ptr<ReportRequest> request, | |
| 211 const std::string& app_id, | |
| 212 const std::string& auth_token, | |
| 213 const StatusCallback& status_callback) { | |
| 214 DCHECK(request.get()); | |
| 215 | |
| 216 // Check that the app, if any, has some kind of authentication token. | |
| 217 // Don't allow it to piggyback on Chrome's credentials. | |
| 218 if (!app_id.empty() && delegate_->GetAPIKey(app_id).empty() && | |
| 219 auth_token.empty()) { | |
| 220 LOG(ERROR) << "App " << app_id << " has no API key or auth token"; | |
| 221 status_callback.Run(FAIL); | |
| 222 return; | |
| 223 } | |
| 224 | |
| 225 // Store just one auth token since we should have only one account | |
| 226 // per instance of the copresence component. | |
| 227 // TODO(ckehoe): We may eventually need to support multiple auth tokens. | |
| 228 const bool authenticated = !auth_token.empty(); | |
| 229 if (authenticated && auth_token != auth_token_) { | |
| 230 LOG_IF(ERROR, !auth_token_.empty()) | |
| 231 << "Overwriting old auth token: " << LoggingStrForToken(auth_token); | |
| 232 auth_token_ = auth_token; | |
| 233 } | |
| 234 | |
| 235 // Check that we have a "device" registered for this authentication state. | |
| 236 bool queue_request; | |
| 237 const std::string device_id = delegate_->GetDeviceId(authenticated); | |
| 238 if (device_id.empty()) { | |
| 239 queue_request = true; | |
| 240 if (pending_registrations_.count(authenticated) == 0) | |
| 241 RegisterDevice(authenticated); | |
| 242 // else, registration is already in progress. | |
| 243 } else { | |
| 244 queue_request = false; | |
| 245 } | |
| 246 | |
| 247 // We're not registered, or registration is in progress. | |
| 248 if (queue_request) { | |
| 249 pending_requests_queue_.push_back(new PendingRequest( | |
| 250 std::move(request), app_id, authenticated, status_callback)); | |
| 251 return; | |
| 252 } | |
| 253 | |
| 254 DVLOG(3) << "Sending ReportRequest to server."; | |
| 255 | |
| 256 // If we are unpublishing or unsubscribing, we need to stop those publish or | |
| 257 // subscribes right away, we don't need to wait for the server to tell us. | |
| 258 ProcessRemovedOperations(*request); | |
| 259 | |
| 260 request->mutable_update_signals_request()->set_allocated_state( | |
| 261 GetDeviceCapabilities(*request).release()); | |
| 262 | |
| 263 AddPlayingTokens(request.get()); | |
| 264 | |
| 265 request->set_allocated_header(CreateRequestHeader(app_id, device_id)); | |
| 266 server_post_callback_.Run( | |
| 267 delegate_->GetRequestContext(), kReportRequestRpcName, | |
| 268 delegate_->GetAPIKey(app_id), auth_token, | |
| 269 base::WrapUnique<MessageLite>(request.release()), | |
| 270 // On destruction, this request will be cancelled. | |
| 271 base::Bind(&RpcHandler::ReportResponseHandler, base::Unretained(this), | |
| 272 status_callback)); | |
| 273 } | |
| 274 | |
| 275 void RpcHandler::ReportTokens(const std::vector<AudioToken>& tokens) { | |
| 276 DCHECK(!tokens.empty()); | |
| 277 | |
| 278 std::unique_ptr<ReportRequest> request(new ReportRequest); | |
| 279 for (const AudioToken& token : tokens) { | |
| 280 if (invalid_audio_token_cache_.HasKey(ToUrlSafe(token.token))) | |
| 281 continue; | |
| 282 DVLOG(3) << "Sending token " << token.token << " to server"; | |
| 283 AddTokenToRequest(token, request.get()); | |
| 284 } | |
| 285 | |
| 286 ReportOnAllDevices(std::move(request)); | |
| 287 } | |
| 288 | |
| 289 | |
| 290 // Private functions. | |
| 291 | |
| 292 RpcHandler::PendingRequest::PendingRequest( | |
| 293 std::unique_ptr<ReportRequest> report, | |
| 294 const std::string& app_id, | |
| 295 bool authenticated, | |
| 296 const StatusCallback& callback) | |
| 297 : report(std::move(report)), | |
| 298 app_id(app_id), | |
| 299 authenticated(authenticated), | |
| 300 callback(callback) {} | |
| 301 | |
| 302 RpcHandler::PendingRequest::~PendingRequest() {} | |
| 303 | |
| 304 void RpcHandler::RegisterDevice(const bool authenticated) { | |
| 305 DVLOG(2) << "Sending " << (authenticated ? "authenticated" : "anonymous") | |
| 306 << " registration to server."; | |
| 307 | |
| 308 std::unique_ptr<RegisterDeviceRequest> request(new RegisterDeviceRequest); | |
| 309 | |
| 310 // Add a GCM ID for authenticated registration, if we have one. | |
| 311 if (!authenticated || gcm_id_.empty()) { | |
| 312 request->mutable_push_service()->set_service(PUSH_SERVICE_NONE); | |
| 313 } else { | |
| 314 DVLOG(2) << "Registering GCM ID with " << LoggingStrForToken(auth_token_); | |
| 315 request->mutable_push_service()->set_service(GCM); | |
| 316 request->mutable_push_service()->mutable_gcm_registration() | |
| 317 ->set_device_token(gcm_id_); | |
| 318 } | |
| 319 | |
| 320 // Only identify as a Chrome device if we're in anonymous mode. | |
| 321 // Authenticated calls come from a "GAIA device". | |
| 322 if (!authenticated) { | |
| 323 // Make sure this isn't a duplicate anonymous registration. | |
| 324 // Duplicate authenticated registrations are allowed, to update the GCM ID. | |
| 325 DCHECK(delegate_->GetDeviceId(false).empty()) | |
| 326 << "Attempted anonymous re-registration"; | |
| 327 | |
| 328 Identity* identity = | |
| 329 request->mutable_device_identifiers()->mutable_registrant(); | |
| 330 identity->set_type(CHROME); | |
| 331 } | |
| 332 | |
| 333 bool gcm_pending = authenticated && gcm_handler_ && gcm_id_.empty(); | |
| 334 pending_registrations_.insert(authenticated); | |
| 335 request->set_allocated_header(CreateRequestHeader( | |
| 336 // The device is empty on first registration. | |
| 337 // When re-registering to pass on the GCM ID, it will be present. | |
| 338 std::string(), delegate_->GetDeviceId(authenticated))); | |
| 339 if (authenticated) | |
| 340 DCHECK(!auth_token_.empty()); | |
| 341 server_post_callback_.Run( | |
| 342 delegate_->GetRequestContext(), kRegisterDeviceRpcName, std::string(), | |
| 343 authenticated ? auth_token_ : std::string(), | |
| 344 base::WrapUnique<MessageLite>(request.release()), | |
| 345 // On destruction, this request will be cancelled. | |
| 346 base::Bind(&RpcHandler::RegisterResponseHandler, base::Unretained(this), | |
| 347 authenticated, gcm_pending)); | |
| 348 } | |
| 349 | |
| 350 void RpcHandler::ProcessQueuedRequests(const bool authenticated) { | |
| 351 // If there is no device ID for this auth state, registration failed. | |
| 352 bool registration_failed = delegate_->GetDeviceId(authenticated).empty(); | |
| 353 | |
| 354 // We momentarily take ownership of all the pointers in the queue. | |
| 355 // They are either deleted here or passed on to a new queue. | |
| 356 ScopedVector<PendingRequest> requests_being_processed; | |
| 357 std::swap(requests_being_processed, pending_requests_queue_); | |
| 358 for (PendingRequest* request : requests_being_processed) { | |
| 359 if (request->authenticated == authenticated) { | |
| 360 if (registration_failed) { | |
| 361 request->callback.Run(FAIL); | |
| 362 } else { | |
| 363 if (request->authenticated) | |
| 364 DCHECK(!auth_token_.empty()); | |
| 365 SendReportRequest(std::move(request->report), request->app_id, | |
| 366 request->authenticated ? auth_token_ : std::string(), | |
| 367 request->callback); | |
| 368 } | |
| 369 delete request; | |
| 370 } else { | |
| 371 // The request is in a different auth state. | |
| 372 pending_requests_queue_.push_back(request); | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 // Only keep the requests that weren't processed. | |
| 377 // All the pointers in the queue are now spoken for. | |
| 378 requests_being_processed.weak_clear(); | |
| 379 } | |
| 380 | |
| 381 void RpcHandler::ReportOnAllDevices(std::unique_ptr<ReportRequest> request) { | |
| 382 std::vector<bool> auth_states; | |
| 383 if (!auth_token_.empty() && !delegate_->GetDeviceId(true).empty()) | |
| 384 auth_states.push_back(true); | |
| 385 if (!delegate_->GetDeviceId(false).empty()) | |
| 386 auth_states.push_back(false); | |
| 387 if (auth_states.empty()) { | |
| 388 VLOG(2) << "Skipping reporting because no device IDs are registered"; | |
| 389 return; | |
| 390 } | |
| 391 | |
| 392 for (bool authenticated : auth_states) { | |
| 393 SendReportRequest( | |
| 394 base::WrapUnique(new ReportRequest(*request)), std::string(), | |
| 395 authenticated ? auth_token_ : std::string(), StatusCallback()); | |
| 396 } | |
| 397 } | |
| 398 | |
| 399 // Store a GCM ID and send it to the server if needed. The constructor passes | |
| 400 // this callback to the GCMHandler to receive the ID whenever it's ready. | |
| 401 // It may be returned immediately, if the ID is cached, or require a server | |
| 402 // round-trip. This ID must then be passed along to the copresence server. | |
| 403 // There are a few ways this can happen: | |
| 404 // | |
| 405 // 1. The GCM ID is available when we first register, and is passed along | |
| 406 // with the RegisterDeviceRequest. | |
| 407 // | |
| 408 // 2. The GCM ID becomes available after the RegisterDeviceRequest has | |
| 409 // completed. Then this function will invoke RegisterDevice() | |
| 410 // again to pass on the ID. | |
| 411 // | |
| 412 // 3. The GCM ID becomes available after the RegisterDeviceRequest is sent, | |
| 413 // but before it completes. In this case, the gcm_pending flag is passed | |
| 414 // through to the RegisterResponseHandler, which invokes RegisterDevice() | |
| 415 // again to pass on the ID. This function must skip pending registrations, | |
| 416 // as the device ID will be empty. | |
| 417 // | |
| 418 // TODO(ckehoe): Add tests for these scenarios. | |
| 419 void RpcHandler::RegisterGcmId(const std::string& gcm_id) { | |
| 420 gcm_id_ = gcm_id; | |
| 421 if (!gcm_id.empty()) { | |
| 422 const std::string& device_id = delegate_->GetDeviceId(true); | |
| 423 if (!auth_token_.empty() && !device_id.empty()) | |
| 424 RegisterDevice(true); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 void RpcHandler::RegisterResponseHandler( | |
| 429 bool authenticated, | |
| 430 bool gcm_pending, | |
| 431 HttpPost* completed_post, | |
| 432 int http_status_code, | |
| 433 const std::string& response_data) { | |
| 434 if (completed_post) { | |
| 435 size_t elements_erased = pending_posts_.erase(completed_post); | |
| 436 DCHECK_GT(elements_erased, 0u); | |
| 437 } | |
| 438 | |
| 439 size_t registrations_completed = pending_registrations_.erase(authenticated); | |
| 440 DCHECK_GT(registrations_completed, 0u); | |
| 441 | |
| 442 RegisterDeviceResponse response; | |
| 443 const std::string token_str = | |
| 444 LoggingStrForToken(authenticated ? auth_token_ : std::string()); | |
| 445 if (http_status_code != net::HTTP_OK) { | |
| 446 // TODO(ckehoe): Retry registration if appropriate. | |
| 447 LOG(ERROR) << token_str << " device registration failed"; | |
| 448 } else if (!response.ParseFromString(response_data)) { | |
| 449 LOG(ERROR) << "Invalid RegisterDeviceResponse:\n" << response_data; | |
| 450 } else if (!IsErrorStatus(response.header().status())) { | |
| 451 const std::string& device_id = response.registered_device_id(); | |
| 452 DCHECK(!device_id.empty()); | |
| 453 delegate_->SaveDeviceId(authenticated, device_id); | |
| 454 DVLOG(2) << token_str << " device registration successful. Id: " | |
| 455 << device_id; | |
| 456 | |
| 457 // If we have a GCM ID now, and didn't before, pass it on to the server. | |
| 458 if (gcm_pending && !gcm_id_.empty()) | |
| 459 RegisterDevice(authenticated); | |
| 460 } | |
| 461 | |
| 462 // Send or fail requests on this auth token. | |
| 463 ProcessQueuedRequests(authenticated); | |
| 464 } | |
| 465 | |
| 466 void RpcHandler::ReportResponseHandler(const StatusCallback& status_callback, | |
| 467 HttpPost* completed_post, | |
| 468 int http_status_code, | |
| 469 const std::string& response_data) { | |
| 470 if (completed_post) { | |
| 471 size_t elements_erased = pending_posts_.erase(completed_post); | |
| 472 DCHECK_GT(elements_erased, 0u); | |
| 473 } | |
| 474 | |
| 475 if (http_status_code != net::HTTP_OK) { | |
| 476 if (!status_callback.is_null()) | |
| 477 status_callback.Run(FAIL); | |
| 478 return; | |
| 479 } | |
| 480 | |
| 481 DVLOG(3) << "Received ReportResponse."; | |
| 482 ReportResponse response; | |
| 483 if (!response.ParseFromString(response_data)) { | |
| 484 LOG(ERROR) << "Invalid ReportResponse"; | |
| 485 if (!status_callback.is_null()) | |
| 486 status_callback.Run(FAIL); | |
| 487 return; | |
| 488 } | |
| 489 | |
| 490 if (ReportErrorLogged(response)) { | |
| 491 if (!status_callback.is_null()) | |
| 492 status_callback.Run(FAIL); | |
| 493 return; | |
| 494 } | |
| 495 | |
| 496 for (const MessageResult& result : | |
| 497 response.manage_messages_response().published_message_result()) { | |
| 498 DVLOG(2) << "Published message with id " << result.published_message_id(); | |
| 499 } | |
| 500 | |
| 501 for (const SubscriptionResult& result : | |
| 502 response.manage_subscriptions_response().subscription_result()) { | |
| 503 DVLOG(2) << "Created subscription with id " << result.subscription_id(); | |
| 504 } | |
| 505 | |
| 506 if (response.has_update_signals_response()) { | |
| 507 const UpdateSignalsResponse& update_response = | |
| 508 response.update_signals_response(); | |
| 509 new_messages_callback_.Run(update_response.message()); | |
| 510 | |
| 511 for (const Directive& directive : update_response.directive()) | |
| 512 directive_handler_->AddDirective(directive); | |
| 513 | |
| 514 for (const Token& token : update_response.token()) { | |
| 515 if (state_) | |
| 516 state_->UpdateTokenStatus(token.id(), token.status()); | |
| 517 switch (token.status()) { | |
| 518 case VALID: | |
| 519 // TODO(rkc/ckehoe): Store the token in a |valid_token_cache_| with a | |
| 520 // short TTL (like 10s) and send it up with every report request. | |
| 521 // Then we'll still get messages while we're waiting to hear it again. | |
| 522 VLOG(1) << "Got valid token " << token.id(); | |
| 523 break; | |
| 524 case INVALID: | |
| 525 DVLOG(3) << "Discarding invalid token " << token.id(); | |
| 526 invalid_audio_token_cache_.Add(token.id(), true); | |
| 527 break; | |
| 528 default: | |
| 529 DVLOG(2) << "Token " << token.id() << " has status code " | |
| 530 << token.status(); | |
| 531 } | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 // TODO(ckehoe): Return a more detailed status response. | |
| 536 if (!status_callback.is_null()) | |
| 537 status_callback.Run(SUCCESS); | |
| 538 } | |
| 539 | |
| 540 void RpcHandler::ProcessRemovedOperations(const ReportRequest& request) { | |
| 541 // Remove unpublishes. | |
| 542 if (request.has_manage_messages_request()) { | |
| 543 for (const std::string& unpublish : | |
| 544 request.manage_messages_request().id_to_unpublish()) { | |
| 545 directive_handler_->RemoveDirectives(unpublish); | |
| 546 } | |
| 547 } | |
| 548 | |
| 549 // Remove unsubscribes. | |
| 550 if (request.has_manage_subscriptions_request()) { | |
| 551 for (const std::string& unsubscribe : | |
| 552 request.manage_subscriptions_request().id_to_unsubscribe()) { | |
| 553 directive_handler_->RemoveDirectives(unsubscribe); | |
| 554 } | |
| 555 } | |
| 556 } | |
| 557 | |
| 558 void RpcHandler::AddPlayingTokens(ReportRequest* request) { | |
| 559 const std::string& audible_token = | |
| 560 directive_handler_->GetCurrentAudioToken(AUDIBLE); | |
| 561 const std::string& inaudible_token = | |
| 562 directive_handler_->GetCurrentAudioToken(INAUDIBLE); | |
| 563 | |
| 564 if (!audible_token.empty()) | |
| 565 AddTokenToRequest(AudioToken(audible_token, true), request); | |
| 566 if (!inaudible_token.empty()) | |
| 567 AddTokenToRequest(AudioToken(inaudible_token, false), request); | |
| 568 } | |
| 569 | |
| 570 // TODO(ckehoe): Pass in the version string and | |
| 571 // group this with the local functions up top. | |
| 572 RequestHeader* RpcHandler::CreateRequestHeader( | |
| 573 const std::string& app_id, | |
| 574 const std::string& device_id) const { | |
| 575 RequestHeader* header = new RequestHeader; | |
| 576 | |
| 577 header->set_allocated_framework_version(CreateVersion( | |
| 578 "Chrome", delegate_->GetPlatformVersionString())); | |
| 579 if (!app_id.empty()) | |
| 580 header->set_allocated_client_version(CreateVersion(app_id, std::string())); | |
| 581 header->set_current_time_millis(base::Time::Now().ToJsTime()); | |
| 582 if (!device_id.empty()) | |
| 583 header->set_registered_device_id(device_id); | |
| 584 | |
| 585 DeviceFingerprint* fingerprint = new DeviceFingerprint; | |
| 586 fingerprint->set_platform_version(delegate_->GetPlatformVersionString()); | |
| 587 fingerprint->set_type(CHROME_PLATFORM_TYPE); | |
| 588 header->set_allocated_device_fingerprint(fingerprint); | |
| 589 | |
| 590 return header; | |
| 591 } | |
| 592 | |
| 593 void RpcHandler::SendHttpPost(net::URLRequestContextGetter* url_context_getter, | |
| 594 const std::string& rpc_name, | |
| 595 const std::string& api_key, | |
| 596 const std::string& auth_token, | |
| 597 std::unique_ptr<MessageLite> request_proto, | |
| 598 const PostCleanupCallback& callback) { | |
| 599 // Create the base URL to call. | |
| 600 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
| 601 const std::string copresence_server_host = | |
| 602 command_line->HasSwitch(switches::kCopresenceServer) ? | |
| 603 command_line->GetSwitchValueASCII(switches::kCopresenceServer) : | |
| 604 kDefaultCopresenceServer; | |
| 605 | |
| 606 // Create the request and keep a pointer until it completes. | |
| 607 HttpPost* http_post = new HttpPost( | |
| 608 url_context_getter, | |
| 609 copresence_server_host, | |
| 610 rpc_name, | |
| 611 api_key, | |
| 612 auth_token, | |
| 613 command_line->GetSwitchValueASCII(switches::kCopresenceTracingToken), | |
| 614 *request_proto); | |
| 615 | |
| 616 http_post->Start(base::Bind(callback, http_post)); | |
| 617 pending_posts_.insert(http_post); | |
| 618 } | |
| 619 | |
| 620 } // namespace copresence | |
| OLD | NEW |