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 base::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() {
400 void DevToolsAndroidBridge::AgentHostDelegate::SendMessageToBackend(
401 const std::string
& message
) {
402 // We could have detached due to physical connection being closed.
406 web_socket_
->SendFrame(message
);
408 pending_messages_
.push_back(message
);
411 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketOpened() {
412 socket_opened_
= true;
413 for (std::vector
<std::string
>::iterator it
= pending_messages_
.begin();
414 it
!= pending_messages_
.end(); ++it
) {
415 SendMessageToBackend(*it
);
417 pending_messages_
.clear();
420 void DevToolsAndroidBridge::AgentHostDelegate::OnFrameRead(
421 const std::string
& message
) {
423 proxy_
->DispatchOnClientHost(message
);
426 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketClosed() {
428 std::string message
= "{ \"method\": \"Inspector.detached\", "
429 "\"params\": { \"reason\": \"Connection lost.\"} }";
430 proxy_
->DispatchOnClientHost(message
);
435 //// RemotePageTarget ----------------------------------------------
437 class DevToolsAndroidBridge::RemotePageTarget
: public DevToolsTargetImpl
{
439 RemotePageTarget(DevToolsAndroidBridge
* bridge
,
440 const BrowserId
& browser_id
,
441 const base::DictionaryValue
& value
);
442 ~RemotePageTarget() override
;
444 // DevToolsTargetImpl overrides.
445 std::string
GetId() const override
;
446 bool IsAttached() const override
;
447 bool Activate() const override
;
448 bool Close() const override
;
449 void Inspect(Profile
* profile
) const override
;
450 void Reload() const override
;
453 base::WeakPtr
<DevToolsAndroidBridge
> bridge_
;
454 BrowserId browser_id_
;
455 std::string target_path_
;
456 std::string frontend_url_
;
457 std::string remote_id_
;
458 std::string remote_type_
;
459 std::string local_id_
;
460 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget
);
463 static std::string
GetStringProperty(const base::DictionaryValue
& value
,
464 const std::string
& name
) {
466 value
.GetString(name
, &result
);
470 static std::string
BuildUniqueTargetId(
471 const DevToolsAndroidBridge::BrowserId
& browser_id
,
472 const base::DictionaryValue
& value
) {
473 return base::StringPrintf("%s:%s:%s", browser_id
.first
.c_str(),
474 browser_id
.second
.c_str(), GetStringProperty(value
, "id").c_str());
477 static std::string
GetFrontendURL(const base::DictionaryValue
& value
) {
478 std::string frontend_url
= GetStringProperty(value
, "devtoolsFrontendUrl");
479 size_t ws_param
= frontend_url
.find("?ws");
480 if (ws_param
!= std::string::npos
)
481 frontend_url
= frontend_url
.substr(0, ws_param
);
482 if (frontend_url
.find("http:") == 0)
483 frontend_url
= "https:" + frontend_url
.substr(5);
487 static std::string
GetTargetPath(const base::DictionaryValue
& value
) {
488 std::string target_path
= GetStringProperty(value
, "webSocketDebuggerUrl");
490 if (target_path
.find("ws://") == 0) {
491 size_t pos
= target_path
.find("/", 5);
492 if (pos
== std::string::npos
)
494 target_path
= target_path
.substr(pos
);
496 target_path
= std::string();
501 DevToolsAndroidBridge::RemotePageTarget::RemotePageTarget(
502 DevToolsAndroidBridge
* bridge
,
503 const BrowserId
& browser_id
,
504 const base::DictionaryValue
& value
)
505 : DevToolsTargetImpl(AgentHostDelegate::GetOrCreateAgentHost(
507 BuildUniqueTargetId(browser_id
, value
),
509 GetTargetPath(value
))),
510 bridge_(bridge
->AsWeakPtr()),
511 browser_id_(browser_id
),
512 target_path_(GetTargetPath(value
)),
513 frontend_url_(GetFrontendURL(value
)),
514 remote_id_(GetStringProperty(value
, "id")),
515 remote_type_(GetStringProperty(value
, "type")),
516 local_id_(BuildUniqueTargetId(browser_id
, value
)) {
517 set_type("adb_page");
518 set_url(GURL(GetStringProperty(value
, "url")));
519 set_title(base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(
520 GetStringProperty(value
, "title")))));
521 set_description(GetStringProperty(value
, "description"));
522 set_favicon_url(GURL(GetStringProperty(value
, "faviconUrl")));
523 target_path_
= GetTargetPath(value
);
526 DevToolsAndroidBridge::RemotePageTarget::~RemotePageTarget() {
529 std::string
DevToolsAndroidBridge::RemotePageTarget::GetId() const {
533 bool DevToolsAndroidBridge::RemotePageTarget::IsAttached() const {
534 return target_path_
.empty();
537 static void NoOp(int, const std::string
&) {}
539 void DevToolsAndroidBridge::RemotePageTarget::Inspect(Profile
* profile
) const {
541 bool isWorker
= remote_type_
== kTargetTypeWorker
||
542 remote_type_
== kTargetTypeServiceWorker
;
543 DevToolsWindow::OpenExternalFrontend(profile
, frontend_url_
, GetAgentHost(),
547 bool DevToolsAndroidBridge::RemotePageTarget::Activate() const {
551 std::string request
= base::StringPrintf(kActivatePageRequest
,
553 bridge_
->SendJsonRequest(browser_id_
, request
, base::Bind(&NoOp
));
557 bool DevToolsAndroidBridge::RemotePageTarget::Close() const {
561 std::string request
= base::StringPrintf(kClosePageRequest
,
563 bridge_
->SendJsonRequest(browser_id_
, request
, base::Bind(&NoOp
));
567 void DevToolsAndroidBridge::RemotePageTarget::Reload() const {
571 bridge_
->SendProtocolCommand(browser_id_
, target_path_
, kPageReloadCommand
,
572 NULL
, base::Closure());
575 // DevToolsAndroidBridge::RemotePage ------------------------------------------
577 DevToolsAndroidBridge::RemotePage::RemotePage(const BrowserId
& browser_id
,
578 const base::DictionaryValue
& dict
)
579 : browser_id_(browser_id
),
580 frontend_url_(GetFrontendURL(dict
)),
581 dict_(dict
.DeepCopy()) {
584 DevToolsAndroidBridge::RemotePage::~RemotePage() {
587 // DevToolsAndroidBridge::RemoteBrowser ---------------------------------------
589 DevToolsAndroidBridge::RemoteBrowser::RemoteBrowser(
590 const std::string
& serial
,
591 const AndroidDeviceManager::BrowserInfo
& browser_info
)
592 : browser_id_(std::make_pair(serial
, browser_info
.socket_name
)),
593 display_name_(browser_info
.display_name
),
594 user_(browser_info
.user
),
595 type_(browser_info
.type
) {
598 bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() {
599 return type_
== AndroidDeviceManager::BrowserInfo::kTypeChrome
;
602 std::string
DevToolsAndroidBridge::RemoteBrowser::GetId() {
603 return serial() + ":" + socket();
606 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion
607 DevToolsAndroidBridge::RemoteBrowser::GetParsedVersion() {
608 ParsedVersion result
;
609 for (const base::StringPiece
& part
:
610 base::SplitStringPiece(
611 version_
, ".", base::KEEP_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
)) {
613 base::StringToInt(part
, &value
);
614 result
.push_back(value
);
620 DevToolsAndroidBridge::CreatePageTarget(scoped_refptr
<RemotePage
> page
) {
621 return new RemotePageTarget(this, page
->browser_id_
, *page
->dict_
);
624 void DevToolsAndroidBridge::SendJsonRequest(
625 const BrowserId
& browser_id
,
626 const std::string
& request
,
627 const JsonRequestCallback
& callback
) {
628 scoped_refptr
<AndroidDeviceManager::Device
> device(
629 FindDevice(browser_id
.first
));
631 callback
.Run(net::ERR_FAILED
, std::string());
634 device
->SendJsonRequest(browser_id
.second
, request
, callback
);
637 void DevToolsAndroidBridge::SendProtocolCommand(
638 const BrowserId
& browser_id
,
639 const std::string
& target_path
,
640 const std::string
& method
,
641 scoped_ptr
<base::DictionaryValue
> params
,
642 const base::Closure callback
) {
643 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
644 if (target_path
.empty())
646 scoped_refptr
<AndroidDeviceManager::Device
> device(
647 FindDevice(browser_id
.first
));
653 device
, browser_id
.second
, target_path
,
654 DevToolsProtocol::SerializeCommand(1, method
, params
.Pass()),
658 scoped_refptr
<content::DevToolsAgentHost
>
659 DevToolsAndroidBridge::GetBrowserAgentHost(
660 scoped_refptr
<RemoteBrowser
> browser
) {
661 return AgentHostDelegate::GetOrCreateAgentHost(
663 "adb:" + browser
->serial() + ":" + browser
->socket(),
664 browser
->browser_id_
,
665 kBrowserTargetSocket
);
668 void DevToolsAndroidBridge::SendJsonRequest(
669 const std::string
& browser_id_str
,
670 const std::string
& url
,
671 const JsonRequestCallback
& callback
) {
672 BrowserId browser_id
;
673 if (!BrowserIdFromString(browser_id_str
, &browser_id
)) {
674 callback
.Run(net::ERR_FAILED
, std::string());
677 SendJsonRequest(browser_id
, url
, callback
);
680 scoped_refptr
<AndroidDeviceManager::Device
> DevToolsAndroidBridge::FindDevice(
681 const std::string
& serial
) {
682 DeviceMap::iterator it
= device_map_
.find(serial
);
683 return it
== device_map_
.end() ? nullptr : it
->second
;
686 void DevToolsAndroidBridge::OpenRemotePage(scoped_refptr
<RemoteBrowser
> browser
,
687 const std::string
& input_url
) {
688 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
689 GURL
gurl(input_url
);
690 if (!gurl
.is_valid()) {
691 gurl
= GURL("http://" + input_url
);
692 if (!gurl
.is_valid())
695 std::string url
= gurl
.spec();
696 RemoteBrowser::ParsedVersion parsed_version
= browser
->GetParsedVersion();
698 std::string query
= net::EscapeQueryParamValue(url
, false /* use_plus */);
699 std::string request
=
700 base::StringPrintf(kNewPageRequestWithURL
, query
.c_str());
701 SendJsonRequest(browser
->browser_id_
, request
, base::Bind(&NoOp
));
704 DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() {
707 // DevToolsAndroidBridge::RemoteDevice ----------------------------------------
709 DevToolsAndroidBridge::RemoteDevice::RemoteDevice(
710 const std::string
& serial
,
711 const AndroidDeviceManager::DeviceInfo
& device_info
)
713 model_(device_info
.model
),
714 connected_(device_info
.connected
),
715 screen_size_(device_info
.screen_size
) {
716 for (std::vector
<AndroidDeviceManager::BrowserInfo
>::const_iterator it
=
717 device_info
.browser_info
.begin();
718 it
!= device_info
.browser_info
.end();
720 browsers_
.push_back(new RemoteBrowser(serial
, *it
));
724 DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() {
727 // DevToolsAndroidBridge ------------------------------------------------------
729 DevToolsAndroidBridge::DevToolsAndroidBridge(
731 SigninManagerBase
* signin_manager
,
732 ProfileOAuth2TokenService
* const token_service
)
734 signin_manager_(signin_manager
),
735 token_service_(token_service
),
736 device_manager_(AndroidDeviceManager::Create()),
737 task_scheduler_(base::Bind(&DevToolsAndroidBridge::ScheduleTaskDefault
)),
738 port_forwarding_controller_(new PortForwardingController(profile
, this)),
739 weak_factory_(this) {
740 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
741 pref_change_registrar_
.Init(profile_
->GetPrefs());
742 pref_change_registrar_
.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled
,
743 base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders
,
744 base::Unretained(this)));
745 CreateDeviceProviders();
748 void DevToolsAndroidBridge::AddDeviceListListener(
749 DeviceListListener
* listener
) {
750 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
751 bool polling_was_off
= !NeedsDeviceListPolling();
752 device_list_listeners_
.push_back(listener
);
754 StartDeviceListPolling();
757 void DevToolsAndroidBridge::RemoveDeviceListListener(
758 DeviceListListener
* listener
) {
759 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
760 DeviceListListeners::iterator it
= std::find(
761 device_list_listeners_
.begin(), device_list_listeners_
.end(), listener
);
762 DCHECK(it
!= device_list_listeners_
.end());
763 device_list_listeners_
.erase(it
);
764 if (!NeedsDeviceListPolling())
765 StopDeviceListPolling();
768 void DevToolsAndroidBridge::AddDeviceCountListener(
769 DeviceCountListener
* listener
) {
770 device_count_listeners_
.push_back(listener
);
771 if (device_count_listeners_
.size() == 1)
772 StartDeviceCountPolling();
775 void DevToolsAndroidBridge::RemoveDeviceCountListener(
776 DeviceCountListener
* listener
) {
777 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
778 DeviceCountListeners::iterator it
= std::find(
779 device_count_listeners_
.begin(), device_count_listeners_
.end(), listener
);
780 DCHECK(it
!= device_count_listeners_
.end());
781 device_count_listeners_
.erase(it
);
782 if (device_count_listeners_
.empty())
783 StopDeviceCountPolling();
786 void DevToolsAndroidBridge::AddPortForwardingListener(
787 PortForwardingListener
* listener
) {
788 bool polling_was_off
= !NeedsDeviceListPolling();
789 port_forwarding_listeners_
.push_back(listener
);
791 StartDeviceListPolling();
794 void DevToolsAndroidBridge::RemovePortForwardingListener(
795 PortForwardingListener
* listener
) {
796 PortForwardingListeners::iterator it
= std::find(
797 port_forwarding_listeners_
.begin(),
798 port_forwarding_listeners_
.end(),
800 DCHECK(it
!= port_forwarding_listeners_
.end());
801 port_forwarding_listeners_
.erase(it
);
802 if (!NeedsDeviceListPolling())
803 StopDeviceListPolling();
806 bool DevToolsAndroidBridge::HasDevToolsWindow(const std::string
& agent_id
) {
807 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
808 return host_delegates_
.find(agent_id
) != host_delegates_
.end();
811 DevToolsAndroidBridge::~DevToolsAndroidBridge() {
812 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
813 DCHECK(device_list_listeners_
.empty());
814 DCHECK(device_count_listeners_
.empty());
815 DCHECK(port_forwarding_listeners_
.empty());
818 void DevToolsAndroidBridge::StartDeviceListPolling() {
819 device_list_callback_
.Reset(
820 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList
, AsWeakPtr()));
821 RequestDeviceList(device_list_callback_
.callback());
824 void DevToolsAndroidBridge::StopDeviceListPolling() {
825 device_list_callback_
.Cancel();
829 bool DevToolsAndroidBridge::NeedsDeviceListPolling() {
830 return !device_list_listeners_
.empty() || !port_forwarding_listeners_
.empty();
833 void DevToolsAndroidBridge::RequestDeviceList(
834 const DeviceListCallback
& callback
) {
835 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
837 if (!NeedsDeviceListPolling() ||
838 !callback
.Equals(device_list_callback_
.callback()))
841 new DiscoveryRequest(device_manager_
.get(), callback
);
844 void DevToolsAndroidBridge::ReceivedDeviceList(
845 const CompleteDevices
& complete_devices
) {
846 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
849 RemoteDevices remote_devices
;
850 for (const auto& pair
: complete_devices
) {
851 device_map_
[pair
.first
->serial()] = pair
.first
;
852 remote_devices
.push_back(pair
.second
);
855 DeviceListListeners
copy(device_list_listeners_
);
856 for (DeviceListListeners::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
857 (*it
)->DeviceListChanged(remote_devices
);
859 ForwardingStatus status
=
860 port_forwarding_controller_
->DeviceListChanged(remote_devices
);
861 PortForwardingListeners
forwarding_listeners(port_forwarding_listeners_
);
862 for (PortForwardingListeners::iterator it
= forwarding_listeners
.begin();
863 it
!= forwarding_listeners
.end(); ++it
) {
864 (*it
)->PortStatusChanged(status
);
867 if (!NeedsDeviceListPolling())
871 base::Bind(&DevToolsAndroidBridge::RequestDeviceList
,
872 AsWeakPtr(), device_list_callback_
.callback()));
875 void DevToolsAndroidBridge::StartDeviceCountPolling() {
876 device_count_callback_
.Reset(
877 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount
, AsWeakPtr()));
878 RequestDeviceCount(device_count_callback_
.callback());
881 void DevToolsAndroidBridge::StopDeviceCountPolling() {
882 device_count_callback_
.Cancel();
885 void DevToolsAndroidBridge::RequestDeviceCount(
886 const base::Callback
<void(int)>& callback
) {
887 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
889 if (device_count_listeners_
.empty() ||
890 !callback
.Equals(device_count_callback_
.callback()))
893 UsbDeviceProvider::CountDevices(callback
);
896 void DevToolsAndroidBridge::ReceivedDeviceCount(int count
) {
897 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
899 DeviceCountListeners
copy(device_count_listeners_
);
900 for (DeviceCountListeners::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
901 (*it
)->DeviceCountChanged(count
);
903 if (device_count_listeners_
.empty())
907 base::Bind(&DevToolsAndroidBridge::RequestDeviceCount
,
908 AsWeakPtr(), device_count_callback_
.callback()));
912 void DevToolsAndroidBridge::ScheduleTaskDefault(const base::Closure
& task
) {
913 BrowserThread::PostDelayedTask(
917 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs
));
920 static scoped_refptr
<TCPDeviceProvider
> CreateTCPDeviceProvider() {
921 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
922 if (!command_line
->HasSwitch(switches::kRemoteDebuggingTargets
))
925 command_line
->GetSwitchValueASCII(switches::kRemoteDebuggingTargets
);
926 std::vector
<std::string
> addresses
= base::SplitString(
927 value
, ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
928 std::set
<net::HostPortPair
> targets
;
929 for (const std::string
& address
: addresses
) {
930 net::HostPortPair target
= net::HostPortPair::FromString(address
);
931 if (target
.IsEmpty()) {
932 LOG(WARNING
) << "Invalid target: " << address
;
935 targets
.insert(target
);
939 return new TCPDeviceProvider(targets
);
942 void DevToolsAndroidBridge::CreateDeviceProviders() {
943 AndroidDeviceManager::DeviceProviders device_providers
;
945 if (scoped_refptr
<TCPDeviceProvider
> provider
= CreateTCPDeviceProvider())
946 device_providers
.push_back(provider
);
947 device_providers
.push_back(new AdbDeviceProvider());
949 PrefService
* service
= profile_
->GetPrefs();
950 const PrefService::Preference
* pref
=
951 service
->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled
);
952 const base::Value
* pref_value
= pref
->GetValue();
955 if (pref_value
->GetAsBoolean(&enabled
) && enabled
) {
956 device_providers
.push_back(new UsbDeviceProvider(profile_
));
959 if (IsWebRTCDeviceProviderEnabled()) {
960 device_providers
.push_back(
961 new WebRTCDeviceProvider(profile_
, signin_manager_
, token_service_
));
964 device_manager_
->SetDeviceProviders(device_providers
);
965 if (NeedsDeviceListPolling()) {
966 StopDeviceListPolling();
967 StartDeviceListPolling();