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.
5 #include "chrome/browser/devtools/device/devtools_android_bridge.h"
11 #include "base/base64.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/compiler_specific.h"
15 #include "base/json/json_reader.h"
16 #include "base/lazy_instance.h"
17 #include "base/memory/singleton.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/threading/thread.h"
26 #include "base/values.h"
27 #include "chrome/browser/devtools/device/adb/adb_device_provider.h"
28 #include "chrome/browser/devtools/device/port_forwarding_controller.h"
29 #include "chrome/browser/devtools/device/tcp_device_provider.h"
30 #include "chrome/browser/devtools/device/usb/usb_device_provider.h"
31 #include "chrome/browser/devtools/device/webrtc/webrtc_device_provider.h"
32 #include "chrome/browser/devtools/devtools_protocol.h"
33 #include "chrome/browser/devtools/devtools_target_impl.h"
34 #include "chrome/browser/devtools/devtools_window.h"
35 #include "chrome/browser/devtools/remote_debugging_server.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
38 #include "chrome/browser/signin/signin_manager_factory.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/pref_names.h"
41 #include "components/keyed_service/content/browser_context_dependency_manager.h"
42 #include "components/signin/core/browser/profile_oauth2_token_service.h"
43 #include "components/signin/core/browser/signin_manager.h"
44 #include "content/public/browser/devtools_agent_host.h"
45 #include "content/public/browser/devtools_external_agent_proxy.h"
46 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
47 #include "content/public/browser/user_metrics.h"
48 #include "net/base/escape.h"
49 #include "net/base/host_port_pair.h"
50 #include "net/base/net_errors.h"
52 using content::BrowserThread
;
56 const char kPageListRequest
[] = "/json";
57 const char kVersionRequest
[] = "/json/version";
58 const char kClosePageRequest
[] = "/json/close/%s";
59 const char kNewPageRequestWithURL
[] = "/json/new?%s";
60 const char kActivatePageRequest
[] = "/json/activate/%s";
61 const char kBrowserTargetSocket
[] = "/devtools/browser";
62 const int kAdbPollingIntervalMs
= 1000;
64 const char kPageReloadCommand
[] = "Page.reload";
66 const char kWebViewSocketPrefix
[] = "webview_devtools_remote";
68 bool IsWebRTCDeviceProviderEnabled() {
69 return base::CommandLine::ForCurrentProcess()->HasSwitch(
70 switches::kEnableDevToolsExperiments
);
73 bool BrowserIdFromString(const std::string
& browser_id_str
,
74 DevToolsAndroidBridge::BrowserId
* browser_id
) {
75 size_t colon_pos
= browser_id_str
.find(':');
76 if (colon_pos
== std::string::npos
)
78 browser_id
->first
= browser_id_str
.substr(0, colon_pos
);
79 browser_id
->second
= browser_id_str
.substr(colon_pos
+ 1);
85 // DiscoveryRequest -----------------------------------------------------
87 class DevToolsAndroidBridge::DiscoveryRequest
88 : public base::RefCountedThreadSafe
<DiscoveryRequest
,
89 BrowserThread::DeleteOnUIThread
> {
91 DiscoveryRequest(AndroidDeviceManager
* device_manager
,
92 const DeviceListCallback
& callback
);
94 friend struct BrowserThread::DeleteOnThread
<BrowserThread::UI
>;
95 friend class base::DeleteHelper
<DiscoveryRequest
>;
96 virtual ~DiscoveryRequest();
98 void ReceivedDevices(const AndroidDeviceManager::Devices
& devices
);
99 void ReceivedDeviceInfo(scoped_refptr
<AndroidDeviceManager::Device
> device
,
100 const AndroidDeviceManager::DeviceInfo
& device_info
);
101 void ReceivedVersion(scoped_refptr
<RemoteBrowser
>,
103 const std::string
& response
);
104 void ReceivedPages(scoped_refptr
<RemoteBrowser
>,
106 const std::string
& response
);
108 DeviceListCallback callback_
;
109 CompleteDevices complete_devices_
;
112 DevToolsAndroidBridge::DiscoveryRequest::DiscoveryRequest(
113 AndroidDeviceManager
* device_manager
,
114 const DeviceListCallback
& callback
)
115 : callback_(callback
) {
116 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
117 device_manager
->QueryDevices(
118 base::Bind(&DiscoveryRequest::ReceivedDevices
, this));
121 DevToolsAndroidBridge::DiscoveryRequest::~DiscoveryRequest() {
122 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
123 callback_
.Run(complete_devices_
);
126 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDevices(
127 const AndroidDeviceManager::Devices
& devices
) {
128 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
129 for (const auto& device
: devices
) {
130 device
->QueryDeviceInfo(
131 base::Bind(&DiscoveryRequest::ReceivedDeviceInfo
, this, device
));
135 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDeviceInfo(
136 scoped_refptr
<AndroidDeviceManager::Device
> device
,
137 const AndroidDeviceManager::DeviceInfo
& device_info
) {
138 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
139 scoped_refptr
<RemoteDevice
> remote_device
=
140 new RemoteDevice(device
->serial(), device_info
);
141 complete_devices_
.push_back(std::make_pair(device
, remote_device
));
142 for (RemoteBrowsers::iterator it
= remote_device
->browsers().begin();
143 it
!= remote_device
->browsers().end(); ++it
) {
144 device
->SendJsonRequest(
147 base::Bind(&DiscoveryRequest::ReceivedVersion
, this, *it
));
148 device
->SendJsonRequest(
151 base::Bind(&DiscoveryRequest::ReceivedPages
, this, *it
));
155 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedVersion(
156 scoped_refptr
<RemoteBrowser
> browser
,
158 const std::string
& response
) {
159 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
162 // Parse version, append to package name if available,
163 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(response
);
164 base::DictionaryValue
* dict
;
165 if (value
&& value
->GetAsDictionary(&dict
)) {
166 std::string browser_name
;
167 if (dict
->GetString("Browser", &browser_name
)) {
168 std::vector
<std::string
> parts
= base::SplitString(
169 browser_name
, "/", base::KEEP_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
170 if (parts
.size() == 2)
171 browser
->version_
= parts
[1];
173 browser
->version_
= browser_name
;
176 if (dict
->GetString("Android-Package", &package
)) {
177 browser
->display_name_
=
178 AndroidDeviceManager::GetBrowserName(browser
->socket(), package
);
183 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedPages(
184 scoped_refptr
<RemoteBrowser
> browser
,
186 const std::string
& response
) {
187 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
190 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(response
);
191 base::ListValue
* list_value
;
192 if (value
&& value
->GetAsList(&list_value
)) {
193 for (const auto& page_value
: *list_value
) {
194 base::DictionaryValue
* dict
;
195 if (page_value
->GetAsDictionary(&dict
))
196 browser
->pages_
.push_back(new RemotePage(browser
->browser_id_
, *dict
));
201 // ProtocolCommand ------------------------------------------------------------
205 class ProtocolCommand
206 : public AndroidDeviceManager::AndroidWebSocket::Delegate
{
209 scoped_refptr
<AndroidDeviceManager::Device
> device
,
210 const std::string
& socket
,
211 const std::string
& target_path
,
212 const std::string
& command
,
213 const base::Closure callback
);
216 void OnSocketOpened() override
;
217 void OnFrameRead(const std::string
& message
) override
;
218 void OnSocketClosed() override
;
219 ~ProtocolCommand() override
;
221 const std::string command_
;
222 const base::Closure callback_
;
223 scoped_ptr
<AndroidDeviceManager::AndroidWebSocket
> web_socket_
;
225 DISALLOW_COPY_AND_ASSIGN(ProtocolCommand
);
228 ProtocolCommand::ProtocolCommand(
229 scoped_refptr
<AndroidDeviceManager::Device
> device
,
230 const std::string
& socket
,
231 const std::string
& target_path
,
232 const std::string
& command
,
233 const base::Closure callback
)
236 web_socket_(device
->CreateWebSocket(socket
, target_path
, this)) {
239 void ProtocolCommand::OnSocketOpened() {
240 web_socket_
->SendFrame(command_
);
243 void ProtocolCommand::OnFrameRead(const std::string
& message
) {
247 void ProtocolCommand::OnSocketClosed() {
251 ProtocolCommand::~ProtocolCommand() {
252 if (!callback_
.is_null())
259 DevToolsAndroidBridge::Factory
* DevToolsAndroidBridge::Factory::GetInstance() {
260 return Singleton
<DevToolsAndroidBridge::Factory
>::get();
264 DevToolsAndroidBridge
* DevToolsAndroidBridge::Factory::GetForProfile(
266 return static_cast<DevToolsAndroidBridge
*>(GetInstance()->
267 GetServiceForBrowserContext(profile
, true));
270 DevToolsAndroidBridge::Factory::Factory()
271 : BrowserContextKeyedServiceFactory(
272 "DevToolsAndroidBridge",
273 BrowserContextDependencyManager::GetInstance()) {
274 if (IsWebRTCDeviceProviderEnabled()) {
275 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
276 DependsOn(SigninManagerFactory::GetInstance());
280 DevToolsAndroidBridge::Factory::~Factory() {}
282 KeyedService
* DevToolsAndroidBridge::Factory::BuildServiceInstanceFor(
283 content::BrowserContext
* context
) const {
284 Profile
* profile
= Profile::FromBrowserContext(context
);
286 ProfileOAuth2TokenService
* token_service
= nullptr;
287 SigninManagerBase
* signin_manager
= nullptr;
289 if (IsWebRTCDeviceProviderEnabled()) {
290 token_service
= ProfileOAuth2TokenServiceFactory::GetForProfile(profile
);
291 signin_manager
= SigninManagerFactory::GetForProfile(profile
);
294 return new DevToolsAndroidBridge(
295 profile
, signin_manager
, token_service
);
298 // AgentHostDelegate ----------------------------------------------------------
300 class DevToolsAndroidBridge::AgentHostDelegate
301 : public content::DevToolsExternalAgentProxyDelegate
,
302 public AndroidDeviceManager::AndroidWebSocket::Delegate
{
304 static scoped_refptr
<content::DevToolsAgentHost
> GetOrCreateAgentHost(
305 DevToolsAndroidBridge
* bridge
,
306 const std::string
& id
,
307 const BrowserId
& browser_id
,
308 const std::string
& target_path
);
312 DevToolsAndroidBridge
* bridge
,
313 const std::string
& id
,
314 const BrowserId
& browser_id
,
315 const std::string
& target_path
);
316 ~AgentHostDelegate() override
;
317 void Attach(content::DevToolsExternalAgentProxy
* proxy
) override
;
318 void Detach() override
;
319 void SendMessageToBackend(const std::string
& message
) override
;
320 void OnSocketOpened() override
;
321 void OnFrameRead(const std::string
& message
) override
;
322 void OnSocketClosed() override
;
325 base::WeakPtr
<DevToolsAndroidBridge
> bridge_
;
326 BrowserId browser_id_
;
327 std::string target_path_
;
329 std::vector
<std::string
> pending_messages_
;
330 scoped_refptr
<AndroidDeviceManager::Device
> device_
;
331 scoped_ptr
<AndroidDeviceManager::AndroidWebSocket
> web_socket_
;
332 content::DevToolsAgentHost
* agent_host_
;
333 content::DevToolsExternalAgentProxy
* proxy_
;
334 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate
);
338 scoped_refptr
<content::DevToolsAgentHost
>
339 DevToolsAndroidBridge::AgentHostDelegate::GetOrCreateAgentHost(
340 DevToolsAndroidBridge
* bridge
,
341 const std::string
& id
,
342 const BrowserId
& browser_id
,
343 const std::string
& target_path
) {
344 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
345 AgentHostDelegates::iterator it
= bridge
->host_delegates_
.find(id
);
346 if (it
!= bridge
->host_delegates_
.end())
347 return it
->second
->agent_host_
;
349 AgentHostDelegate
* delegate
=
350 new AgentHostDelegate(bridge
, id
, browser_id
, target_path
);
351 scoped_refptr
<content::DevToolsAgentHost
> result
=
352 content::DevToolsAgentHost::Create(delegate
);
353 delegate
->agent_host_
= result
.get();
357 DevToolsAndroidBridge::AgentHostDelegate::AgentHostDelegate(
358 DevToolsAndroidBridge
* bridge
,
359 const std::string
& id
,
360 const BrowserId
& browser_id
,
361 const std::string
& target_path
)
363 bridge_(bridge
->AsWeakPtr()),
364 browser_id_(browser_id
),
365 target_path_(target_path
),
366 socket_opened_(false),
369 bridge_
->host_delegates_
[id
] = this;
372 DevToolsAndroidBridge::AgentHostDelegate::~AgentHostDelegate() {
374 bridge_
->host_delegates_
.erase(id_
);
377 void DevToolsAndroidBridge::AgentHostDelegate::Attach(
378 content::DevToolsExternalAgentProxy
* proxy
) {
380 content::RecordAction(browser_id_
.second
.find(kWebViewSocketPrefix
) == 0 ?
381 base::UserMetricsAction("DevTools_InspectAndroidWebView") :
382 base::UserMetricsAction("DevTools_InspectAndroidPage"));
384 // Retain the device so it's not released until AgentHost is detached.
386 device_
= bridge_
->FindDevice(browser_id_
.first
);
391 device_
->CreateWebSocket(browser_id_
.second
, target_path_
, this));
394 void DevToolsAndroidBridge::AgentHostDelegate::Detach() {
399 void DevToolsAndroidBridge::AgentHostDelegate::SendMessageToBackend(
400 const std::string
& message
) {
402 web_socket_
->SendFrame(message
);
404 pending_messages_
.push_back(message
);
407 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketOpened() {
408 socket_opened_
= true;
409 for (std::vector
<std::string
>::iterator it
= pending_messages_
.begin();
410 it
!= pending_messages_
.end(); ++it
) {
411 SendMessageToBackend(*it
);
413 pending_messages_
.clear();
416 void DevToolsAndroidBridge::AgentHostDelegate::OnFrameRead(
417 const std::string
& message
) {
419 proxy_
->DispatchOnClientHost(message
);
422 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketClosed() {
424 proxy_
->ConnectionClosed();
427 //// RemotePageTarget ----------------------------------------------
429 class DevToolsAndroidBridge::RemotePageTarget
: public DevToolsTargetImpl
{
431 RemotePageTarget(DevToolsAndroidBridge
* bridge
,
432 const BrowserId
& browser_id
,
433 const base::DictionaryValue
& value
);
434 ~RemotePageTarget() override
;
436 // DevToolsTargetImpl overrides.
437 std::string
GetId() const override
;
438 bool IsAttached() const override
;
439 bool Activate() const override
;
440 bool Close() const override
;
441 void Inspect(Profile
* profile
) const override
;
442 void Reload() const override
;
445 base::WeakPtr
<DevToolsAndroidBridge
> bridge_
;
446 BrowserId browser_id_
;
447 std::string target_path_
;
448 std::string frontend_url_
;
449 std::string remote_id_
;
450 std::string remote_type_
;
451 std::string local_id_
;
452 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget
);
455 static std::string
GetStringProperty(const base::DictionaryValue
& value
,
456 const std::string
& name
) {
458 value
.GetString(name
, &result
);
462 static std::string
BuildUniqueTargetId(
463 const DevToolsAndroidBridge::BrowserId
& browser_id
,
464 const base::DictionaryValue
& value
) {
465 return base::StringPrintf("%s:%s:%s", browser_id
.first
.c_str(),
466 browser_id
.second
.c_str(), GetStringProperty(value
, "id").c_str());
469 static std::string
GetFrontendURL(const base::DictionaryValue
& value
) {
470 std::string frontend_url
= GetStringProperty(value
, "devtoolsFrontendUrl");
471 size_t ws_param
= frontend_url
.find("?ws");
472 if (ws_param
!= std::string::npos
)
473 frontend_url
= frontend_url
.substr(0, ws_param
);
474 if (frontend_url
.find("http:") == 0)
475 frontend_url
= "https:" + frontend_url
.substr(5);
479 static std::string
GetTargetPath(const base::DictionaryValue
& value
) {
480 std::string target_path
= GetStringProperty(value
, "webSocketDebuggerUrl");
482 if (target_path
.find("ws://") == 0) {
483 size_t pos
= target_path
.find("/", 5);
484 if (pos
== std::string::npos
)
486 target_path
= target_path
.substr(pos
);
488 target_path
= std::string();
493 DevToolsAndroidBridge::RemotePageTarget::RemotePageTarget(
494 DevToolsAndroidBridge
* bridge
,
495 const BrowserId
& browser_id
,
496 const base::DictionaryValue
& value
)
497 : DevToolsTargetImpl(AgentHostDelegate::GetOrCreateAgentHost(
499 BuildUniqueTargetId(browser_id
, value
),
501 GetTargetPath(value
))),
502 bridge_(bridge
->AsWeakPtr()),
503 browser_id_(browser_id
),
504 target_path_(GetTargetPath(value
)),
505 frontend_url_(GetFrontendURL(value
)),
506 remote_id_(GetStringProperty(value
, "id")),
507 remote_type_(GetStringProperty(value
, "type")),
508 local_id_(BuildUniqueTargetId(browser_id
, value
)) {
509 set_type("adb_page");
510 set_url(GURL(GetStringProperty(value
, "url")));
511 set_title(base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(
512 GetStringProperty(value
, "title")))));
513 set_description(GetStringProperty(value
, "description"));
514 set_favicon_url(GURL(GetStringProperty(value
, "faviconUrl")));
515 target_path_
= GetTargetPath(value
);
518 DevToolsAndroidBridge::RemotePageTarget::~RemotePageTarget() {
521 std::string
DevToolsAndroidBridge::RemotePageTarget::GetId() const {
525 bool DevToolsAndroidBridge::RemotePageTarget::IsAttached() const {
526 return target_path_
.empty();
529 static void NoOp(int, const std::string
&) {}
531 void DevToolsAndroidBridge::RemotePageTarget::Inspect(Profile
* profile
) const {
533 bool isWorker
= remote_type_
== kTargetTypeWorker
||
534 remote_type_
== kTargetTypeServiceWorker
;
535 DevToolsWindow::OpenExternalFrontend(profile
, frontend_url_
, GetAgentHost(),
539 bool DevToolsAndroidBridge::RemotePageTarget::Activate() const {
543 std::string request
= base::StringPrintf(kActivatePageRequest
,
545 bridge_
->SendJsonRequest(browser_id_
, request
, base::Bind(&NoOp
));
549 bool DevToolsAndroidBridge::RemotePageTarget::Close() const {
553 std::string request
= base::StringPrintf(kClosePageRequest
,
555 bridge_
->SendJsonRequest(browser_id_
, request
, base::Bind(&NoOp
));
559 void DevToolsAndroidBridge::RemotePageTarget::Reload() const {
563 bridge_
->SendProtocolCommand(browser_id_
, target_path_
, kPageReloadCommand
,
564 NULL
, base::Closure());
567 // DevToolsAndroidBridge::RemotePage ------------------------------------------
569 DevToolsAndroidBridge::RemotePage::RemotePage(const BrowserId
& browser_id
,
570 const base::DictionaryValue
& dict
)
571 : browser_id_(browser_id
),
572 frontend_url_(GetFrontendURL(dict
)),
573 dict_(dict
.DeepCopy()) {
576 DevToolsAndroidBridge::RemotePage::~RemotePage() {
579 // DevToolsAndroidBridge::RemoteBrowser ---------------------------------------
581 DevToolsAndroidBridge::RemoteBrowser::RemoteBrowser(
582 const std::string
& serial
,
583 const AndroidDeviceManager::BrowserInfo
& browser_info
)
584 : browser_id_(std::make_pair(serial
, browser_info
.socket_name
)),
585 display_name_(browser_info
.display_name
),
586 user_(browser_info
.user
),
587 type_(browser_info
.type
) {
590 bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() {
591 return type_
== AndroidDeviceManager::BrowserInfo::kTypeChrome
;
594 std::string
DevToolsAndroidBridge::RemoteBrowser::GetId() {
595 return serial() + ":" + socket();
598 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion
599 DevToolsAndroidBridge::RemoteBrowser::GetParsedVersion() {
600 ParsedVersion result
;
601 for (const base::StringPiece
& part
:
602 base::SplitStringPiece(
603 version_
, ".", base::KEEP_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
)) {
605 base::StringToInt(part
, &value
);
606 result
.push_back(value
);
612 DevToolsAndroidBridge::CreatePageTarget(scoped_refptr
<RemotePage
> page
) {
613 return new RemotePageTarget(this, page
->browser_id_
, *page
->dict_
);
616 void DevToolsAndroidBridge::SendJsonRequest(
617 const BrowserId
& browser_id
,
618 const std::string
& request
,
619 const JsonRequestCallback
& callback
) {
620 scoped_refptr
<AndroidDeviceManager::Device
> device(
621 FindDevice(browser_id
.first
));
623 callback
.Run(net::ERR_FAILED
, std::string());
626 device
->SendJsonRequest(browser_id
.second
, request
, callback
);
629 void DevToolsAndroidBridge::SendProtocolCommand(
630 const BrowserId
& browser_id
,
631 const std::string
& target_path
,
632 const std::string
& method
,
633 scoped_ptr
<base::DictionaryValue
> params
,
634 const base::Closure callback
) {
635 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
636 if (target_path
.empty())
638 scoped_refptr
<AndroidDeviceManager::Device
> device(
639 FindDevice(browser_id
.first
));
645 device
, browser_id
.second
, target_path
,
646 DevToolsProtocol::SerializeCommand(1, method
, params
.Pass()),
650 scoped_refptr
<content::DevToolsAgentHost
>
651 DevToolsAndroidBridge::GetBrowserAgentHost(
652 scoped_refptr
<RemoteBrowser
> browser
) {
653 return AgentHostDelegate::GetOrCreateAgentHost(
655 "adb:" + browser
->serial() + ":" + browser
->socket(),
656 browser
->browser_id_
,
657 kBrowserTargetSocket
);
660 void DevToolsAndroidBridge::SendJsonRequest(
661 const std::string
& browser_id_str
,
662 const std::string
& url
,
663 const JsonRequestCallback
& callback
) {
664 BrowserId browser_id
;
665 if (!BrowserIdFromString(browser_id_str
, &browser_id
)) {
666 callback
.Run(net::ERR_FAILED
, std::string());
669 SendJsonRequest(browser_id
, url
, callback
);
672 scoped_refptr
<AndroidDeviceManager::Device
> DevToolsAndroidBridge::FindDevice(
673 const std::string
& serial
) {
674 DeviceMap::iterator it
= device_map_
.find(serial
);
675 return it
== device_map_
.end() ? nullptr : it
->second
;
678 void DevToolsAndroidBridge::OpenRemotePage(scoped_refptr
<RemoteBrowser
> browser
,
679 const std::string
& input_url
) {
680 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
681 GURL
gurl(input_url
);
682 if (!gurl
.is_valid()) {
683 gurl
= GURL("http://" + input_url
);
684 if (!gurl
.is_valid())
687 std::string url
= gurl
.spec();
688 RemoteBrowser::ParsedVersion parsed_version
= browser
->GetParsedVersion();
690 std::string query
= net::EscapeQueryParamValue(url
, false /* use_plus */);
691 std::string request
=
692 base::StringPrintf(kNewPageRequestWithURL
, query
.c_str());
693 SendJsonRequest(browser
->browser_id_
, request
, base::Bind(&NoOp
));
696 DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() {
699 // DevToolsAndroidBridge::RemoteDevice ----------------------------------------
701 DevToolsAndroidBridge::RemoteDevice::RemoteDevice(
702 const std::string
& serial
,
703 const AndroidDeviceManager::DeviceInfo
& device_info
)
705 model_(device_info
.model
),
706 connected_(device_info
.connected
),
707 screen_size_(device_info
.screen_size
) {
708 for (std::vector
<AndroidDeviceManager::BrowserInfo
>::const_iterator it
=
709 device_info
.browser_info
.begin();
710 it
!= device_info
.browser_info
.end();
712 browsers_
.push_back(new RemoteBrowser(serial
, *it
));
716 DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() {
719 // DevToolsAndroidBridge ------------------------------------------------------
721 DevToolsAndroidBridge::DevToolsAndroidBridge(
723 SigninManagerBase
* signin_manager
,
724 ProfileOAuth2TokenService
* const token_service
)
726 signin_manager_(signin_manager
),
727 token_service_(token_service
),
728 device_manager_(AndroidDeviceManager::Create()),
729 task_scheduler_(base::Bind(&DevToolsAndroidBridge::ScheduleTaskDefault
)),
730 port_forwarding_controller_(new PortForwardingController(profile
, this)),
731 weak_factory_(this) {
732 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
733 pref_change_registrar_
.Init(profile_
->GetPrefs());
734 pref_change_registrar_
.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled
,
735 base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders
,
736 base::Unretained(this)));
737 CreateDeviceProviders();
740 void DevToolsAndroidBridge::AddDeviceListListener(
741 DeviceListListener
* listener
) {
742 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
743 bool polling_was_off
= !NeedsDeviceListPolling();
744 device_list_listeners_
.push_back(listener
);
746 StartDeviceListPolling();
749 void DevToolsAndroidBridge::RemoveDeviceListListener(
750 DeviceListListener
* listener
) {
751 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
752 DeviceListListeners::iterator it
= std::find(
753 device_list_listeners_
.begin(), device_list_listeners_
.end(), listener
);
754 DCHECK(it
!= device_list_listeners_
.end());
755 device_list_listeners_
.erase(it
);
756 if (!NeedsDeviceListPolling())
757 StopDeviceListPolling();
760 void DevToolsAndroidBridge::AddDeviceCountListener(
761 DeviceCountListener
* listener
) {
762 device_count_listeners_
.push_back(listener
);
763 if (device_count_listeners_
.size() == 1)
764 StartDeviceCountPolling();
767 void DevToolsAndroidBridge::RemoveDeviceCountListener(
768 DeviceCountListener
* listener
) {
769 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
770 DeviceCountListeners::iterator it
= std::find(
771 device_count_listeners_
.begin(), device_count_listeners_
.end(), listener
);
772 DCHECK(it
!= device_count_listeners_
.end());
773 device_count_listeners_
.erase(it
);
774 if (device_count_listeners_
.empty())
775 StopDeviceCountPolling();
778 void DevToolsAndroidBridge::AddPortForwardingListener(
779 PortForwardingListener
* listener
) {
780 bool polling_was_off
= !NeedsDeviceListPolling();
781 port_forwarding_listeners_
.push_back(listener
);
783 StartDeviceListPolling();
786 void DevToolsAndroidBridge::RemovePortForwardingListener(
787 PortForwardingListener
* listener
) {
788 PortForwardingListeners::iterator it
= std::find(
789 port_forwarding_listeners_
.begin(),
790 port_forwarding_listeners_
.end(),
792 DCHECK(it
!= port_forwarding_listeners_
.end());
793 port_forwarding_listeners_
.erase(it
);
794 if (!NeedsDeviceListPolling())
795 StopDeviceListPolling();
798 bool DevToolsAndroidBridge::HasDevToolsWindow(const std::string
& agent_id
) {
799 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
800 return host_delegates_
.find(agent_id
) != host_delegates_
.end();
803 DevToolsAndroidBridge::~DevToolsAndroidBridge() {
804 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
805 DCHECK(device_list_listeners_
.empty());
806 DCHECK(device_count_listeners_
.empty());
807 DCHECK(port_forwarding_listeners_
.empty());
810 void DevToolsAndroidBridge::StartDeviceListPolling() {
811 device_list_callback_
.Reset(
812 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList
, AsWeakPtr()));
813 RequestDeviceList(device_list_callback_
.callback());
816 void DevToolsAndroidBridge::StopDeviceListPolling() {
817 device_list_callback_
.Cancel();
821 bool DevToolsAndroidBridge::NeedsDeviceListPolling() {
822 return !device_list_listeners_
.empty() || !port_forwarding_listeners_
.empty();
825 void DevToolsAndroidBridge::RequestDeviceList(
826 const DeviceListCallback
& callback
) {
827 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
829 if (!NeedsDeviceListPolling() ||
830 !callback
.Equals(device_list_callback_
.callback()))
833 new DiscoveryRequest(device_manager_
.get(), callback
);
836 void DevToolsAndroidBridge::ReceivedDeviceList(
837 const CompleteDevices
& complete_devices
) {
838 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
841 RemoteDevices remote_devices
;
842 for (const auto& pair
: complete_devices
) {
843 device_map_
[pair
.first
->serial()] = pair
.first
;
844 remote_devices
.push_back(pair
.second
);
847 DeviceListListeners
copy(device_list_listeners_
);
848 for (DeviceListListeners::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
849 (*it
)->DeviceListChanged(remote_devices
);
851 ForwardingStatus status
=
852 port_forwarding_controller_
->DeviceListChanged(remote_devices
);
853 PortForwardingListeners
forwarding_listeners(port_forwarding_listeners_
);
854 for (PortForwardingListeners::iterator it
= forwarding_listeners
.begin();
855 it
!= forwarding_listeners
.end(); ++it
) {
856 (*it
)->PortStatusChanged(status
);
859 if (!NeedsDeviceListPolling())
863 base::Bind(&DevToolsAndroidBridge::RequestDeviceList
,
864 AsWeakPtr(), device_list_callback_
.callback()));
867 void DevToolsAndroidBridge::StartDeviceCountPolling() {
868 device_count_callback_
.Reset(
869 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount
, AsWeakPtr()));
870 RequestDeviceCount(device_count_callback_
.callback());
873 void DevToolsAndroidBridge::StopDeviceCountPolling() {
874 device_count_callback_
.Cancel();
877 void DevToolsAndroidBridge::RequestDeviceCount(
878 const base::Callback
<void(int)>& callback
) {
879 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
881 if (device_count_listeners_
.empty() ||
882 !callback
.Equals(device_count_callback_
.callback()))
885 UsbDeviceProvider::CountDevices(callback
);
888 void DevToolsAndroidBridge::ReceivedDeviceCount(int count
) {
889 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
891 DeviceCountListeners
copy(device_count_listeners_
);
892 for (DeviceCountListeners::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
893 (*it
)->DeviceCountChanged(count
);
895 if (device_count_listeners_
.empty())
899 base::Bind(&DevToolsAndroidBridge::RequestDeviceCount
,
900 AsWeakPtr(), device_count_callback_
.callback()));
904 void DevToolsAndroidBridge::ScheduleTaskDefault(const base::Closure
& task
) {
905 BrowserThread::PostDelayedTask(
909 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs
));
912 static scoped_refptr
<TCPDeviceProvider
> CreateTCPDeviceProvider() {
913 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
914 if (!command_line
->HasSwitch(switches::kRemoteDebuggingTargets
))
917 command_line
->GetSwitchValueASCII(switches::kRemoteDebuggingTargets
);
918 std::vector
<std::string
> addresses
= base::SplitString(
919 value
, ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
920 std::set
<net::HostPortPair
> targets
;
921 for (const std::string
& address
: addresses
) {
922 net::HostPortPair target
= net::HostPortPair::FromString(address
);
923 if (target
.IsEmpty()) {
924 LOG(WARNING
) << "Invalid target: " << address
;
927 targets
.insert(target
);
931 return new TCPDeviceProvider(targets
);
934 void DevToolsAndroidBridge::CreateDeviceProviders() {
935 AndroidDeviceManager::DeviceProviders device_providers
;
937 if (scoped_refptr
<TCPDeviceProvider
> provider
= CreateTCPDeviceProvider())
938 device_providers
.push_back(provider
);
939 device_providers
.push_back(new AdbDeviceProvider());
941 PrefService
* service
= profile_
->GetPrefs();
942 const PrefService::Preference
* pref
=
943 service
->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled
);
944 const base::Value
* pref_value
= pref
->GetValue();
947 if (pref_value
->GetAsBoolean(&enabled
) && enabled
) {
948 device_providers
.push_back(new UsbDeviceProvider(profile_
));
951 if (IsWebRTCDeviceProviderEnabled()) {
952 device_providers
.push_back(
953 new WebRTCDeviceProvider(profile_
, signin_manager_
, token_service_
));
956 device_manager_
->SetDeviceProviders(device_providers
);
957 if (NeedsDeviceListPolling()) {
958 StopDeviceListPolling();
959 StartDeviceListPolling();