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
),
761 adb_message_loop_(adb_message_loop
),
763 web_socket_
= new AdbWebSocket(
764 device
, socket_name
, debug_url
, adb_message_loop
, this);
765 g_host_delegates
.Get()[id
] = this;
767 if (socket_name
.find(kWebViewSocketPrefix
) == 0) {
768 content::RecordAction(
769 base::UserMetricsAction("DevTools_InspectAndroidWebView"));
771 content::RecordAction(
772 base::UserMetricsAction("DevTools_InspectAndroidPage"));
776 void OpenFrontend() {
779 DevToolsWindow::OpenExternalFrontend(
780 profile_
, frontend_url_
, proxy_
->GetAgentHost().get());
783 virtual ~AgentHostDelegate() {
784 g_host_delegates
.Get().erase(id_
);
787 virtual void Attach() OVERRIDE
{}
789 virtual void Detach() OVERRIDE
{
790 web_socket_
->Disconnect();
793 virtual void SendMessageToBackend(const std::string
& message
) OVERRIDE
{
794 web_socket_
->SendFrame(message
);
797 virtual void OnSocketOpened() OVERRIDE
{
798 proxy_
.reset(content::DevToolsExternalAgentProxy::Create(this));
802 virtual void OnFrameRead(const std::string
& message
) OVERRIDE
{
803 proxy_
->DispatchOnClientHost(message
);
806 virtual void OnSocketClosed(bool closed_by_device
) OVERRIDE
{
807 if (proxy_
&& closed_by_device
)
808 proxy_
->ConnectionClosed();
812 virtual bool ProcessIncomingMessage(const std::string
& message
) OVERRIDE
{
816 const std::string id_
;
817 const std::string frontend_url_
;
818 base::MessageLoop
* adb_message_loop_
;
821 scoped_ptr
<content::DevToolsExternalAgentProxy
> proxy_
;
822 scoped_refptr
<AdbWebSocket
> web_socket_
;
823 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate
);
826 //// RemotePageTarget ----------------------------------------------
828 class RemotePageTarget
: public DevToolsTargetImpl
{
830 RemotePageTarget(scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser
,
831 const base::DictionaryValue
& value
);
832 virtual ~RemotePageTarget();
834 // content::DevToolsTarget overrides:
835 virtual bool IsAttached() const OVERRIDE
;
836 virtual bool Activate() const OVERRIDE
;
837 virtual bool Close() const OVERRIDE
;
839 // DevToolsTargetImpl overrides:
840 virtual void Inspect(Profile
* profile
) const OVERRIDE
;
841 virtual void Reload() const OVERRIDE
;
843 void Navigate(const std::string
& url
) const;
846 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser_
;
847 std::string debug_url_
;
848 std::string frontend_url_
;
849 std::string agent_id_
;
850 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget
);
853 RemotePageTarget::RemotePageTarget(
854 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser
,
855 const base::DictionaryValue
& value
)
856 : browser_(browser
) {
858 value
.GetString("id", &id_
);
860 value
.GetString("url", &url
);
862 value
.GetString("title", &title_
);
863 title_
= base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(title_
)));
864 value
.GetString("description", &description_
);
865 std::string favicon_url
;
866 value
.GetString("faviconUrl", &favicon_url
);
867 favicon_url_
= GURL(favicon_url
);
868 value
.GetString("webSocketDebuggerUrl", &debug_url_
);
869 value
.GetString("devtoolsFrontendUrl", &frontend_url_
);
871 if (id_
.empty() && !debug_url_
.empty()) {
872 // Target id is not available until Chrome 26. Use page id at the end of
873 // debug_url_ instead. For attached targets the id will remain empty.
874 std::vector
<std::string
> parts
;
875 Tokenize(debug_url_
, "/", &parts
);
876 id_
= parts
[parts
.size()-1];
879 if (debug_url_
.find("ws://") == 0)
880 debug_url_
= debug_url_
.substr(5);
884 size_t ws_param
= frontend_url_
.find("?ws");
885 if (ws_param
!= std::string::npos
)
886 frontend_url_
= frontend_url_
.substr(0, ws_param
);
887 if (frontend_url_
.find("http:") == 0)
888 frontend_url_
= "https:" + frontend_url_
.substr(5);
890 agent_id_
= base::StringPrintf("%s:%s:%s",
891 browser_
->device()->serial().c_str(),
892 browser_
->socket().c_str(),
896 RemotePageTarget::~RemotePageTarget() {
899 bool RemotePageTarget::IsAttached() const {
900 return debug_url_
.empty();
903 void RemotePageTarget::Inspect(Profile
* profile
) const {
904 std::string request
= base::StringPrintf(kActivatePageRequest
, id_
.c_str());
905 base::Closure inspect_callback
= base::Bind(&AgentHostDelegate::Create
,
906 id_
, browser_
, debug_url_
, frontend_url_
, profile
);
907 browser_
->SendJsonRequest(request
, inspect_callback
);
910 bool RemotePageTarget::Activate() const {
911 std::string request
= base::StringPrintf(kActivatePageRequest
, id_
.c_str());
912 browser_
->SendJsonRequest(request
, base::Closure());
916 bool RemotePageTarget::Close() const {
919 std::string request
= base::StringPrintf(kClosePageRequest
, id_
.c_str());
920 browser_
->SendJsonRequest(request
, base::Closure());
924 void RemotePageTarget::Reload() const {
925 browser_
->SendProtocolCommand(debug_url_
, kPageReloadCommand
, NULL
);
928 void RemotePageTarget::Navigate(const std::string
& url
) const {
929 base::DictionaryValue params
;
930 params
.SetString(kUrlParam
, url
);
931 browser_
->SendProtocolCommand(debug_url_
, kPageNavigateCommand
, ¶ms
);
934 // DevToolsAdbBridge::RemoteBrowser -------------------------------------------
936 DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
937 scoped_refptr
<RefCountedAdbThread
> adb_thread
,
938 scoped_refptr
<AndroidDevice
> device
,
939 const std::string
& socket
)
940 : adb_thread_(adb_thread
),
943 page_descriptors_(new base::ListValue()) {
946 bool DevToolsAdbBridge::RemoteBrowser::IsChrome() const {
947 return socket_
.find(kChromeDefaultSocket
) == 0;
950 DevToolsAdbBridge::RemoteBrowser::ParsedVersion
951 DevToolsAdbBridge::RemoteBrowser::GetParsedVersion() const {
952 ParsedVersion result
;
953 std::vector
<std::string
> parts
;
954 Tokenize(version_
, ".", &parts
);
955 for (size_t i
= 0; i
!= parts
.size(); ++i
) {
957 base::StringToInt(parts
[i
], &value
);
958 result
.push_back(value
);
963 std::vector
<DevToolsTargetImpl
*>
964 DevToolsAdbBridge::RemoteBrowser::CreatePageTargets() {
965 std::vector
<DevToolsTargetImpl
*> result
;
966 for (size_t i
= 0; i
< page_descriptors_
->GetSize(); ++i
) {
968 page_descriptors_
->Get(i
, &item
);
971 base::DictionaryValue
* dict
;
972 if (!item
->GetAsDictionary(&dict
))
974 result
.push_back(new RemotePageTarget(this, *dict
));
979 void DevToolsAdbBridge::RemoteBrowser::SetPageDescriptors(
980 const base::ListValue
& list
) {
981 page_descriptors_
.reset(list
.DeepCopy());
984 static void RespondOnUIThread(base::Closure callback
, int, const std::string
&) {
985 if (!callback
.is_null())
986 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, callback
);
989 void DevToolsAdbBridge::RemoteBrowser::SendJsonRequest(
990 const std::string
& request
, base::Closure callback
) {
991 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
992 adb_thread_
->message_loop()->PostTask(FROM_HERE
,
993 base::Bind(&AndroidDevice::HttpQuery
, device_
, socket_
, request
,
994 base::Bind(&RespondOnUIThread
, callback
)));
997 void DevToolsAdbBridge::RemoteBrowser::SendProtocolCommand(
998 const std::string
& debug_url
,
999 const std::string
& method
,
1000 base::DictionaryValue
* params
) {
1001 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1002 if (debug_url
.empty())
1004 DevToolsProtocol::Command
command(1, method
, params
);
1005 new AdbProtocolCommand(
1006 adb_thread_
, device_
, socket_
, debug_url
, command
.Serialize());
1009 static void NoOp(int, const std::string
&) {}
1011 void DevToolsAdbBridge::RemoteBrowser::Open(const std::string
& input_url
) {
1012 GURL
gurl(input_url
);
1013 if (!gurl
.is_valid()) {
1014 gurl
= GURL("http://" + input_url
);
1015 if (!gurl
.is_valid())
1018 std::string url
= gurl
.spec();
1020 ParsedVersion parsed_version
= GetParsedVersion();
1022 !parsed_version
.empty() &&
1023 parsed_version
[0] >= kMinVersionNewWithURL
) {
1024 std::string query
= net::EscapeQueryParamValue(url
, false /* use_plus */);
1025 std::string request
=
1026 base::StringPrintf(kNewPageRequestWithURL
, query
.c_str());
1027 adb_thread_
->message_loop()->PostTask(FROM_HERE
,
1028 base::Bind(&AndroidDevice::HttpQuery
,
1029 device_
, socket_
, request
, base::Bind(&NoOp
)));
1031 adb_thread_
->message_loop()->PostTask(FROM_HERE
,
1032 base::Bind(&AndroidDevice::HttpQuery
,
1033 device_
, socket_
, kNewPageRequest
,
1034 base::Bind(&RemoteBrowser::PageCreatedOnHandlerThread
, this, url
)));
1038 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnHandlerThread(
1039 const std::string
& url
, int result
, const std::string
& response
) {
1042 // Navigating too soon after the page creation breaks navigation history
1043 // (crbug.com/311014). This can be avoided by adding a moderate delay.
1044 BrowserThread::PostDelayedTask(
1045 BrowserThread::UI
, FROM_HERE
,
1046 base::Bind(&RemoteBrowser::PageCreatedOnUIThread
, this, response
, url
),
1047 base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs
));
1050 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnUIThread(
1051 const std::string
& response
, const std::string
& url
) {
1052 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response
));
1053 base::DictionaryValue
* dict
;
1054 if (value
&& value
->GetAsDictionary(&dict
)) {
1055 RemotePageTarget
new_page(this, *dict
);
1056 new_page
.Navigate(url
);
1060 DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
1064 // DevToolsAdbBridge::RemoteDevice --------------------------------------------
1066 DevToolsAdbBridge::RemoteDevice::RemoteDevice(
1067 scoped_refptr
<AndroidDevice
> device
)
1071 std::string
DevToolsAdbBridge::RemoteDevice::GetSerial() {
1072 return device_
->serial();
1075 std::string
DevToolsAdbBridge::RemoteDevice::GetModel() {
1076 return device_
->model();
1079 bool DevToolsAdbBridge::RemoteDevice::IsConnected() {
1080 return device_
->is_connected();
1083 void DevToolsAdbBridge::RemoteDevice::AddBrowser(
1084 scoped_refptr
<RemoteBrowser
> browser
) {
1085 browsers_
.push_back(browser
);
1088 DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
1092 // DevToolsAdbBridge ----------------------------------------------------------
1094 DevToolsAdbBridge::DevToolsAdbBridge()
1095 : adb_thread_(RefCountedAdbThread::GetInstance()),
1096 has_message_loop_(adb_thread_
->message_loop() != NULL
) {
1099 void DevToolsAdbBridge::AddListener(Listener
* listener
) {
1100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1101 if (listeners_
.empty())
1102 RequestRemoteDevices();
1103 listeners_
.push_back(listener
);
1106 void DevToolsAdbBridge::RemoveListener(Listener
* listener
) {
1107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1108 Listeners::iterator it
=
1109 std::find(listeners_
.begin(), listeners_
.end(), listener
);
1110 DCHECK(it
!= listeners_
.end());
1111 listeners_
.erase(it
);
1114 bool DevToolsAdbBridge::HasDevToolsWindow(const std::string
& agent_id
) {
1115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1116 return g_host_delegates
.Get().find(agent_id
) != g_host_delegates
.Get().end();
1119 DevToolsAdbBridge::~DevToolsAdbBridge() {
1120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1121 DCHECK(listeners_
.empty());
1124 void DevToolsAdbBridge::RequestRemoteDevices() {
1125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1126 if (!has_message_loop_
)
1129 new AdbPagesCommand(
1130 adb_thread_
, device_providers_
,
1131 base::Bind(&DevToolsAdbBridge::ReceivedRemoteDevices
, this));
1134 void DevToolsAdbBridge::ReceivedRemoteDevices(RemoteDevices
* devices_ptr
) {
1135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1137 scoped_ptr
<RemoteDevices
> devices(devices_ptr
);
1139 Listeners
copy(listeners_
);
1140 for (Listeners::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
1141 (*it
)->RemoteDevicesChanged(devices
.get());
1143 if (listeners_
.empty())
1146 BrowserThread::PostDelayedTask(
1149 base::Bind(&DevToolsAdbBridge::RequestRemoteDevices
, this),
1150 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs
));