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, ¶ms);
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(®istry_, 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