• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/devtools/device/port_forwarding_controller.h"
6 
7 #include <algorithm>
8 #include <map>
9 
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/memory/singleton.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/threading/non_thread_safe.h"
19 #include "chrome/browser/devtools/devtools_protocol.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/keyed_service/content/browser_context_dependency_manager.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "net/base/address_list.h"
25 #include "net/base/io_buffer.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/net_util.h"
28 #include "net/dns/host_resolver.h"
29 #include "net/socket/tcp_client_socket.h"
30 
31 using content::BrowserThread;
32 
33 namespace {
34 
35 const int kBufferSize = 16 * 1024;
36 
37 enum {
38   kStatusError = -3,
39   kStatusDisconnecting = -2,
40   kStatusConnecting = -1,
41   kStatusOK = 0,
42   // Positive values are used to count open connections.
43 };
44 
45 static const char kPortAttribute[] = "port";
46 static const char kConnectionIdAttribute[] = "connectionId";
47 static const char kTetheringAccepted[] = "Tethering.accepted";
48 static const char kTetheringBind[] = "Tethering.bind";
49 static const char kTetheringUnbind[] = "Tethering.unbind";
50 
51 static const char kDevToolsRemoteBrowserTarget[] = "/devtools/browser";
52 const int kMinVersionPortForwarding = 28;
53 
54 class SocketTunnel : public base::NonThreadSafe {
55  public:
56   typedef base::Callback<void(int)> CounterCallback;
57 
StartTunnel(const std::string & host,int port,const CounterCallback & callback,int result,net::StreamSocket * socket)58   static void StartTunnel(const std::string& host,
59                           int port,
60                           const CounterCallback& callback,
61                           int result,
62                           net::StreamSocket* socket) {
63     if (result < 0)
64       return;
65     SocketTunnel* tunnel = new SocketTunnel(callback);
66     tunnel->Start(socket, host, port);
67   }
68 
69  private:
SocketTunnel(const CounterCallback & callback)70   explicit SocketTunnel(const CounterCallback& callback)
71       : pending_writes_(0),
72         pending_destruction_(false),
73         callback_(callback),
74         about_to_destroy_(false) {
75     callback_.Run(1);
76   }
77 
Start(net::StreamSocket * socket,const std::string & host,int port)78   void Start(net::StreamSocket* socket, const std::string& host, int port) {
79     remote_socket_.reset(socket);
80 
81     host_resolver_ = net::HostResolver::CreateDefaultResolver(NULL);
82     net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
83     int result = host_resolver_->Resolve(
84         request_info,
85         net::DEFAULT_PRIORITY,
86         &address_list_,
87         base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
88         NULL,
89         net::BoundNetLog());
90     if (result != net::ERR_IO_PENDING)
91       OnResolved(result);
92   }
93 
OnResolved(int result)94   void OnResolved(int result) {
95     if (result < 0) {
96       SelfDestruct();
97       return;
98     }
99 
100     host_socket_.reset(new net::TCPClientSocket(address_list_, NULL,
101                                                 net::NetLog::Source()));
102     result = host_socket_->Connect(base::Bind(&SocketTunnel::OnConnected,
103                                               base::Unretained(this)));
104     if (result != net::ERR_IO_PENDING)
105       OnConnected(result);
106   }
107 
~SocketTunnel()108   ~SocketTunnel() {
109     about_to_destroy_ = true;
110     if (host_socket_)
111       host_socket_->Disconnect();
112     if (remote_socket_)
113       remote_socket_->Disconnect();
114     callback_.Run(-1);
115   }
116 
OnConnected(int result)117   void OnConnected(int result) {
118     if (result < 0) {
119       SelfDestruct();
120       return;
121     }
122 
123     ++pending_writes_; // avoid SelfDestruct in first Pump
124     Pump(host_socket_.get(), remote_socket_.get());
125     --pending_writes_;
126     if (pending_destruction_) {
127       SelfDestruct();
128     } else {
129       Pump(remote_socket_.get(), host_socket_.get());
130     }
131   }
132 
Pump(net::StreamSocket * from,net::StreamSocket * to)133   void Pump(net::StreamSocket* from, net::StreamSocket* to) {
134     scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
135     int result = from->Read(
136         buffer.get(),
137         kBufferSize,
138         base::Bind(
139             &SocketTunnel::OnRead, base::Unretained(this), from, to, buffer));
140     if (result != net::ERR_IO_PENDING)
141       OnRead(from, to, buffer, result);
142   }
143 
OnRead(net::StreamSocket * from,net::StreamSocket * to,scoped_refptr<net::IOBuffer> buffer,int result)144   void OnRead(net::StreamSocket* from,
145               net::StreamSocket* to,
146               scoped_refptr<net::IOBuffer> buffer,
147               int result) {
148     if (result <= 0) {
149       SelfDestruct();
150       return;
151     }
152 
153     int total = result;
154     scoped_refptr<net::DrainableIOBuffer> drainable =
155         new net::DrainableIOBuffer(buffer.get(), total);
156 
157     ++pending_writes_;
158     result = to->Write(drainable.get(),
159                        total,
160                        base::Bind(&SocketTunnel::OnWritten,
161                                   base::Unretained(this),
162                                   drainable,
163                                   from,
164                                   to));
165     if (result != net::ERR_IO_PENDING)
166       OnWritten(drainable, from, to, result);
167   }
168 
OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,net::StreamSocket * from,net::StreamSocket * to,int result)169   void OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,
170                  net::StreamSocket* from,
171                  net::StreamSocket* to,
172                  int result) {
173     --pending_writes_;
174     if (result < 0) {
175       SelfDestruct();
176       return;
177     }
178 
179     drainable->DidConsume(result);
180     if (drainable->BytesRemaining() > 0) {
181       ++pending_writes_;
182       result = to->Write(drainable.get(),
183                          drainable->BytesRemaining(),
184                          base::Bind(&SocketTunnel::OnWritten,
185                                     base::Unretained(this),
186                                     drainable,
187                                     from,
188                                     to));
189       if (result != net::ERR_IO_PENDING)
190         OnWritten(drainable, from, to, result);
191       return;
192     }
193 
194     if (pending_destruction_) {
195       SelfDestruct();
196       return;
197     }
198     Pump(from, to);
199   }
200 
SelfDestruct()201   void SelfDestruct() {
202     // In case one of the connections closes, we could get here
203     // from another one due to Disconnect firing back on all
204     // read callbacks.
205     if (about_to_destroy_)
206       return;
207     if (pending_writes_ > 0) {
208       pending_destruction_ = true;
209       return;
210     }
211     delete this;
212   }
213 
214   scoped_ptr<net::StreamSocket> remote_socket_;
215   scoped_ptr<net::StreamSocket> host_socket_;
216   scoped_ptr<net::HostResolver> host_resolver_;
217   net::AddressList address_list_;
218   int pending_writes_;
219   bool pending_destruction_;
220   CounterCallback callback_;
221   bool about_to_destroy_;
222 };
223 
224 typedef DevToolsAndroidBridge::RemoteBrowser::ParsedVersion ParsedVersion;
225 
IsVersionLower(const ParsedVersion & left,const ParsedVersion & right)226 static bool IsVersionLower(const ParsedVersion& left,
227                            const ParsedVersion& right) {
228   return std::lexicographical_compare(
229     left.begin(), left.end(), right.begin(), right.end());
230 }
231 
IsPortForwardingSupported(const ParsedVersion & version)232 static bool IsPortForwardingSupported(const ParsedVersion& version) {
233   return !version.empty() && version[0] >= kMinVersionPortForwarding;
234 }
235 
236 static scoped_refptr<DevToolsAndroidBridge::RemoteBrowser>
FindBestBrowserForTethering(const DevToolsAndroidBridge::RemoteBrowsers browsers)237 FindBestBrowserForTethering(
238     const DevToolsAndroidBridge::RemoteBrowsers browsers) {
239   scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> best_browser;
240   ParsedVersion newest_version;
241   for (DevToolsAndroidBridge::RemoteBrowsers::const_iterator it =
242       browsers.begin(); it != browsers.end(); ++it) {
243     scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser = *it;
244     ParsedVersion current_version = browser->GetParsedVersion();
245     if (IsPortForwardingSupported(current_version) &&
246         IsVersionLower(newest_version, current_version)) {
247       best_browser = browser;
248       newest_version = current_version;
249     }
250   }
251   return best_browser;
252 }
253 
254 }  // namespace
255 
256 class PortForwardingController::Connection
257     : public DevToolsAndroidBridge::AndroidWebSocket::Delegate,
258       public base::RefCountedThreadSafe<
259           Connection,
260           content::BrowserThread::DeleteOnUIThread> {
261  public:
262   Connection(Registry* registry,
263              scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,
264              scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
265              const ForwardingMap& forwarding_map);
266 
267   const PortStatusMap& GetPortStatusMap();
268 
269   void UpdateForwardingMap(const ForwardingMap& new_forwarding_map);
270 
271   void Shutdown();
272 
273  private:
274   friend struct content::BrowserThread::DeleteOnThread<
275       content::BrowserThread::UI>;
276   friend class base::DeleteHelper<Connection>;
277 
278   virtual ~Connection();
279 
280   typedef std::map<int, std::string> ForwardingMap;
281 
282   typedef base::Callback<void(PortStatus)> CommandCallback;
283   typedef std::map<int, CommandCallback> CommandCallbackMap;
284 
285   void SerializeChanges(const std::string& method,
286                         const ForwardingMap& old_map,
287                         const ForwardingMap& new_map);
288 
289   void SendCommand(const std::string& method, int port);
290   bool ProcessResponse(const std::string& json);
291 
292   void ProcessBindResponse(int port, PortStatus status);
293   void ProcessUnbindResponse(int port, PortStatus status);
294 
295   void UpdateSocketCountOnHandlerThread(int port, int increment);
296   void UpdateSocketCount(int port, int increment);
297 
298   // DevToolsAndroidBridge::AndroidWebSocket::Delegate implementation:
299   virtual void OnSocketOpened() OVERRIDE;
300   virtual void OnFrameRead(const std::string& message) OVERRIDE;
301   virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
302 
303   PortForwardingController::Registry* registry_;
304   scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device_;
305   scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
306   scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
307   int command_id_;
308   bool connected_;
309   ForwardingMap forwarding_map_;
310   CommandCallbackMap pending_responses_;
311   PortStatusMap port_status_;
312 
313   DISALLOW_COPY_AND_ASSIGN(Connection);
314 };
315 
Connection(Registry * registry,scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,const ForwardingMap & forwarding_map)316 PortForwardingController::Connection::Connection(
317     Registry* registry,
318     scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,
319     scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
320     const ForwardingMap& forwarding_map)
321     : registry_(registry),
322       device_(device),
323       browser_(browser),
324       command_id_(0),
325       connected_(false),
326       forwarding_map_(forwarding_map) {
327   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328   (*registry_)[device_->serial()] = this;
329   web_socket_ = browser->CreateWebSocket(kDevToolsRemoteBrowserTarget, this);
330   web_socket_->Connect();
331   AddRef();  // Balanced in OnSocketClosed();
332 }
333 
Shutdown()334 void PortForwardingController::Connection::Shutdown() {
335   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
336   registry_ = NULL;
337   // This will have no effect if the socket is not connected yet.
338   web_socket_->Disconnect();
339 }
340 
~Connection()341 PortForwardingController::Connection::~Connection() {
342   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343   if (registry_) {
344     DCHECK(registry_->find(device_->serial()) != registry_->end());
345     registry_->erase(device_->serial());
346   }
347 }
348 
UpdateForwardingMap(const ForwardingMap & new_forwarding_map)349 void PortForwardingController::Connection::UpdateForwardingMap(
350     const ForwardingMap& new_forwarding_map) {
351   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
352   if (connected_) {
353     SerializeChanges(kTetheringUnbind, new_forwarding_map, forwarding_map_);
354     SerializeChanges(kTetheringBind, forwarding_map_, new_forwarding_map);
355   }
356   forwarding_map_ = new_forwarding_map;
357 }
358 
SerializeChanges(const std::string & method,const ForwardingMap & old_map,const ForwardingMap & new_map)359 void PortForwardingController::Connection::SerializeChanges(
360     const std::string& method,
361     const ForwardingMap& old_map,
362     const ForwardingMap& new_map) {
363   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364   for (ForwardingMap::const_iterator new_it(new_map.begin());
365       new_it != new_map.end(); ++new_it) {
366     int port = new_it->first;
367     const std::string& location = new_it->second;
368     ForwardingMap::const_iterator old_it = old_map.find(port);
369     if (old_it != old_map.end() && old_it->second == location)
370       continue;  // The port points to the same location in both configs, skip.
371 
372     SendCommand(method, port);
373   }
374 }
375 
SendCommand(const std::string & method,int port)376 void PortForwardingController::Connection::SendCommand(
377     const std::string& method, int port) {
378   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
379   base::DictionaryValue params;
380   params.SetInteger(kPortAttribute, port);
381   DevToolsProtocol::Command command(++command_id_, method, &params);
382 
383   if (method == kTetheringBind) {
384     pending_responses_[command.id()] =
385         base::Bind(&Connection::ProcessBindResponse,
386                    base::Unretained(this), port);
387 #if defined(DEBUG_DEVTOOLS)
388     port_status_[port] = kStatusConnecting;
389 #endif  // defined(DEBUG_DEVTOOLS)
390   } else {
391     DCHECK_EQ(kTetheringUnbind, method);
392 
393     PortStatusMap::iterator it = port_status_.find(port);
394     if (it != port_status_.end() && it->second == kStatusError) {
395       // The bind command failed on this port, do not attempt unbind.
396       port_status_.erase(it);
397       return;
398     }
399 
400     pending_responses_[command.id()] =
401         base::Bind(&Connection::ProcessUnbindResponse,
402                    base::Unretained(this), port);
403 #if defined(DEBUG_DEVTOOLS)
404     port_status_[port] = kStatusDisconnecting;
405 #endif  // defined(DEBUG_DEVTOOLS)
406   }
407 
408   web_socket_->SendFrame(command.Serialize());
409 }
410 
ProcessResponse(const std::string & message)411 bool PortForwardingController::Connection::ProcessResponse(
412     const std::string& message) {
413   scoped_ptr<DevToolsProtocol::Response> response(
414       DevToolsProtocol::ParseResponse(message));
415   if (!response)
416     return false;
417 
418   CommandCallbackMap::iterator it = pending_responses_.find(response->id());
419   if (it == pending_responses_.end())
420     return false;
421 
422   it->second.Run(response->error_code() ? kStatusError : kStatusOK);
423   pending_responses_.erase(it);
424   return true;
425 }
426 
ProcessBindResponse(int port,PortStatus status)427 void PortForwardingController::Connection::ProcessBindResponse(
428     int port, PortStatus status) {
429   port_status_[port] = status;
430 }
431 
ProcessUnbindResponse(int port,PortStatus status)432 void PortForwardingController::Connection::ProcessUnbindResponse(
433     int port, PortStatus status) {
434   PortStatusMap::iterator it = port_status_.find(port);
435   if (it == port_status_.end())
436     return;
437   if (status == kStatusError)
438     it->second = status;
439   else
440     port_status_.erase(it);
441 }
442 
UpdateSocketCountOnHandlerThread(int port,int increment)443 void PortForwardingController::Connection::UpdateSocketCountOnHandlerThread(
444     int port, int increment) {
445   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
446      base::Bind(&Connection::UpdateSocketCount, this, port, increment));
447 }
448 
UpdateSocketCount(int port,int increment)449 void PortForwardingController::Connection::UpdateSocketCount(
450     int port, int increment) {
451 #if defined(DEBUG_DEVTOOLS)
452   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453   PortStatusMap::iterator it = port_status_.find(port);
454   if (it == port_status_.end())
455     return;
456   if (it->second < 0 || (it->second == 0 && increment < 0))
457     return;
458   it->second += increment;
459 #endif  // defined(DEBUG_DEVTOOLS)
460 }
461 
462 const PortForwardingController::PortStatusMap&
GetPortStatusMap()463 PortForwardingController::Connection::GetPortStatusMap() {
464   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
465   return port_status_;
466 }
467 
OnSocketOpened()468 void PortForwardingController::Connection::OnSocketOpened() {
469   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
470   if (!registry_) {
471     // Socket was created after Shutdown was called. Disconnect immediately.
472     web_socket_->Disconnect();
473     return;
474   }
475   connected_ = true;
476   SerializeChanges(kTetheringBind, ForwardingMap(), forwarding_map_);
477 }
478 
OnSocketClosed(bool closed_by_device)479 void PortForwardingController::Connection::OnSocketClosed(
480     bool closed_by_device) {
481   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
482   Release();  // Balanced in the constructor.
483 }
484 
OnFrameRead(const std::string & message)485 void PortForwardingController::Connection::OnFrameRead(
486     const std::string& message) {
487   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
488   if (ProcessResponse(message))
489     return;
490 
491   scoped_ptr<DevToolsProtocol::Notification> notification(
492       DevToolsProtocol::ParseNotification(message));
493   if (!notification)
494     return;
495 
496   if (notification->method() != kTetheringAccepted)
497     return;
498 
499   base::DictionaryValue* params = notification->params();
500   if (!params)
501     return;
502 
503   int port;
504   std::string connection_id;
505   if (!params->GetInteger(kPortAttribute, &port) ||
506       !params->GetString(kConnectionIdAttribute, &connection_id))
507     return;
508 
509   std::map<int, std::string>::iterator it = forwarding_map_.find(port);
510   if (it == forwarding_map_.end())
511     return;
512 
513   std::string location = it->second;
514   std::vector<std::string> tokens;
515   Tokenize(location, ":", &tokens);
516   int destination_port = 0;
517   if (tokens.size() != 2 || !base::StringToInt(tokens[1], &destination_port))
518     return;
519   std::string destination_host = tokens[0];
520 
521   SocketTunnel::CounterCallback callback =
522       base::Bind(&Connection::UpdateSocketCountOnHandlerThread, this, port);
523 
524   device_->OpenSocket(
525       connection_id.c_str(),
526       base::Bind(&SocketTunnel::StartTunnel,
527                  destination_host,
528                  destination_port,
529                  callback));
530 }
531 
PortForwardingController(Profile * profile)532 PortForwardingController::PortForwardingController(Profile* profile)
533     : profile_(profile),
534       pref_service_(profile->GetPrefs()),
535       listening_(false) {
536   pref_change_registrar_.Init(pref_service_);
537   base::Closure callback = base::Bind(
538       &PortForwardingController::OnPrefsChange, base::Unretained(this));
539   pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, callback);
540   pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, callback);
541   OnPrefsChange();
542 }
543 
544 
~PortForwardingController()545 PortForwardingController::~PortForwardingController() {}
546 
Shutdown()547 void PortForwardingController::Shutdown() {
548   // Existing connection will not be shut down. This might be confusing for
549   // some users, but the opposite is more confusing.
550   StopListening();
551 }
552 
AddListener(Listener * listener)553 void PortForwardingController::AddListener(Listener* listener) {
554   listeners_.push_back(listener);
555 }
556 
RemoveListener(Listener * listener)557 void PortForwardingController::RemoveListener(Listener* listener) {
558   Listeners::iterator it =
559       std::find(listeners_.begin(), listeners_.end(), listener);
560   DCHECK(it != listeners_.end());
561   listeners_.erase(it);
562 }
563 
DeviceListChanged(const DevToolsAndroidBridge::RemoteDevices & devices)564 void PortForwardingController::DeviceListChanged(
565     const DevToolsAndroidBridge::RemoteDevices& devices) {
566   DevicesStatus status;
567 
568   for (DevToolsAndroidBridge::RemoteDevices::const_iterator it =
569       devices.begin(); it != devices.end(); ++it) {
570     scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device = *it;
571     if (!device->is_connected())
572       continue;
573     Registry::iterator rit = registry_.find(device->serial());
574     if (rit == registry_.end()) {
575       scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser =
576           FindBestBrowserForTethering(device->browsers());
577       if (browser) {
578         new Connection(&registry_, device, browser, forwarding_map_);
579       }
580     } else {
581       status[device->serial()] = (*rit).second->GetPortStatusMap();
582     }
583   }
584 
585   NotifyListeners(status);
586 }
587 
OnPrefsChange()588 void PortForwardingController::OnPrefsChange() {
589   forwarding_map_.clear();
590 
591   if (pref_service_->GetBoolean(prefs::kDevToolsPortForwardingEnabled)) {
592     const base::DictionaryValue* dict =
593         pref_service_->GetDictionary(prefs::kDevToolsPortForwardingConfig);
594     for (base::DictionaryValue::Iterator it(*dict);
595          !it.IsAtEnd(); it.Advance()) {
596       int port_num;
597       std::string location;
598       if (base::StringToInt(it.key(), &port_num) &&
599           dict->GetString(it.key(), &location))
600         forwarding_map_[port_num] = location;
601     }
602   }
603 
604   if (!forwarding_map_.empty()) {
605     StartListening();
606     UpdateConnections();
607   } else {
608     StopListening();
609     ShutdownConnections();
610     NotifyListeners(DevicesStatus());
611   }
612 }
613 
StartListening()614 void PortForwardingController::StartListening() {
615   if (listening_)
616     return;
617   listening_ = true;
618   DevToolsAndroidBridge* android_bridge =
619       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
620   if (android_bridge)
621     android_bridge->AddDeviceListListener(this);
622 
623 }
624 
StopListening()625 void PortForwardingController::StopListening() {
626   if (!listening_)
627     return;
628   listening_ = false;
629   DevToolsAndroidBridge* android_bridge =
630       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
631   if (android_bridge)
632     android_bridge->RemoveDeviceListListener(this);
633 }
634 
UpdateConnections()635 void PortForwardingController::UpdateConnections() {
636   for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
637     it->second->UpdateForwardingMap(forwarding_map_);
638 }
639 
ShutdownConnections()640 void PortForwardingController::ShutdownConnections() {
641   for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
642     it->second->Shutdown();
643   registry_.clear();
644 }
645 
NotifyListeners(const DevicesStatus & status) const646 void PortForwardingController::NotifyListeners(
647     const DevicesStatus& status) const {
648   Listeners copy(listeners_);  // Iterate over copy.
649   for (Listeners::const_iterator it = copy.begin(); it != copy.end(); ++it)
650     (*it)->PortStatusChanged(status);
651 }
652 
653 // static
654 PortForwardingController::Factory*
GetInstance()655 PortForwardingController::Factory::GetInstance() {
656   return Singleton<PortForwardingController::Factory>::get();
657 }
658 
659 // static
GetForProfile(Profile * profile)660 PortForwardingController* PortForwardingController::Factory::GetForProfile(
661     Profile* profile) {
662   return static_cast<PortForwardingController*>(GetInstance()->
663           GetServiceForBrowserContext(profile, true));
664 }
665 
Factory()666 PortForwardingController::Factory::Factory()
667     : BrowserContextKeyedServiceFactory(
668           "PortForwardingController",
669           BrowserContextDependencyManager::GetInstance()) {}
670 
~Factory()671 PortForwardingController::Factory::~Factory() {}
672 
BuildServiceInstanceFor(content::BrowserContext * context) const673 KeyedService* PortForwardingController::Factory::BuildServiceInstanceFor(
674     content::BrowserContext* context) const {
675   Profile* profile = Profile::FromBrowserContext(context);
676   return new PortForwardingController(profile);
677 }
678