1 // Copyright (c) 2013 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/devtools_adb_bridge.h"
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/json/json_reader.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/memory/singleton.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread.h"
24 #include "base/values.h"
25 #include "chrome/browser/devtools/adb/android_rsa.h"
26 #include "chrome/browser/devtools/adb_client_socket.h"
27 #include "chrome/browser/devtools/adb_web_socket.h"
28 #include "chrome/browser/devtools/devtools_protocol.h"
29 #include "chrome/browser/devtools/devtools_target_impl.h"
30 #include "chrome/browser/devtools/devtools_window.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
33 #include "content/public/browser/devtools_agent_host.h"
34 #include "content/public/browser/devtools_client_host.h"
35 #include "content/public/browser/devtools_external_agent_proxy.h"
36 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
37 #include "content/public/browser/devtools_manager.h"
38 #include "content/public/browser/user_metrics.h"
39 #include "crypto/rsa_private_key.h"
40 #include "net/base/escape.h"
41 #include "net/base/net_errors.h"
43 using content::BrowserThread
;
47 const char kDeviceModelCommand
[] = "shell:getprop ro.product.model";
48 const char kInstalledChromePackagesCommand
[] = "shell:pm list packages";
49 const char kOpenedUnixSocketsCommand
[] = "shell:cat /proc/net/unix";
50 const char kListProcessesCommand
[] = "shell:ps";
51 const char kDumpsysCommand
[] = "shell:dumpsys window policy";
52 const char kDumpsysScreenSizePrefix
[] = "mStable=";
54 const char kUnknownModel
[] = "Offline";
56 const char kPageListRequest
[] = "GET /json HTTP/1.1\r\n\r\n";
57 const char kVersionRequest
[] = "GET /json/version HTTP/1.1\r\n\r\n";
58 const char kClosePageRequest
[] = "GET /json/close/%s HTTP/1.1\r\n\r\n";
59 const char kNewPageRequest
[] = "GET /json/new HTTP/1.1\r\n\r\n";
60 const char kNewPageRequestWithURL
[] = "GET /json/new?%s HTTP/1.1\r\n\r\n";
61 const char kActivatePageRequest
[] =
62 "GET /json/activate/%s HTTP/1.1\r\n\r\n";
63 const int kAdbPollingIntervalMs
= 1000;
65 const char kUrlParam
[] = "url";
66 const char kPageReloadCommand
[] = "Page.reload";
67 const char kPageNavigateCommand
[] = "Page.navigate";
69 const char kChromeDefaultName
[] = "Chrome";
70 const char kChromeDefaultSocket
[] = "chrome_devtools_remote";
71 const int kMinVersionNewWithURL
= 32;
72 const int kNewPageNavigateDelayMs
= 500;
74 const char kWebViewSocketPrefix
[] = "webview_devtools_remote";
75 const char kWebViewNameTemplate
[] = "WebView in %s";
77 typedef DevToolsAdbBridge::Callback Callback
;
78 typedef std::vector
<scoped_refptr
<AndroidDevice
> >
80 typedef base::Callback
<void(const AndroidDevices
&)> AndroidDevicesCallback
;
83 struct BrowserDescriptor
{
86 const char* display_name
;
89 const BrowserDescriptor kBrowserDescriptors
[] = {
101 "com.google.android.apps.chrome_dev",
102 kChromeDefaultSocket
,
106 "com.google.android.apps.chrome",
107 kChromeDefaultSocket
,
111 "org.chromium.content_shell_apk",
112 "content_shell_devtools_remote",
116 "org.chromium.chrome.testshell",
117 "chromium_testshell_devtools_remote",
118 "Chromium Test Shell"
121 "org.chromium.android_webview.shell",
122 "webview_devtools_remote",
127 const BrowserDescriptor
* FindBrowserDescriptor(const std::string
& package
) {
128 int count
= sizeof(kBrowserDescriptors
) / sizeof(kBrowserDescriptors
[0]);
129 for (int i
= 0; i
< count
; i
++)
130 if (kBrowserDescriptors
[i
].package
== package
)
131 return &kBrowserDescriptors
[i
];
135 typedef std::map
<std::string
, const BrowserDescriptor
*> DescriptorMap
;
137 static DescriptorMap
FindInstalledBrowserPackages(
138 const std::string
& response
) {
139 // Parse 'pm list packages' output which on Android looks like this:
141 // package:com.android.chrome
142 // package:com.chrome.beta
143 // package:com.example.app
145 DescriptorMap package_to_descriptor
;
146 const std::string package_prefix
= "package:";
147 std::vector
<std::string
> entries
;
148 Tokenize(response
, "'\r\n", &entries
);
149 for (size_t i
= 0; i
< entries
.size(); ++i
) {
150 if (entries
[i
].find(package_prefix
) != 0)
152 std::string package
= entries
[i
].substr(package_prefix
.size());
153 const BrowserDescriptor
* descriptor
= FindBrowserDescriptor(package
);
156 package_to_descriptor
[descriptor
->package
] = descriptor
;
158 return package_to_descriptor
;
161 typedef std::map
<std::string
, std::string
> StringMap
;
163 static void MapProcessesToPackages(const std::string
& response
,
164 StringMap
& pid_to_package
,
165 StringMap
& package_to_pid
) {
166 // Parse 'ps' output which on Android looks like this:
168 // USER PID PPID VSIZE RSS WCHAN PC ? NAME
170 std::vector
<std::string
> entries
;
171 Tokenize(response
, "\n", &entries
);
172 for (size_t i
= 1; i
< entries
.size(); ++i
) {
173 std::vector
<std::string
> fields
;
174 Tokenize(entries
[i
], " \r", &fields
);
175 if (fields
.size() < 9)
177 std::string pid
= fields
[1];
178 std::string package
= fields
[8];
179 pid_to_package
[pid
] = package
;
180 package_to_pid
[package
] = pid
;
184 typedef std::map
<std::string
,
185 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> > BrowserMap
;
187 static StringMap
MapSocketsToProcesses(const std::string
& response
,
188 const std::string
& channel_pattern
) {
189 // Parse 'cat /proc/net/unix' output which on Android looks like this:
191 // Num RefCount Protocol Flags Type St Inode Path
192 // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
193 // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
194 // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
196 // We need to find records with paths starting from '@' (abstract socket)
197 // and containing the channel pattern ("_devtools_remote").
198 StringMap socket_to_pid
;
199 std::vector
<std::string
> entries
;
200 Tokenize(response
, "\n", &entries
);
201 for (size_t i
= 1; i
< entries
.size(); ++i
) {
202 std::vector
<std::string
> fields
;
203 Tokenize(entries
[i
], " \r", &fields
);
204 if (fields
.size() < 8)
206 if (fields
[3] != "00010000" || fields
[5] != "01")
208 std::string path_field
= fields
[7];
209 if (path_field
.size() < 1 || path_field
[0] != '@')
211 size_t socket_name_pos
= path_field
.find(channel_pattern
);
212 if (socket_name_pos
== std::string::npos
)
215 std::string socket
= path_field
.substr(1);
218 size_t socket_name_end
= socket_name_pos
+ channel_pattern
.size();
219 if (socket_name_end
< path_field
.size() &&
220 path_field
[socket_name_end
] == '_') {
221 pid
= path_field
.substr(socket_name_end
+ 1);
223 socket_to_pid
[socket
] = pid
;
225 return socket_to_pid
;
228 // AdbPagesCommand ------------------------------------------------------------
230 class AdbPagesCommand
: public base::RefCountedThreadSafe
<
232 BrowserThread::DeleteOnUIThread
> {
234 typedef base::Callback
<void(DevToolsAdbBridge::RemoteDevices
*)> Callback
;
237 scoped_refptr
<RefCountedAdbThread
> adb_thread
,
238 const DevToolsAdbBridge::DeviceProviders
& device_providers
,
239 const Callback
& callback
);
242 friend struct BrowserThread::DeleteOnThread
<
244 friend class base::DeleteHelper
<AdbPagesCommand
>;
246 virtual ~AdbPagesCommand();
247 void ProcessDeviceProviders();
248 void ReceivedDevices(const AndroidDevices
& devices
);
250 void ProcessSerials();
251 void ReceivedModel(int result
, const std::string
& response
);
252 void ReceivedDumpsys(int result
, const std::string
& response
);
253 void ReceivedPackages(int result
, const std::string
& response
);
254 void ReceivedProcesses(
255 const std::string
& packages_response
,
257 const std::string
& processes_response
);
258 void ReceivedSockets(
259 const std::string
& packages_response
,
260 const std::string
& processes_response
,
262 const std::string
& sockets_response
);
263 void ProcessSockets();
264 void ReceivedVersion(int result
, const std::string
& response
);
265 void ReceivedPages(int result
, const std::string
& response
);
267 scoped_refptr
<AndroidDevice
> current_device() const {
268 return devices_
.back();
271 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> current_browser() const {
272 return browsers_
.back();
280 void CreateBrowsers(const std::string
& packages_response
,
281 const std::string
& processes_response
,
282 const std::string
& sockets_response
);
284 void ParseDumpsysResponse(const std::string
& response
);
285 void ParseScreenSize(const std::string
& str
);
287 scoped_refptr
<RefCountedAdbThread
> adb_thread_
;
289 AndroidDevices devices_
;
290 DevToolsAdbBridge::RemoteBrowsers browsers_
;
291 scoped_ptr
<DevToolsAdbBridge::RemoteDevices
> remote_devices_
;
292 DevToolsAdbBridge::DeviceProviders device_providers_
;
295 AdbPagesCommand::AdbPagesCommand(
296 scoped_refptr
<RefCountedAdbThread
> adb_thread
,
297 const DevToolsAdbBridge::DeviceProviders
& device_providers
,
298 const Callback
& callback
)
299 : adb_thread_(adb_thread
),
301 device_providers_(device_providers
) {
302 remote_devices_
.reset(new DevToolsAdbBridge::RemoteDevices());
304 ProcessDeviceProviders();
307 AdbPagesCommand::~AdbPagesCommand() {
308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
311 void AdbPagesCommand::ProcessDeviceProviders() {
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
313 if (device_providers_
.empty()) {
314 adb_thread_
->message_loop()->PostTask(
315 FROM_HERE
, base::Bind(&AdbPagesCommand::ProcessSerials
, this));
319 const scoped_refptr
<AndroidDeviceProvider
>& device_provider
=
320 device_providers_
.back();
322 device_provider
->QueryDevices(
323 base::Bind(&AdbPagesCommand::ReceivedDevices
, this));
326 void AdbPagesCommand::ReceivedDevices(const AndroidDevices
& devices
) {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
328 DCHECK(!device_providers_
.empty());
329 device_providers_
.pop_back();
331 devices_
.insert(devices_
.end(), devices
.begin(), devices
.end());
333 if (!device_providers_
.empty()) {
334 ProcessDeviceProviders();
336 adb_thread_
->message_loop()->PostTask(
337 FROM_HERE
, base::Bind(&AdbPagesCommand::ProcessSerials
, this));
341 void AdbPagesCommand::ProcessSerials() {
342 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
343 if (devices_
.size() == 0) {
344 BrowserThread::PostTask(
345 BrowserThread::UI
, FROM_HERE
,
346 base::Bind(&AdbPagesCommand::Respond
, this));
350 scoped_refptr
<AndroidDevice
> device
= current_device();
352 if (device
->is_connected()) {
353 device
->RunCommand(kDeviceModelCommand
,
354 base::Bind(&AdbPagesCommand::ReceivedModel
, this));
356 device
->set_model(kUnknownModel
);
357 remote_devices_
->push_back(new DevToolsAdbBridge::RemoteDevice(device
));
362 void AdbPagesCommand::ReceivedModel(int result
, const std::string
& response
) {
363 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
368 scoped_refptr
<AndroidDevice
> device
= current_device();
369 device
->set_model(response
);
370 remote_devices_
->push_back(new DevToolsAdbBridge::RemoteDevice(device
));
371 device
->RunCommand(kDumpsysCommand
,
372 base::Bind(&AdbPagesCommand::ReceivedDumpsys
, this));
375 void AdbPagesCommand::ReceivedDumpsys(int result
,
376 const std::string
& response
) {
377 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
379 ParseDumpsysResponse(response
);
381 current_device()->RunCommand(
382 kInstalledChromePackagesCommand
,
383 base::Bind(&AdbPagesCommand::ReceivedPackages
, this));
386 void AdbPagesCommand::ReceivedPackages(int result
,
387 const std::string
& packages_response
) {
388 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
393 current_device()->RunCommand(
394 kListProcessesCommand
,
395 base::Bind(&AdbPagesCommand::ReceivedProcesses
, this, packages_response
));
398 void AdbPagesCommand::ReceivedProcesses(
399 const std::string
& packages_response
,
401 const std::string
& processes_response
) {
402 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
407 current_device()->RunCommand(
408 kOpenedUnixSocketsCommand
,
409 base::Bind(&AdbPagesCommand::ReceivedSockets
,
412 processes_response
));
415 void AdbPagesCommand::ReceivedSockets(
416 const std::string
& packages_response
,
417 const std::string
& processes_response
,
419 const std::string
& sockets_response
) {
420 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
422 CreateBrowsers(packages_response
, processes_response
, sockets_response
);
426 void AdbPagesCommand::ProcessSockets() {
427 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
428 if (browsers_
.size() == 0) {
433 current_device()->HttpQuery(
434 current_browser()->socket(),
436 base::Bind(&AdbPagesCommand::ReceivedVersion
, this));
439 void AdbPagesCommand::ReceivedVersion(int result
,
440 const std::string
& response
) {
441 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
447 // Parse version, append to package name if available,
448 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response
));
449 base::DictionaryValue
* dict
;
450 if (value
&& value
->GetAsDictionary(&dict
)) {
452 if (dict
->GetString("Browser", &browser
)) {
453 std::vector
<std::string
> parts
;
454 Tokenize(browser
, "/", &parts
);
455 if (parts
.size() == 2)
456 current_browser()->set_version(parts
[1]);
458 current_browser()->set_version(browser
);
461 if (dict
->GetString("Android-Package", &package
)) {
462 const BrowserDescriptor
* descriptor
= FindBrowserDescriptor(package
);
464 current_browser()->set_display_name(descriptor
->display_name
);
468 current_device()->HttpQuery(
469 current_browser()->socket(),
471 base::Bind(&AdbPagesCommand::ReceivedPages
, this));
474 void AdbPagesCommand::ReceivedPages(int result
,
475 const std::string
& response
) {
476 DCHECK_EQ(adb_thread_
->message_loop(), base::MessageLoop::current());
478 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response
));
479 base::ListValue
* list_value
;
480 if (value
&& value
->GetAsList(&list_value
))
481 current_browser()->SetPageDescriptors(*list_value
);
486 void AdbPagesCommand::NextBrowser() {
487 browsers_
.pop_back();
491 void AdbPagesCommand::NextDevice() {
496 void AdbPagesCommand::Respond() {
497 callback_
.Run(remote_devices_
.release());
500 void AdbPagesCommand::CreateBrowsers(
501 const std::string
& packages_response
,
502 const std::string
& processes_response
,
503 const std::string
& sockets_response
) {
504 DescriptorMap package_to_descriptor
=
505 FindInstalledBrowserPackages(packages_response
);
507 StringMap pid_to_package
;
508 StringMap package_to_pid
;
509 MapProcessesToPackages(processes_response
, pid_to_package
, package_to_pid
);
511 const std::string channel_pattern
=
512 base::StringPrintf(kDevToolsChannelNameFormat
, "");
514 StringMap socket_to_pid
= MapSocketsToProcesses(sockets_response
,
517 scoped_refptr
<DevToolsAdbBridge::RemoteDevice
> remote_device
=
518 remote_devices_
->back();
520 // Create RemoteBrowser instances.
521 BrowserMap package_to_running_browser
;
522 BrowserMap socket_to_unnamed_browser
;
523 for (StringMap::iterator it
= socket_to_pid
.begin();
524 it
!= socket_to_pid
.end(); ++it
) {
525 std::string socket
= it
->first
;
526 std::string pid
= it
->second
;
528 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser
=
529 new DevToolsAdbBridge::RemoteBrowser(
530 adb_thread_
, remote_device
->device(), socket
);
532 StringMap::iterator pit
= pid_to_package
.find(pid
);
533 if (pit
!= pid_to_package
.end()) {
534 std::string package
= pit
->second
;
535 package_to_running_browser
[package
] = browser
;
536 const BrowserDescriptor
* descriptor
= FindBrowserDescriptor(package
);
538 browser
->set_display_name(descriptor
->display_name
);
539 } else if (socket
.find(kWebViewSocketPrefix
) == 0) {
540 browser
->set_display_name(
541 base::StringPrintf(kWebViewNameTemplate
, package
.c_str()));
543 browser
->set_display_name(package
);
546 // Set fallback display name.
547 std::string name
= socket
.substr(0, socket
.find(channel_pattern
));
548 name
[0] = base::ToUpperASCII(name
[0]);
549 browser
->set_display_name(name
);
551 socket_to_unnamed_browser
[socket
] = browser
;
553 remote_device
->AddBrowser(browser
);
556 browsers_
= remote_device
->browsers();
558 // Find installed packages not mapped to browsers.
559 typedef std::multimap
<std::string
, const BrowserDescriptor
*>
561 DescriptorMultimap socket_to_descriptor
;
562 for (DescriptorMap::iterator it
= package_to_descriptor
.begin();
563 it
!= package_to_descriptor
.end(); ++it
) {
564 std::string package
= it
->first
;
565 const BrowserDescriptor
* descriptor
= it
->second
;
567 if (package_to_running_browser
.find(package
) !=
568 package_to_running_browser
.end())
569 continue; // This package is already mapped to a browser.
571 if (package_to_pid
.find(package
) != package_to_pid
.end()) {
572 // This package is running but not mapped to a browser.
573 socket_to_descriptor
.insert(
574 DescriptorMultimap::value_type(descriptor
->socket
, descriptor
));
579 // Try naming remaining unnamed browsers.
580 for (DescriptorMultimap::iterator it
= socket_to_descriptor
.begin();
581 it
!= socket_to_descriptor
.end(); ++it
) {
582 std::string socket
= it
->first
;
583 const BrowserDescriptor
* descriptor
= it
->second
;
585 if (socket_to_descriptor
.count(socket
) != 1)
586 continue; // No definitive match.
588 BrowserMap::iterator bit
= socket_to_unnamed_browser
.find(socket
);
589 if (bit
!= socket_to_unnamed_browser
.end())
590 bit
->second
->set_display_name(descriptor
->display_name
);
594 void AdbPagesCommand::ParseDumpsysResponse(const std::string
& response
) {
595 std::vector
<std::string
> lines
;
596 Tokenize(response
, "\r", &lines
);
597 for (size_t i
= 0; i
< lines
.size(); ++i
) {
598 std::string line
= lines
[i
];
599 size_t pos
= line
.find(kDumpsysScreenSizePrefix
);
600 if (pos
!= std::string::npos
) {
602 line
.substr(pos
+ std::string(kDumpsysScreenSizePrefix
).size()));
608 void AdbPagesCommand::ParseScreenSize(const std::string
& str
) {
609 std::vector
<std::string
> pairs
;
610 Tokenize(str
, "-", &pairs
);
611 if (pairs
.size() != 2)
616 std::vector
<std::string
> numbers
;
617 Tokenize(pairs
[1].substr(1, pairs
[1].size() - 2), ",", &numbers
);
618 if (numbers
.size() != 2 ||
619 !base::StringToInt(numbers
[0], &width
) ||
620 !base::StringToInt(numbers
[1], &height
))
623 remote_devices_
->back()->set_screen_size(gfx::Size(width
, height
));
627 // AdbProtocolCommand ---------------------------------------------------------
629 class AdbProtocolCommand
: public AdbWebSocket::Delegate
{
632 scoped_refptr
<RefCountedAdbThread
> adb_thread
,
633 scoped_refptr
<AndroidDevice
> device
,
634 const std::string
& socket_name
,
635 const std::string
& debug_url
,
636 const std::string
& command
);
639 virtual void OnSocketOpened() OVERRIDE
;
640 virtual void OnFrameRead(const std::string
& message
) OVERRIDE
;
641 virtual void OnSocketClosed(bool closed_by_device
) OVERRIDE
;
642 virtual bool ProcessIncomingMessage(const std::string
& message
) OVERRIDE
;
644 scoped_refptr
<RefCountedAdbThread
> adb_thread_
;
645 const std::string command_
;
646 scoped_refptr
<AdbWebSocket
> web_socket_
;
648 DISALLOW_COPY_AND_ASSIGN(AdbProtocolCommand
);
651 AdbProtocolCommand::AdbProtocolCommand(
652 scoped_refptr
<RefCountedAdbThread
> adb_thread
,
653 scoped_refptr
<AndroidDevice
> device
,
654 const std::string
& socket_name
,
655 const std::string
& debug_url
,
656 const std::string
& command
)
657 : adb_thread_(adb_thread
),
659 web_socket_
= new AdbWebSocket(
660 device
, socket_name
, debug_url
, adb_thread_
->message_loop(), this);
663 void AdbProtocolCommand::OnSocketOpened() {
664 web_socket_
->SendFrame(command_
);
665 web_socket_
->Disconnect();
668 void AdbProtocolCommand::OnFrameRead(const std::string
& message
) {}
670 void AdbProtocolCommand::OnSocketClosed(bool closed_by_device
) {
674 bool AdbProtocolCommand::ProcessIncomingMessage(const std::string
& message
) {
680 const char kDevToolsChannelNameFormat
[] = "%s_devtools_remote";
682 class AgentHostDelegate
;
684 typedef std::map
<std::string
, AgentHostDelegate
*> AgentHostDelegates
;
686 base::LazyInstance
<AgentHostDelegates
>::Leaky g_host_delegates
=
687 LAZY_INSTANCE_INITIALIZER
;
689 DevToolsAdbBridge::Wrapper::Wrapper() {
690 bridge_
= new DevToolsAdbBridge();
693 DevToolsAdbBridge::Wrapper::~Wrapper() {
696 DevToolsAdbBridge
* DevToolsAdbBridge::Wrapper::Get() {
697 return bridge_
.get();
701 DevToolsAdbBridge::Factory
* DevToolsAdbBridge::Factory::GetInstance() {
702 return Singleton
<DevToolsAdbBridge::Factory
>::get();
706 DevToolsAdbBridge
* DevToolsAdbBridge::Factory::GetForProfile(
708 DevToolsAdbBridge::Wrapper
* wrapper
=
709 static_cast<DevToolsAdbBridge::Wrapper
*>(GetInstance()->
710 GetServiceForBrowserContext(profile
, true));
711 return wrapper
? wrapper
->Get() : NULL
;
714 DevToolsAdbBridge::Factory::Factory()
715 : BrowserContextKeyedServiceFactory(
717 BrowserContextDependencyManager::GetInstance()) {}
719 DevToolsAdbBridge::Factory::~Factory() {}
721 BrowserContextKeyedService
*
722 DevToolsAdbBridge::Factory::BuildServiceInstanceFor(
723 content::BrowserContext
* context
) const {
724 return new DevToolsAdbBridge::Wrapper();
728 // AgentHostDelegate ----------------------------------------------------------
730 class AgentHostDelegate
: public content::DevToolsExternalAgentProxyDelegate
,
731 public AdbWebSocket::Delegate
{
733 static void Create(const std::string
& id
,
734 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser
,
735 const std::string
& debug_url
,
736 const std::string
& frontend_url
,
738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
739 AgentHostDelegates::iterator it
=
740 g_host_delegates
.Get().find(id
);
741 if (it
!= g_host_delegates
.Get().end()) {
742 it
->second
->OpenFrontend();
743 } else if (!frontend_url
.empty()) {
744 new AgentHostDelegate(
745 id
, browser
->device(), browser
->socket(), debug_url
,
746 frontend_url
, browser
->adb_thread()->message_loop(), profile
);
752 const std::string
& id
,
753 scoped_refptr
<AndroidDevice
> device
,
754 const std::string
& socket_name
,
755 const std::string
& debug_url
,
756 const std::string
& frontend_url
,
757 base::MessageLoop
* adb_message_loop
,
760 frontend_url_(frontend_url
),
762 web_socket_
= new AdbWebSocket(
763 device
, socket_name
, debug_url
, adb_message_loop
, this);
764 g_host_delegates
.Get()[id
] = this;
766 if (socket_name
.find(kWebViewSocketPrefix
) == 0) {
767 content::RecordAction(
768 base::UserMetricsAction("DevTools_InspectAndroidWebView"));
770 content::RecordAction(
771 base::UserMetricsAction("DevTools_InspectAndroidPage"));
775 void OpenFrontend() {
778 DevToolsWindow::OpenExternalFrontend(
779 profile_
, frontend_url_
, proxy_
->GetAgentHost().get());
782 virtual ~AgentHostDelegate() {
783 g_host_delegates
.Get().erase(id_
);
786 virtual void Attach() OVERRIDE
{}
788 virtual void Detach() OVERRIDE
{
789 web_socket_
->Disconnect();
792 virtual void SendMessageToBackend(const std::string
& message
) OVERRIDE
{
793 web_socket_
->SendFrame(message
);
796 virtual void OnSocketOpened() OVERRIDE
{
797 proxy_
.reset(content::DevToolsExternalAgentProxy::Create(this));
801 virtual void OnFrameRead(const std::string
& message
) OVERRIDE
{
802 proxy_
->DispatchOnClientHost(message
);
805 virtual void OnSocketClosed(bool closed_by_device
) OVERRIDE
{
806 if (proxy_
&& closed_by_device
)
807 proxy_
->ConnectionClosed();
811 virtual bool ProcessIncomingMessage(const std::string
& message
) OVERRIDE
{
815 const std::string id_
;
816 const std::string frontend_url_
;
819 scoped_ptr
<content::DevToolsExternalAgentProxy
> proxy_
;
820 scoped_refptr
<AdbWebSocket
> web_socket_
;
821 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate
);
824 //// RemotePageTarget ----------------------------------------------
826 class RemotePageTarget
: public DevToolsTargetImpl
{
828 RemotePageTarget(scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser
,
829 const base::DictionaryValue
& value
);
830 virtual ~RemotePageTarget();
832 // content::DevToolsTarget overrides:
833 virtual bool IsAttached() const OVERRIDE
;
834 virtual bool Activate() const OVERRIDE
;
835 virtual bool Close() const OVERRIDE
;
837 // DevToolsTargetImpl overrides:
838 virtual void Inspect(Profile
* profile
) const OVERRIDE
;
839 virtual void Reload() const OVERRIDE
;
841 void Navigate(const std::string
& url
) const;
844 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser_
;
845 std::string debug_url_
;
846 std::string frontend_url_
;
847 std::string remote_id_
;
848 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget
);
851 RemotePageTarget::RemotePageTarget(
852 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser
,
853 const base::DictionaryValue
& value
)
854 : browser_(browser
) {
856 value
.GetString("id", &remote_id_
);
858 value
.GetString("url", &url
);
860 value
.GetString("title", &title_
);
861 title_
= base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(title_
)));
862 value
.GetString("description", &description_
);
863 std::string favicon_url
;
864 value
.GetString("faviconUrl", &favicon_url
);
865 favicon_url_
= GURL(favicon_url
);
866 value
.GetString("webSocketDebuggerUrl", &debug_url_
);
867 value
.GetString("devtoolsFrontendUrl", &frontend_url_
);
869 if (remote_id_
.empty() && !debug_url_
.empty()) {
870 // Target id is not available until Chrome 26. Use page id at the end of
871 // debug_url_ instead. For attached targets the id will remain empty.
872 std::vector
<std::string
> parts
;
873 Tokenize(debug_url_
, "/", &parts
);
874 remote_id_
= parts
[parts
.size()-1];
877 if (debug_url_
.find("ws://") == 0)
878 debug_url_
= debug_url_
.substr(5);
882 size_t ws_param
= frontend_url_
.find("?ws");
883 if (ws_param
!= std::string::npos
)
884 frontend_url_
= frontend_url_
.substr(0, ws_param
);
885 if (frontend_url_
.find("http:") == 0)
886 frontend_url_
= "https:" + frontend_url_
.substr(5);
888 id_
= base::StringPrintf("%s:%s:%s",
889 browser_
->device()->serial().c_str(),
890 browser_
->socket().c_str(),
894 RemotePageTarget::~RemotePageTarget() {
897 bool RemotePageTarget::IsAttached() const {
898 return debug_url_
.empty();
901 void RemotePageTarget::Inspect(Profile
* profile
) const {
902 std::string request
= base::StringPrintf(kActivatePageRequest
,
904 base::Closure inspect_callback
= base::Bind(&AgentHostDelegate::Create
,
905 id_
, browser_
, debug_url_
, frontend_url_
, profile
);
906 browser_
->SendJsonRequest(request
, inspect_callback
);
909 bool RemotePageTarget::Activate() const {
910 std::string request
= base::StringPrintf(kActivatePageRequest
,
912 browser_
->SendJsonRequest(request
, base::Closure());
916 bool RemotePageTarget::Close() const {
919 std::string request
= base::StringPrintf(kClosePageRequest
,
921 browser_
->SendJsonRequest(request
, base::Closure());
925 void RemotePageTarget::Reload() const {
926 browser_
->SendProtocolCommand(debug_url_
, kPageReloadCommand
, NULL
);
929 void RemotePageTarget::Navigate(const std::string
& url
) const {
930 base::DictionaryValue params
;
931 params
.SetString(kUrlParam
, url
);
932 browser_
->SendProtocolCommand(debug_url_
, kPageNavigateCommand
, ¶ms
);
935 // DevToolsAdbBridge::RemoteBrowser -------------------------------------------
937 DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
938 scoped_refptr
<RefCountedAdbThread
> adb_thread
,
939 scoped_refptr
<AndroidDevice
> device
,
940 const std::string
& socket
)
941 : adb_thread_(adb_thread
),
944 page_descriptors_(new base::ListValue()) {
947 bool DevToolsAdbBridge::RemoteBrowser::IsChrome() const {
948 return socket_
.find(kChromeDefaultSocket
) == 0;
951 DevToolsAdbBridge::RemoteBrowser::ParsedVersion
952 DevToolsAdbBridge::RemoteBrowser::GetParsedVersion() const {
953 ParsedVersion result
;
954 std::vector
<std::string
> parts
;
955 Tokenize(version_
, ".", &parts
);
956 for (size_t i
= 0; i
!= parts
.size(); ++i
) {
958 base::StringToInt(parts
[i
], &value
);
959 result
.push_back(value
);
964 std::vector
<DevToolsTargetImpl
*>
965 DevToolsAdbBridge::RemoteBrowser::CreatePageTargets() {
966 std::vector
<DevToolsTargetImpl
*> result
;
967 for (size_t i
= 0; i
< page_descriptors_
->GetSize(); ++i
) {
969 page_descriptors_
->Get(i
, &item
);
972 base::DictionaryValue
* dict
;
973 if (!item
->GetAsDictionary(&dict
))
975 result
.push_back(new RemotePageTarget(this, *dict
));
980 void DevToolsAdbBridge::RemoteBrowser::SetPageDescriptors(
981 const base::ListValue
& list
) {
982 page_descriptors_
.reset(list
.DeepCopy());
985 static void RespondOnUIThread(base::Closure callback
, int, const std::string
&) {
986 if (!callback
.is_null())
987 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, callback
);
990 void DevToolsAdbBridge::RemoteBrowser::SendJsonRequest(
991 const std::string
& request
, base::Closure callback
) {
992 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
993 adb_thread_
->message_loop()->PostTask(FROM_HERE
,
994 base::Bind(&AndroidDevice::HttpQuery
, device_
, socket_
, request
,
995 base::Bind(&RespondOnUIThread
, callback
)));
998 void DevToolsAdbBridge::RemoteBrowser::SendProtocolCommand(
999 const std::string
& debug_url
,
1000 const std::string
& method
,
1001 base::DictionaryValue
* params
) {
1002 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1003 if (debug_url
.empty())
1005 DevToolsProtocol::Command
command(1, method
, params
);
1006 new AdbProtocolCommand(
1007 adb_thread_
, device_
, socket_
, debug_url
, command
.Serialize());
1010 static void NoOp(int, const std::string
&) {}
1012 void DevToolsAdbBridge::RemoteBrowser::Open(const std::string
& input_url
) {
1013 GURL
gurl(input_url
);
1014 if (!gurl
.is_valid()) {
1015 gurl
= GURL("http://" + input_url
);
1016 if (!gurl
.is_valid())
1019 std::string url
= gurl
.spec();
1021 ParsedVersion parsed_version
= GetParsedVersion();
1023 !parsed_version
.empty() &&
1024 parsed_version
[0] >= kMinVersionNewWithURL
) {
1025 std::string query
= net::EscapeQueryParamValue(url
, false /* use_plus */);
1026 std::string request
=
1027 base::StringPrintf(kNewPageRequestWithURL
, query
.c_str());
1028 adb_thread_
->message_loop()->PostTask(FROM_HERE
,
1029 base::Bind(&AndroidDevice::HttpQuery
,
1030 device_
, socket_
, request
, base::Bind(&NoOp
)));
1032 adb_thread_
->message_loop()->PostTask(FROM_HERE
,
1033 base::Bind(&AndroidDevice::HttpQuery
,
1034 device_
, socket_
, kNewPageRequest
,
1035 base::Bind(&RemoteBrowser::PageCreatedOnHandlerThread
, this, url
)));
1039 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnHandlerThread(
1040 const std::string
& url
, int result
, const std::string
& response
) {
1043 // Navigating too soon after the page creation breaks navigation history
1044 // (crbug.com/311014). This can be avoided by adding a moderate delay.
1045 BrowserThread::PostDelayedTask(
1046 BrowserThread::UI
, FROM_HERE
,
1047 base::Bind(&RemoteBrowser::PageCreatedOnUIThread
, this, response
, url
),
1048 base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs
));
1051 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnUIThread(
1052 const std::string
& response
, const std::string
& url
) {
1053 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response
));
1054 base::DictionaryValue
* dict
;
1055 if (value
&& value
->GetAsDictionary(&dict
)) {
1056 RemotePageTarget
new_page(this, *dict
);
1057 new_page
.Navigate(url
);
1061 DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
1065 // DevToolsAdbBridge::RemoteDevice --------------------------------------------
1067 DevToolsAdbBridge::RemoteDevice::RemoteDevice(
1068 scoped_refptr
<AndroidDevice
> device
)
1072 std::string
DevToolsAdbBridge::RemoteDevice::GetSerial() {
1073 return device_
->serial();
1076 std::string
DevToolsAdbBridge::RemoteDevice::GetModel() {
1077 return device_
->model();
1080 bool DevToolsAdbBridge::RemoteDevice::IsConnected() {
1081 return device_
->is_connected();
1084 void DevToolsAdbBridge::RemoteDevice::AddBrowser(
1085 scoped_refptr
<RemoteBrowser
> browser
) {
1086 browsers_
.push_back(browser
);
1089 DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
1093 // DevToolsAdbBridge ----------------------------------------------------------
1095 DevToolsAdbBridge::DevToolsAdbBridge()
1096 : adb_thread_(RefCountedAdbThread::GetInstance()),
1097 has_message_loop_(adb_thread_
->message_loop() != NULL
) {
1100 void DevToolsAdbBridge::AddListener(Listener
* listener
) {
1101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1102 if (listeners_
.empty())
1103 RequestRemoteDevices();
1104 listeners_
.push_back(listener
);
1107 void DevToolsAdbBridge::RemoveListener(Listener
* listener
) {
1108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1109 Listeners::iterator it
=
1110 std::find(listeners_
.begin(), listeners_
.end(), listener
);
1111 DCHECK(it
!= listeners_
.end());
1112 listeners_
.erase(it
);
1115 bool DevToolsAdbBridge::HasDevToolsWindow(const std::string
& agent_id
) {
1116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1117 return g_host_delegates
.Get().find(agent_id
) != g_host_delegates
.Get().end();
1120 DevToolsAdbBridge::~DevToolsAdbBridge() {
1121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1122 DCHECK(listeners_
.empty());
1125 void DevToolsAdbBridge::RequestRemoteDevices() {
1126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1127 if (!has_message_loop_
)
1130 new AdbPagesCommand(
1131 adb_thread_
, device_providers_
,
1132 base::Bind(&DevToolsAdbBridge::ReceivedRemoteDevices
, this));
1135 void DevToolsAdbBridge::ReceivedRemoteDevices(RemoteDevices
* devices_ptr
) {
1136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1138 scoped_ptr
<RemoteDevices
> devices(devices_ptr
);
1140 Listeners
copy(listeners_
);
1141 for (Listeners::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
1142 (*it
)->RemoteDevicesChanged(devices
.get());
1144 if (listeners_
.empty())
1147 BrowserThread::PostDelayedTask(
1150 base::Bind(&DevToolsAdbBridge::RequestRemoteDevices
, this),
1151 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs
));