| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/dial/dial_service.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <set> | |
| 11 #include <utility> | |
| 12 | |
| 13 #include "base/callback.h" | |
| 14 #include "base/location.h" | |
| 15 #include "base/logging.h" | |
| 16 #include "base/memory/ptr_util.h" | |
| 17 #include "base/rand_util.h" | |
| 18 #include "base/single_thread_task_runner.h" | |
| 19 #include "base/strings/string_number_conversions.h" | |
| 20 #include "base/strings/stringprintf.h" | |
| 21 #include "base/threading/thread_task_runner_handle.h" | |
| 22 #include "base/time/time.h" | |
| 23 #include "build/build_config.h" | |
| 24 #include "chrome/browser/extensions/api/dial/dial_device_data.h" | |
| 25 #include "components/version_info/version_info.h" | |
| 26 #include "content/public/browser/browser_thread.h" | |
| 27 #include "net/base/address_family.h" | |
| 28 #include "net/base/completion_callback.h" | |
| 29 #include "net/base/io_buffer.h" | |
| 30 #include "net/base/ip_endpoint.h" | |
| 31 #include "net/base/net_errors.h" | |
| 32 #include "net/base/network_interfaces.h" | |
| 33 #include "net/http/http_response_headers.h" | |
| 34 #include "net/http/http_util.h" | |
| 35 #include "net/log/net_log.h" | |
| 36 #include "net/log/net_log_source_type.h" | |
| 37 #include "url/gurl.h" | |
| 38 | |
| 39 #if defined(OS_CHROMEOS) | |
| 40 #include "chromeos/network/network_state.h" | |
| 41 #include "chromeos/network/network_state_handler.h" | |
| 42 #include "third_party/cros_system_api/dbus/service_constants.h" | |
| 43 #endif | |
| 44 | |
| 45 using base::Time; | |
| 46 using base::TimeDelta; | |
| 47 using content::BrowserThread; | |
| 48 using net::HttpResponseHeaders; | |
| 49 using net::HttpUtil; | |
| 50 using net::IOBufferWithSize; | |
| 51 using net::IPAddress; | |
| 52 using net::NetworkInterface; | |
| 53 using net::NetworkInterfaceList; | |
| 54 using net::StringIOBuffer; | |
| 55 using net::UDPSocket; | |
| 56 | |
| 57 namespace extensions { | |
| 58 namespace api { | |
| 59 namespace dial { | |
| 60 | |
| 61 namespace { | |
| 62 | |
| 63 // The total number of requests to make per discovery cycle. | |
| 64 const int kDialMaxRequests = 4; | |
| 65 | |
| 66 // The interval to wait between successive requests. | |
| 67 const int kDialRequestIntervalMillis = 1000; | |
| 68 | |
| 69 // The maximum delay a device may wait before responding (MX). | |
| 70 const int kDialMaxResponseDelaySecs = 1; | |
| 71 | |
| 72 // The maximum time a response is expected after a M-SEARCH request. | |
| 73 const int kDialResponseTimeoutSecs = 2; | |
| 74 | |
| 75 // The multicast IP address for discovery. | |
| 76 const char kDialRequestAddress[] = "239.255.255.250"; | |
| 77 | |
| 78 // The UDP port number for discovery. | |
| 79 const uint16_t kDialRequestPort = 1900; | |
| 80 | |
| 81 // The DIAL service type as part of the search request. | |
| 82 const char kDialSearchType[] = "urn:dial-multiscreen-org:service:dial:1"; | |
| 83 | |
| 84 // SSDP headers parsed from the response. | |
| 85 const char kSsdpLocationHeader[] = "LOCATION"; | |
| 86 const char kSsdpCacheControlHeader[] = "CACHE-CONTROL"; | |
| 87 const char kSsdpConfigIdHeader[] = "CONFIGID.UPNP.ORG"; | |
| 88 const char kSsdpUsnHeader[] = "USN"; | |
| 89 | |
| 90 // The receive buffer size, in bytes. | |
| 91 const int kDialRecvBufferSize = 1500; | |
| 92 | |
| 93 // Gets a specific header from |headers| and puts it in |value|. | |
| 94 bool GetHeader(HttpResponseHeaders* headers, const char* name, | |
| 95 std::string* value) { | |
| 96 return headers->EnumerateHeader(nullptr, std::string(name), value); | |
| 97 } | |
| 98 | |
| 99 // Returns the request string. | |
| 100 std::string BuildRequest() { | |
| 101 // Extra line at the end to make UPnP lib happy. | |
| 102 std::string request(base::StringPrintf( | |
| 103 "M-SEARCH * HTTP/1.1\r\n" | |
| 104 "HOST: %s:%u\r\n" | |
| 105 "MAN: \"ssdp:discover\"\r\n" | |
| 106 "MX: %d\r\n" | |
| 107 "ST: %s\r\n" | |
| 108 "USER-AGENT: %s/%s %s\r\n" | |
| 109 "\r\n", | |
| 110 kDialRequestAddress, | |
| 111 kDialRequestPort, | |
| 112 kDialMaxResponseDelaySecs, | |
| 113 kDialSearchType, | |
| 114 version_info::GetProductName().c_str(), | |
| 115 version_info::GetVersionNumber().c_str(), | |
| 116 version_info::GetOSType().c_str())); | |
| 117 // 1500 is a good MTU value for most Ethernet LANs. | |
| 118 DCHECK_LE(request.size(), 1500U); | |
| 119 return request; | |
| 120 } | |
| 121 | |
| 122 #if defined(OS_CHROMEOS) | |
| 123 // Finds the IP address of the preferred interface of network type |type| | |
| 124 // to bind the socket and inserts the address into |bind_address_list|. This | |
| 125 // ChromeOS version can prioritize wifi and ethernet interfaces. | |
| 126 void InsertBestBindAddressChromeOS(const chromeos::NetworkTypePattern& type, | |
| 127 net::IPAddressList* bind_address_list) { | |
| 128 const chromeos::NetworkState* state = chromeos::NetworkHandler::Get() | |
| 129 ->network_state_handler()->ConnectedNetworkByType(type); | |
| 130 IPAddress bind_ip_address; | |
| 131 if (state && bind_ip_address.AssignFromIPLiteral(state->ip_address()) && | |
| 132 bind_ip_address.IsIPv4()) { | |
| 133 VLOG(2) << "Found " << state->type() << ", " << state->name() << ": " | |
| 134 << state->ip_address(); | |
| 135 bind_address_list->push_back(bind_ip_address); | |
| 136 } | |
| 137 } | |
| 138 #else | |
| 139 NetworkInterfaceList GetNetworkListOnFileThread() { | |
| 140 NetworkInterfaceList list; | |
| 141 bool success = | |
| 142 net::GetNetworkList(&list, net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES); | |
| 143 if (!success) | |
| 144 VLOG(1) << "Could not retrieve network list!"; | |
| 145 return list; | |
| 146 } | |
| 147 #endif // defined(OS_CHROMEOS) | |
| 148 | |
| 149 } // namespace | |
| 150 | |
| 151 DialServiceImpl::DialSocket::DialSocket( | |
| 152 const base::Closure& discovery_request_cb, | |
| 153 const base::Callback<void(const DialDeviceData&)>& device_discovered_cb, | |
| 154 const base::Closure& on_error_cb) | |
| 155 : discovery_request_cb_(discovery_request_cb), | |
| 156 device_discovered_cb_(device_discovered_cb), | |
| 157 on_error_cb_(on_error_cb), | |
| 158 is_writing_(false), | |
| 159 is_reading_(false) { | |
| 160 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 161 } | |
| 162 | |
| 163 DialServiceImpl::DialSocket::~DialSocket() { | |
| 164 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 165 } | |
| 166 | |
| 167 bool DialServiceImpl::DialSocket::CreateAndBindSocket( | |
| 168 const IPAddress& bind_ip_address, | |
| 169 net::NetLog* net_log, | |
| 170 net::NetLogSource net_log_source) { | |
| 171 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 172 DCHECK(!socket_); | |
| 173 DCHECK(bind_ip_address.IsIPv4()); | |
| 174 | |
| 175 net::RandIntCallback rand_cb = base::Bind(&base::RandInt); | |
| 176 socket_ = base::MakeUnique<UDPSocket>(net::DatagramSocket::RANDOM_BIND, | |
| 177 rand_cb, net_log, net_log_source); | |
| 178 | |
| 179 // 0 means bind a random port | |
| 180 net::IPEndPoint address(bind_ip_address, 0); | |
| 181 | |
| 182 if (socket_->Open(address.GetFamily()) != net::OK || | |
| 183 socket_->SetBroadcast(true) != net::OK || | |
| 184 !CheckResult("Bind", socket_->Bind(address))) { | |
| 185 socket_.reset(); | |
| 186 return false; | |
| 187 } | |
| 188 | |
| 189 recv_buffer_ = new IOBufferWithSize(kDialRecvBufferSize); | |
| 190 return ReadSocket(); | |
| 191 } | |
| 192 | |
| 193 void DialServiceImpl::DialSocket::SendOneRequest( | |
| 194 const net::IPEndPoint& send_address, | |
| 195 const scoped_refptr<net::StringIOBuffer>& send_buffer) { | |
| 196 if (!socket_) { | |
| 197 VLOG(1) << "Socket not connected."; | |
| 198 return; | |
| 199 } | |
| 200 | |
| 201 if (is_writing_) { | |
| 202 VLOG(1) << "Already writing."; | |
| 203 return; | |
| 204 } | |
| 205 | |
| 206 is_writing_ = true; | |
| 207 int result = socket_->SendTo( | |
| 208 send_buffer.get(), send_buffer->size(), send_address, | |
| 209 base::Bind(&DialServiceImpl::DialSocket::OnSocketWrite, | |
| 210 base::Unretained(this), | |
| 211 send_buffer->size())); | |
| 212 bool result_ok = CheckResult("SendTo", result); | |
| 213 if (result_ok && result > 0) { | |
| 214 // Synchronous write. | |
| 215 OnSocketWrite(send_buffer->size(), result); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 bool DialServiceImpl::DialSocket::IsClosed() { | |
| 220 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 221 return !socket_; | |
| 222 } | |
| 223 | |
| 224 bool DialServiceImpl::DialSocket::CheckResult(const char* operation, | |
| 225 int result) { | |
| 226 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 227 VLOG(2) << "Operation " << operation << " result " << result; | |
| 228 if (result < net::OK && result != net::ERR_IO_PENDING) { | |
| 229 Close(); | |
| 230 std::string error_str(net::ErrorToString(result)); | |
| 231 VLOG(1) << "dial socket error: " << error_str; | |
| 232 on_error_cb_.Run(); | |
| 233 return false; | |
| 234 } | |
| 235 return true; | |
| 236 } | |
| 237 | |
| 238 void DialServiceImpl::DialSocket::Close() { | |
| 239 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 240 is_reading_ = false; | |
| 241 is_writing_ = false; | |
| 242 socket_.reset(); | |
| 243 } | |
| 244 | |
| 245 void DialServiceImpl::DialSocket::OnSocketWrite(int send_buffer_size, | |
| 246 int result) { | |
| 247 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 248 is_writing_ = false; | |
| 249 if (!CheckResult("OnSocketWrite", result)) | |
| 250 return; | |
| 251 if (result != send_buffer_size) { | |
| 252 VLOG(1) << "Sent " << result << " chars, expected " | |
| 253 << send_buffer_size << " chars"; | |
| 254 } | |
| 255 discovery_request_cb_.Run(); | |
| 256 } | |
| 257 | |
| 258 bool DialServiceImpl::DialSocket::ReadSocket() { | |
| 259 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 260 if (!socket_) { | |
| 261 VLOG(1) << "Socket not connected."; | |
| 262 return false; | |
| 263 } | |
| 264 | |
| 265 if (is_reading_) { | |
| 266 VLOG(1) << "Already reading."; | |
| 267 return false; | |
| 268 } | |
| 269 | |
| 270 int result = net::OK; | |
| 271 bool result_ok = true; | |
| 272 do { | |
| 273 is_reading_ = true; | |
| 274 result = socket_->RecvFrom( | |
| 275 recv_buffer_.get(), | |
| 276 kDialRecvBufferSize, &recv_address_, | |
| 277 base::Bind(&DialServiceImpl::DialSocket::OnSocketRead, | |
| 278 base::Unretained(this))); | |
| 279 result_ok = CheckResult("RecvFrom", result); | |
| 280 if (result != net::ERR_IO_PENDING) | |
| 281 is_reading_ = false; | |
| 282 if (result_ok && result > 0) { | |
| 283 // Synchronous read. | |
| 284 HandleResponse(result); | |
| 285 } | |
| 286 } while (result_ok && result != net::OK && result != net::ERR_IO_PENDING); | |
| 287 return result_ok; | |
| 288 } | |
| 289 | |
| 290 void DialServiceImpl::DialSocket::OnSocketRead(int result) { | |
| 291 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 292 is_reading_ = false; | |
| 293 if (!CheckResult("OnSocketRead", result)) | |
| 294 return; | |
| 295 if (result > 0) | |
| 296 HandleResponse(result); | |
| 297 | |
| 298 // Await next response. | |
| 299 ReadSocket(); | |
| 300 } | |
| 301 | |
| 302 void DialServiceImpl::DialSocket::HandleResponse(int bytes_read) { | |
| 303 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 304 DCHECK_GT(bytes_read, 0); | |
| 305 if (bytes_read > kDialRecvBufferSize) { | |
| 306 VLOG(1) << bytes_read << " > " << kDialRecvBufferSize << "!?"; | |
| 307 return; | |
| 308 } | |
| 309 VLOG(2) << "Read " << bytes_read << " bytes from " | |
| 310 << recv_address_.ToString(); | |
| 311 | |
| 312 std::string response(recv_buffer_->data(), bytes_read); | |
| 313 Time response_time = Time::Now(); | |
| 314 | |
| 315 // Attempt to parse response, notify observers if successful. | |
| 316 DialDeviceData parsed_device; | |
| 317 if (ParseResponse(response, response_time, &parsed_device)) | |
| 318 device_discovered_cb_.Run(parsed_device); | |
| 319 } | |
| 320 | |
| 321 // static | |
| 322 bool DialServiceImpl::DialSocket::ParseResponse( | |
| 323 const std::string& response, | |
| 324 const base::Time& response_time, | |
| 325 DialDeviceData* device) { | |
| 326 int headers_end = HttpUtil::LocateEndOfHeaders(response.c_str(), | |
| 327 response.size()); | |
| 328 if (headers_end < 1) { | |
| 329 VLOG(1) << "Headers invalid or empty, ignoring: " << response; | |
| 330 return false; | |
| 331 } | |
| 332 std::string raw_headers = | |
| 333 HttpUtil::AssembleRawHeaders(response.c_str(), headers_end); | |
| 334 VLOG(3) << "raw_headers: " << raw_headers << "\n"; | |
| 335 scoped_refptr<HttpResponseHeaders> headers = | |
| 336 new HttpResponseHeaders(raw_headers); | |
| 337 | |
| 338 std::string device_url_str; | |
| 339 if (!GetHeader(headers.get(), kSsdpLocationHeader, &device_url_str) || | |
| 340 device_url_str.empty()) { | |
| 341 VLOG(1) << "No LOCATION header found."; | |
| 342 return false; | |
| 343 } | |
| 344 | |
| 345 GURL device_url(device_url_str); | |
| 346 if (!DialDeviceData::IsDeviceDescriptionUrl(device_url)) { | |
| 347 VLOG(1) << "URL " << device_url_str << " not valid."; | |
| 348 return false; | |
| 349 } | |
| 350 | |
| 351 std::string device_id; | |
| 352 if (!GetHeader(headers.get(), kSsdpUsnHeader, &device_id) || | |
| 353 device_id.empty()) { | |
| 354 VLOG(1) << "No USN header found."; | |
| 355 return false; | |
| 356 } | |
| 357 | |
| 358 device->set_device_id(device_id); | |
| 359 device->set_device_description_url(device_url); | |
| 360 device->set_response_time(response_time); | |
| 361 | |
| 362 // TODO(mfoltz): Parse the max-age value from the cache control header. | |
| 363 // http://crbug.com/165289 | |
| 364 std::string cache_control; | |
| 365 GetHeader(headers.get(), kSsdpCacheControlHeader, &cache_control); | |
| 366 | |
| 367 std::string config_id; | |
| 368 int config_id_int; | |
| 369 if (GetHeader(headers.get(), kSsdpConfigIdHeader, &config_id) && | |
| 370 base::StringToInt(config_id, &config_id_int)) { | |
| 371 device->set_config_id(config_id_int); | |
| 372 } else { | |
| 373 VLOG(1) << "Malformed or missing " << kSsdpConfigIdHeader << ": " | |
| 374 << config_id; | |
| 375 } | |
| 376 | |
| 377 return true; | |
| 378 } | |
| 379 | |
| 380 DialServiceImpl::DialServiceImpl(net::NetLog* net_log) | |
| 381 : net_log_(net_log), | |
| 382 discovery_active_(false), | |
| 383 num_requests_sent_(0), | |
| 384 max_requests_(kDialMaxRequests), | |
| 385 finish_delay_(TimeDelta::FromMilliseconds((kDialMaxRequests - 1) * | |
| 386 kDialRequestIntervalMillis) + | |
| 387 TimeDelta::FromSeconds(kDialResponseTimeoutSecs)), | |
| 388 request_interval_( | |
| 389 TimeDelta::FromMilliseconds(kDialRequestIntervalMillis)), | |
| 390 weak_factory_(this) { | |
| 391 IPAddress address; | |
| 392 bool success = address.AssignFromIPLiteral(kDialRequestAddress); | |
| 393 DCHECK(success); | |
| 394 send_address_ = net::IPEndPoint(address, kDialRequestPort); | |
| 395 send_buffer_ = new StringIOBuffer(BuildRequest()); | |
| 396 net_log_source_.type = net::NetLogSourceType::UDP_SOCKET; | |
| 397 net_log_source_.id = net_log_->NextID(); | |
| 398 } | |
| 399 | |
| 400 DialServiceImpl::~DialServiceImpl() { | |
| 401 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 402 } | |
| 403 | |
| 404 void DialServiceImpl::AddObserver(Observer* observer) { | |
| 405 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 406 observer_list_.AddObserver(observer); | |
| 407 } | |
| 408 | |
| 409 void DialServiceImpl::RemoveObserver(Observer* observer) { | |
| 410 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 411 observer_list_.RemoveObserver(observer); | |
| 412 } | |
| 413 | |
| 414 bool DialServiceImpl::HasObserver(const Observer* observer) const { | |
| 415 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 416 return observer_list_.HasObserver(observer); | |
| 417 } | |
| 418 | |
| 419 bool DialServiceImpl::Discover() { | |
| 420 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 421 if (discovery_active_) { | |
| 422 VLOG(2) << "Discovery is already active - returning."; | |
| 423 return false; | |
| 424 } | |
| 425 discovery_active_ = true; | |
| 426 | |
| 427 VLOG(2) << "Discovery started."; | |
| 428 | |
| 429 StartDiscovery(); | |
| 430 return true; | |
| 431 } | |
| 432 | |
| 433 void DialServiceImpl::StartDiscovery() { | |
| 434 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 435 DCHECK(discovery_active_); | |
| 436 if (HasOpenSockets()) { | |
| 437 VLOG(2) << "Calling StartDiscovery() with open sockets. Returning."; | |
| 438 return; | |
| 439 } | |
| 440 | |
| 441 #if defined(OS_CHROMEOS) | |
| 442 // The ChromeOS specific version of getting network interfaces does not | |
| 443 // require trampolining to another thread, and contains additional interface | |
| 444 // information such as interface types (i.e. wifi vs cellular). | |
| 445 net::IPAddressList chrome_os_address_list; | |
| 446 InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::Ethernet(), | |
| 447 &chrome_os_address_list); | |
| 448 InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::WiFi(), | |
| 449 &chrome_os_address_list); | |
| 450 DiscoverOnAddresses(chrome_os_address_list); | |
| 451 | |
| 452 #else | |
| 453 BrowserThread::PostTaskAndReplyWithResult( | |
| 454 BrowserThread::FILE, FROM_HERE, base::Bind(&GetNetworkListOnFileThread), | |
| 455 base::Bind(&DialServiceImpl::SendNetworkList, | |
| 456 weak_factory_.GetWeakPtr())); | |
| 457 #endif | |
| 458 } | |
| 459 | |
| 460 void DialServiceImpl::SendNetworkList(const NetworkInterfaceList& networks) { | |
| 461 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 462 using InterfaceIndexAddressFamily = std::pair<uint32_t, net::AddressFamily>; | |
| 463 std::set<InterfaceIndexAddressFamily> interface_index_addr_family_seen; | |
| 464 net::IPAddressList ip_addresses; | |
| 465 | |
| 466 // Binds a socket to each IPv4 network interface found. Note that | |
| 467 // there may be duplicates in |networks|, so address family + interface index | |
| 468 // is used to identify unique interfaces. | |
| 469 // TODO(mfoltz): Support IPV6 multicast. http://crbug.com/165286 | |
| 470 for (NetworkInterfaceList::const_iterator iter = networks.begin(); | |
| 471 iter != networks.end(); ++iter) { | |
| 472 net::AddressFamily addr_family = net::GetAddressFamily(iter->address); | |
| 473 VLOG(2) << "Found " << iter->name << ", " << iter->address.ToString() | |
| 474 << ", address family: " << addr_family; | |
| 475 if (addr_family == net::ADDRESS_FAMILY_IPV4) { | |
| 476 InterfaceIndexAddressFamily interface_index_addr_family = | |
| 477 std::make_pair(iter->interface_index, addr_family); | |
| 478 bool inserted = interface_index_addr_family_seen | |
| 479 .insert(interface_index_addr_family) | |
| 480 .second; | |
| 481 // We have not seen this interface before, so add its IP address to the | |
| 482 // discovery list. | |
| 483 if (inserted) { | |
| 484 VLOG(2) << "Encountered " | |
| 485 << "interface index: " << iter->interface_index << ", " | |
| 486 << "address family: " << addr_family << " for the first time, " | |
| 487 << "adding IP address " << iter->address.ToString() | |
| 488 << " to list."; | |
| 489 ip_addresses.push_back(iter->address); | |
| 490 } else { | |
| 491 VLOG(2) << "Already encountered " | |
| 492 << "interface index: " << iter->interface_index << ", " | |
| 493 << "address family: " << addr_family << " before, not adding."; | |
| 494 } | |
| 495 } | |
| 496 } | |
| 497 | |
| 498 DiscoverOnAddresses(ip_addresses); | |
| 499 } | |
| 500 | |
| 501 void DialServiceImpl::DiscoverOnAddresses( | |
| 502 const net::IPAddressList& ip_addresses) { | |
| 503 if (ip_addresses.empty()) { | |
| 504 VLOG(1) << "Could not find a valid interface to bind. Finishing discovery"; | |
| 505 FinishDiscovery(); | |
| 506 return; | |
| 507 } | |
| 508 | |
| 509 // Schedule a timer to finish the discovery process (and close the sockets). | |
| 510 if (finish_delay_ > TimeDelta::FromSeconds(0)) { | |
| 511 VLOG(2) << "Starting timer to finish discovery."; | |
| 512 finish_timer_.Start(FROM_HERE, | |
| 513 finish_delay_, | |
| 514 this, | |
| 515 &DialServiceImpl::FinishDiscovery); | |
| 516 } | |
| 517 | |
| 518 for (const auto& address : ip_addresses) { | |
| 519 BindAndAddSocket(address); | |
| 520 } | |
| 521 | |
| 522 SendOneRequest(); | |
| 523 } | |
| 524 | |
| 525 void DialServiceImpl::BindAndAddSocket(const IPAddress& bind_ip_address) { | |
| 526 std::unique_ptr<DialServiceImpl::DialSocket> dial_socket(CreateDialSocket()); | |
| 527 if (dial_socket->CreateAndBindSocket(bind_ip_address, net_log_, | |
| 528 net_log_source_)) | |
| 529 dial_sockets_.push_back(std::move(dial_socket)); | |
| 530 } | |
| 531 | |
| 532 std::unique_ptr<DialServiceImpl::DialSocket> | |
| 533 DialServiceImpl::CreateDialSocket() { | |
| 534 return base::MakeUnique<DialServiceImpl::DialSocket>( | |
| 535 base::Bind(&DialServiceImpl::NotifyOnDiscoveryRequest, | |
| 536 weak_factory_.GetWeakPtr()), | |
| 537 base::Bind(&DialServiceImpl::NotifyOnDeviceDiscovered, | |
| 538 weak_factory_.GetWeakPtr()), | |
| 539 base::Bind(&DialServiceImpl::NotifyOnError, weak_factory_.GetWeakPtr())); | |
| 540 } | |
| 541 | |
| 542 void DialServiceImpl::SendOneRequest() { | |
| 543 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 544 if (num_requests_sent_ == max_requests_) { | |
| 545 VLOG(2) << "Reached max requests; stopping request timer."; | |
| 546 request_timer_.Stop(); | |
| 547 return; | |
| 548 } | |
| 549 num_requests_sent_++; | |
| 550 VLOG(2) << "Sending request " << num_requests_sent_ << "/" | |
| 551 << max_requests_; | |
| 552 for (const auto& socket : dial_sockets_) { | |
| 553 if (!socket->IsClosed()) | |
| 554 socket->SendOneRequest(send_address_, send_buffer_); | |
| 555 } | |
| 556 } | |
| 557 | |
| 558 void DialServiceImpl::NotifyOnDiscoveryRequest() { | |
| 559 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 560 // If discovery is inactive, no reason to notify observers. | |
| 561 if (!discovery_active_) { | |
| 562 VLOG(2) << "Request sent after discovery finished. Ignoring."; | |
| 563 return; | |
| 564 } | |
| 565 | |
| 566 VLOG(2) << "Notifying observers of discovery request"; | |
| 567 for (auto& observer : observer_list_) | |
| 568 observer.OnDiscoveryRequest(this); | |
| 569 // If we need to send additional requests, schedule a timer to do so. | |
| 570 if (num_requests_sent_ < max_requests_ && num_requests_sent_ == 1) { | |
| 571 VLOG(2) << "Scheduling timer to send additional requests"; | |
| 572 // TODO(imcheng): Move this to SendOneRequest() once the implications are | |
| 573 // understood. | |
| 574 request_timer_.Start(FROM_HERE, | |
| 575 request_interval_, | |
| 576 this, | |
| 577 &DialServiceImpl::SendOneRequest); | |
| 578 } | |
| 579 } | |
| 580 | |
| 581 void DialServiceImpl::NotifyOnDeviceDiscovered( | |
| 582 const DialDeviceData& device_data) { | |
| 583 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 584 if (!discovery_active_) { | |
| 585 VLOG(2) << "Got response after discovery finished. Ignoring."; | |
| 586 return; | |
| 587 } | |
| 588 for (auto& observer : observer_list_) | |
| 589 observer.OnDeviceDiscovered(this, device_data); | |
| 590 } | |
| 591 | |
| 592 void DialServiceImpl::NotifyOnError() { | |
| 593 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 594 // TODO(imcheng): Modify upstream so that the device list is not cleared | |
| 595 // when it could still potentially discover devices on other sockets. | |
| 596 for (auto& observer : observer_list_) { | |
| 597 observer.OnError(this, HasOpenSockets() ? DIAL_SERVICE_SOCKET_ERROR | |
| 598 : DIAL_SERVICE_NO_INTERFACES); | |
| 599 } | |
| 600 } | |
| 601 | |
| 602 void DialServiceImpl::FinishDiscovery() { | |
| 603 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 604 DCHECK(discovery_active_); | |
| 605 VLOG(2) << "Discovery finished."; | |
| 606 // Close all open sockets. | |
| 607 dial_sockets_.clear(); | |
| 608 finish_timer_.Stop(); | |
| 609 request_timer_.Stop(); | |
| 610 discovery_active_ = false; | |
| 611 num_requests_sent_ = 0; | |
| 612 for (auto& observer : observer_list_) | |
| 613 observer.OnDiscoveryFinished(this); | |
| 614 } | |
| 615 | |
| 616 bool DialServiceImpl::HasOpenSockets() { | |
| 617 for (const auto& socket : dial_sockets_) { | |
| 618 if (!socket->IsClosed()) | |
| 619 return true; | |
| 620 } | |
| 621 return false; | |
| 622 } | |
| 623 | |
| 624 } // namespace dial | |
| 625 } // namespace api | |
| 626 } // namespace extensions | |
| OLD | NEW |