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/keyed_service/content/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,
107 kChromeDefaultSocket,
111 "com.google.android.apps.chrome",
112 kChromeDefaultSocket,
116 "org.chromium.content_shell_apk",
117 "content_shell_devtools_remote",
121 "org.chromium.chrome.shell",
122 "chrome_shell_devtools_remote",
126 "org.chromium.android_webview.shell",
127 "webview_devtools_remote",
132 const BrowserDescriptor* FindBrowserDescriptor(const std::string& package) {
133 int count = sizeof(kBrowserDescriptors) / sizeof(kBrowserDescriptors[0]);
134 for (int i = 0; i < count; i++)
135 if (kBrowserDescriptors[i].package == package)
136 return &kBrowserDescriptors[i];
140 typedef std::map<std::string, const BrowserDescriptor*> DescriptorMap;
142 static DescriptorMap FindInstalledBrowserPackages(
143 const std::string& response) {
144 // Parse 'pm list packages' output which on Android looks like this:
146 // package:com.android.chrome
147 // package:com.chrome.beta
148 // package:com.example.app
150 DescriptorMap package_to_descriptor;
151 const std::string package_prefix = "package:";
152 std::vector<std::string> entries;
153 Tokenize(response, "'\r\n", &entries);
154 for (size_t i = 0; i < entries.size(); ++i) {
155 if (entries[i].find(package_prefix) != 0)
157 std::string package = entries[i].substr(package_prefix.size());
158 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
161 package_to_descriptor[descriptor->package] = descriptor;
163 return package_to_descriptor;
166 typedef std::map<std::string, std::string> StringMap;
168 static void MapProcessesToPackages(const std::string& response,
169 StringMap& pid_to_package,
170 StringMap& package_to_pid) {
171 // Parse 'ps' output which on Android looks like this:
173 // USER PID PPID VSIZE RSS WCHAN PC ? NAME
175 std::vector<std::string> entries;
176 Tokenize(response, "\n", &entries);
177 for (size_t i = 1; i < entries.size(); ++i) {
178 std::vector<std::string> fields;
179 Tokenize(entries[i], " \r", &fields);
180 if (fields.size() < 9)
182 std::string pid = fields[1];
183 std::string package = fields[8];
184 pid_to_package[pid] = package;
185 package_to_pid[package] = pid;
189 typedef std::map<std::string,
190 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> > BrowserMap;
192 static StringMap MapSocketsToProcesses(const std::string& response,
193 const std::string& channel_pattern) {
194 // Parse 'cat /proc/net/unix' output which on Android looks like this:
196 // Num RefCount Protocol Flags Type St Inode Path
197 // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
198 // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
199 // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
201 // We need to find records with paths starting from '@' (abstract socket)
202 // and containing the channel pattern ("_devtools_remote").
203 StringMap socket_to_pid;
204 std::vector<std::string> entries;
205 Tokenize(response, "\n", &entries);
206 for (size_t i = 1; i < entries.size(); ++i) {
207 std::vector<std::string> fields;
208 Tokenize(entries[i], " \r", &fields);
209 if (fields.size() < 8)
211 if (fields[3] != "00010000" || fields[5] != "01")
213 std::string path_field = fields[7];
214 if (path_field.size() < 1 || path_field[0] != '@')
216 size_t socket_name_pos = path_field.find(channel_pattern);
217 if (socket_name_pos == std::string::npos)
220 std::string socket = path_field.substr(1);
223 size_t socket_name_end = socket_name_pos + channel_pattern.size();
224 if (socket_name_end < path_field.size() &&
225 path_field[socket_name_end] == '_') {
226 pid = path_field.substr(socket_name_end + 1);
228 socket_to_pid[socket] = pid;
230 return socket_to_pid;
233 // AdbPagesCommand ------------------------------------------------------------
235 class AdbPagesCommand : public base::RefCountedThreadSafe<
237 BrowserThread::DeleteOnUIThread> {
239 typedef base::Callback<void(DevToolsAdbBridge::RemoteDevices*)> Callback;
242 scoped_refptr<RefCountedAdbThread> adb_thread,
243 const DevToolsAdbBridge::DeviceProviders& device_providers,
244 const Callback& callback);
247 friend struct BrowserThread::DeleteOnThread<
249 friend class base::DeleteHelper<AdbPagesCommand>;
251 virtual ~AdbPagesCommand();
252 void ProcessDeviceProviders();
253 void ReceivedDevices(const AndroidDevices& devices);
255 void ProcessSerials();
256 void ReceivedModel(int result, const std::string& response);
257 void ReceivedDumpsys(int result, const std::string& response);
258 void ReceivedPackages(int result, const std::string& response);
259 void ReceivedProcesses(
260 const std::string& packages_response,
262 const std::string& processes_response);
263 void ReceivedSockets(
264 const std::string& packages_response,
265 const std::string& processes_response,
267 const std::string& sockets_response);
268 void ProcessSockets();
269 void ReceivedVersion(int result, const std::string& response);
270 void ReceivedPages(int result, const std::string& response);
272 scoped_refptr<AndroidDevice> current_device() const {
273 return devices_.back();
276 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> current_browser() const {
277 return browsers_.back();
285 void CreateBrowsers(const std::string& packages_response,
286 const std::string& processes_response,
287 const std::string& sockets_response);
289 void ParseDumpsysResponse(const std::string& response);
290 void ParseScreenSize(const std::string& str);
292 scoped_refptr<RefCountedAdbThread> adb_thread_;
294 AndroidDevices devices_;
295 DevToolsAdbBridge::RemoteBrowsers browsers_;
296 scoped_ptr<DevToolsAdbBridge::RemoteDevices> remote_devices_;
297 DevToolsAdbBridge::DeviceProviders device_providers_;
300 AdbPagesCommand::AdbPagesCommand(
301 scoped_refptr<RefCountedAdbThread> adb_thread,
302 const DevToolsAdbBridge::DeviceProviders& device_providers,
303 const Callback& callback)
304 : adb_thread_(adb_thread),
306 device_providers_(device_providers) {
307 remote_devices_.reset(new DevToolsAdbBridge::RemoteDevices());
309 ProcessDeviceProviders();
312 AdbPagesCommand::~AdbPagesCommand() {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
316 void AdbPagesCommand::ProcessDeviceProviders() {
317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
318 if (device_providers_.empty()) {
319 adb_thread_->message_loop()->PostTask(
320 FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
324 const scoped_refptr<AndroidDeviceProvider>& device_provider =
325 device_providers_.back();
327 device_provider->QueryDevices(
328 base::Bind(&AdbPagesCommand::ReceivedDevices, this));
331 void AdbPagesCommand::ReceivedDevices(const AndroidDevices& devices) {
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
333 DCHECK(!device_providers_.empty());
334 device_providers_.pop_back();
336 devices_.insert(devices_.end(), devices.begin(), devices.end());
338 if (!device_providers_.empty()) {
339 ProcessDeviceProviders();
341 adb_thread_->message_loop()->PostTask(
342 FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
346 void AdbPagesCommand::ProcessSerials() {
347 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
348 if (devices_.size() == 0) {
349 BrowserThread::PostTask(
350 BrowserThread::UI, FROM_HERE,
351 base::Bind(&AdbPagesCommand::Respond, this));
355 scoped_refptr<AndroidDevice> device = current_device();
357 if (device->is_connected()) {
358 device->RunCommand(kDeviceModelCommand,
359 base::Bind(&AdbPagesCommand::ReceivedModel, this));
361 device->set_model(kUnknownModel);
362 remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
367 void AdbPagesCommand::ReceivedModel(int result, const std::string& response) {
368 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
373 scoped_refptr<AndroidDevice> device = current_device();
374 device->set_model(response);
375 remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
376 device->RunCommand(kDumpsysCommand,
377 base::Bind(&AdbPagesCommand::ReceivedDumpsys, this));
380 void AdbPagesCommand::ReceivedDumpsys(int result,
381 const std::string& response) {
382 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
384 ParseDumpsysResponse(response);
386 current_device()->RunCommand(
387 kInstalledChromePackagesCommand,
388 base::Bind(&AdbPagesCommand::ReceivedPackages, this));
391 void AdbPagesCommand::ReceivedPackages(int result,
392 const std::string& packages_response) {
393 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
398 current_device()->RunCommand(
399 kListProcessesCommand,
400 base::Bind(&AdbPagesCommand::ReceivedProcesses, this, packages_response));
403 void AdbPagesCommand::ReceivedProcesses(
404 const std::string& packages_response,
406 const std::string& processes_response) {
407 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
412 current_device()->RunCommand(
413 kOpenedUnixSocketsCommand,
414 base::Bind(&AdbPagesCommand::ReceivedSockets,
417 processes_response));
420 void AdbPagesCommand::ReceivedSockets(
421 const std::string& packages_response,
422 const std::string& processes_response,
424 const std::string& sockets_response) {
425 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
427 CreateBrowsers(packages_response, processes_response, sockets_response);
431 void AdbPagesCommand::ProcessSockets() {
432 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
433 if (browsers_.size() == 0) {
438 current_device()->HttpQuery(
439 current_browser()->socket(),
441 base::Bind(&AdbPagesCommand::ReceivedVersion, this));
444 void AdbPagesCommand::ReceivedVersion(int result,
445 const std::string& response) {
446 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
452 // Parse version, append to package name if available,
453 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
454 base::DictionaryValue* dict;
455 if (value && value->GetAsDictionary(&dict)) {
457 if (dict->GetString("Browser", &browser)) {
458 std::vector<std::string> parts;
459 Tokenize(browser, "/", &parts);
460 if (parts.size() == 2)
461 current_browser()->set_version(parts[1]);
463 current_browser()->set_version(browser);
466 if (dict->GetString("Android-Package", &package)) {
467 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
469 current_browser()->set_display_name(descriptor->display_name);
473 current_device()->HttpQuery(
474 current_browser()->socket(),
476 base::Bind(&AdbPagesCommand::ReceivedPages, this));
479 void AdbPagesCommand::ReceivedPages(int result,
480 const std::string& response) {
481 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
483 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
484 base::ListValue* list_value;
485 if (value && value->GetAsList(&list_value))
486 current_browser()->SetPageDescriptors(*list_value);
491 void AdbPagesCommand::NextBrowser() {
492 browsers_.pop_back();
496 void AdbPagesCommand::NextDevice() {
501 void AdbPagesCommand::Respond() {
502 callback_.Run(remote_devices_.release());
505 void AdbPagesCommand::CreateBrowsers(
506 const std::string& packages_response,
507 const std::string& processes_response,
508 const std::string& sockets_response) {
509 DescriptorMap package_to_descriptor =
510 FindInstalledBrowserPackages(packages_response);
512 StringMap pid_to_package;
513 StringMap package_to_pid;
514 MapProcessesToPackages(processes_response, pid_to_package, package_to_pid);
516 const std::string channel_pattern =
517 base::StringPrintf(kDevToolsChannelNameFormat, "");
519 StringMap socket_to_pid = MapSocketsToProcesses(sockets_response,
522 scoped_refptr<DevToolsAdbBridge::RemoteDevice> remote_device =
523 remote_devices_->back();
525 // Create RemoteBrowser instances.
526 BrowserMap package_to_running_browser;
527 BrowserMap socket_to_unnamed_browser;
528 for (StringMap::iterator it = socket_to_pid.begin();
529 it != socket_to_pid.end(); ++it) {
530 std::string socket = it->first;
531 std::string pid = it->second;
533 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser =
534 new DevToolsAdbBridge::RemoteBrowser(
535 adb_thread_, remote_device->device(), socket);
537 StringMap::iterator pit = pid_to_package.find(pid);
538 if (pit != pid_to_package.end()) {
539 std::string package = pit->second;
540 package_to_running_browser[package] = browser;
541 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
543 browser->set_display_name(descriptor->display_name);
544 } else if (socket.find(kWebViewSocketPrefix) == 0) {
545 browser->set_display_name(
546 base::StringPrintf(kWebViewNameTemplate, package.c_str()));
548 browser->set_display_name(package);
551 // Set fallback display name.
552 std::string name = socket.substr(0, socket.find(channel_pattern));
553 name[0] = base::ToUpperASCII(name[0]);
554 browser->set_display_name(name);
556 socket_to_unnamed_browser[socket] = browser;
558 remote_device->AddBrowser(browser);
561 browsers_ = remote_device->browsers();
563 // Find installed packages not mapped to browsers.
564 typedef std::multimap<std::string, const BrowserDescriptor*>
566 DescriptorMultimap socket_to_descriptor;
567 for (DescriptorMap::iterator it = package_to_descriptor.begin();
568 it != package_to_descriptor.end(); ++it) {
569 std::string package = it->first;
570 const BrowserDescriptor* descriptor = it->second;
572 if (package_to_running_browser.find(package) !=
573 package_to_running_browser.end())
574 continue; // This package is already mapped to a browser.
576 if (package_to_pid.find(package) != package_to_pid.end()) {
577 // This package is running but not mapped to a browser.
578 socket_to_descriptor.insert(
579 DescriptorMultimap::value_type(descriptor->socket, descriptor));
584 // Try naming remaining unnamed browsers.
585 for (DescriptorMultimap::iterator it = socket_to_descriptor.begin();
586 it != socket_to_descriptor.end(); ++it) {
587 std::string socket = it->first;
588 const BrowserDescriptor* descriptor = it->second;
590 if (socket_to_descriptor.count(socket) != 1)
591 continue; // No definitive match.
593 BrowserMap::iterator bit = socket_to_unnamed_browser.find(socket);
594 if (bit != socket_to_unnamed_browser.end())
595 bit->second->set_display_name(descriptor->display_name);
599 void AdbPagesCommand::ParseDumpsysResponse(const std::string& response) {
600 std::vector<std::string> lines;
601 Tokenize(response, "\r", &lines);
602 for (size_t i = 0; i < lines.size(); ++i) {
603 std::string line = lines[i];
604 size_t pos = line.find(kDumpsysScreenSizePrefix);
605 if (pos != std::string::npos) {
607 line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
613 void AdbPagesCommand::ParseScreenSize(const std::string& str) {
614 std::vector<std::string> pairs;
615 Tokenize(str, "-", &pairs);
616 if (pairs.size() != 2)
621 std::vector<std::string> numbers;
622 Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
623 if (numbers.size() != 2 ||
624 !base::StringToInt(numbers[0], &width) ||
625 !base::StringToInt(numbers[1], &height))
628 remote_devices_->back()->set_screen_size(gfx::Size(width, height));
632 // AdbProtocolCommand ---------------------------------------------------------
634 class AdbProtocolCommand : public AdbWebSocket::Delegate {
637 scoped_refptr<RefCountedAdbThread> adb_thread,
638 scoped_refptr<AndroidDevice> device,
639 const std::string& socket_name,
640 const std::string& debug_url,
641 const std::string& command);
644 virtual void OnSocketOpened() OVERRIDE;
645 virtual void OnFrameRead(const std::string& message) OVERRIDE;
646 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
647 virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE;
649 scoped_refptr<RefCountedAdbThread> adb_thread_;
650 const std::string command_;
651 scoped_refptr<AdbWebSocket> web_socket_;
653 DISALLOW_COPY_AND_ASSIGN(AdbProtocolCommand);
656 AdbProtocolCommand::AdbProtocolCommand(
657 scoped_refptr<RefCountedAdbThread> adb_thread,
658 scoped_refptr<AndroidDevice> device,
659 const std::string& socket_name,
660 const std::string& debug_url,
661 const std::string& command)
662 : adb_thread_(adb_thread),
664 web_socket_ = new AdbWebSocket(
665 device, socket_name, debug_url, adb_thread_->message_loop(), this);
668 void AdbProtocolCommand::OnSocketOpened() {
669 web_socket_->SendFrame(command_);
670 web_socket_->Disconnect();
673 void AdbProtocolCommand::OnFrameRead(const std::string& message) {}
675 void AdbProtocolCommand::OnSocketClosed(bool closed_by_device) {
679 bool AdbProtocolCommand::ProcessIncomingMessage(const std::string& message) {
685 const char kDevToolsChannelNameFormat[] = "%s_devtools_remote";
687 class AgentHostDelegate;
689 typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates;
691 base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates =
692 LAZY_INSTANCE_INITIALIZER;
694 DevToolsAdbBridge::Wrapper::Wrapper() {
695 bridge_ = new DevToolsAdbBridge();
698 DevToolsAdbBridge::Wrapper::~Wrapper() {
701 DevToolsAdbBridge* DevToolsAdbBridge::Wrapper::Get() {
702 return bridge_.get();
706 DevToolsAdbBridge::Factory* DevToolsAdbBridge::Factory::GetInstance() {
707 return Singleton<DevToolsAdbBridge::Factory>::get();
711 DevToolsAdbBridge* DevToolsAdbBridge::Factory::GetForProfile(
713 DevToolsAdbBridge::Wrapper* wrapper =
714 static_cast<DevToolsAdbBridge::Wrapper*>(GetInstance()->
715 GetServiceForBrowserContext(profile, true));
716 return wrapper ? wrapper->Get() : NULL;
719 DevToolsAdbBridge::Factory::Factory()
720 : BrowserContextKeyedServiceFactory(
722 BrowserContextDependencyManager::GetInstance()) {}
724 DevToolsAdbBridge::Factory::~Factory() {}
726 KeyedService* DevToolsAdbBridge::Factory::BuildServiceInstanceFor(
727 content::BrowserContext* context) const {
728 return new DevToolsAdbBridge::Wrapper();
732 // AgentHostDelegate ----------------------------------------------------------
734 class AgentHostDelegate : public content::DevToolsExternalAgentProxyDelegate,
735 public AdbWebSocket::Delegate {
737 static void Create(const std::string& id,
738 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
739 const std::string& debug_url,
740 const std::string& frontend_url,
742 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
743 AgentHostDelegates::iterator it =
744 g_host_delegates.Get().find(id);
745 if (it != g_host_delegates.Get().end()) {
746 it->second->OpenFrontend();
747 } else if (!frontend_url.empty()) {
748 new AgentHostDelegate(
749 id, browser->device(), browser->socket(), debug_url,
750 frontend_url, browser->adb_thread()->message_loop(), profile);
756 const std::string& id,
757 scoped_refptr<AndroidDevice> device,
758 const std::string& socket_name,
759 const std::string& debug_url,
760 const std::string& frontend_url,
761 base::MessageLoop* adb_message_loop,
764 frontend_url_(frontend_url),
766 web_socket_ = new AdbWebSocket(
767 device, socket_name, debug_url, adb_message_loop, this);
768 g_host_delegates.Get()[id] = this;
770 if (socket_name.find(kWebViewSocketPrefix) == 0) {
771 content::RecordAction(
772 base::UserMetricsAction("DevTools_InspectAndroidWebView"));
774 content::RecordAction(
775 base::UserMetricsAction("DevTools_InspectAndroidPage"));
779 void OpenFrontend() {
782 DevToolsWindow::OpenExternalFrontend(
783 profile_, frontend_url_, proxy_->GetAgentHost().get());
786 virtual ~AgentHostDelegate() {
787 g_host_delegates.Get().erase(id_);
790 virtual void Attach() OVERRIDE {}
792 virtual void Detach() OVERRIDE {
793 web_socket_->Disconnect();
796 virtual void SendMessageToBackend(const std::string& message) OVERRIDE {
797 web_socket_->SendFrame(message);
800 virtual void OnSocketOpened() OVERRIDE {
801 proxy_.reset(content::DevToolsExternalAgentProxy::Create(this));
805 virtual void OnFrameRead(const std::string& message) OVERRIDE {
806 proxy_->DispatchOnClientHost(message);
809 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE {
810 if (proxy_ && closed_by_device)
811 proxy_->ConnectionClosed();
815 virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE {
819 const std::string id_;
820 const std::string frontend_url_;
823 scoped_ptr<content::DevToolsExternalAgentProxy> proxy_;
824 scoped_refptr<AdbWebSocket> web_socket_;
825 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
828 //// RemotePageTarget ----------------------------------------------
830 class RemotePageTarget : public DevToolsTargetImpl {
832 RemotePageTarget(scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
833 const base::DictionaryValue& value);
834 virtual ~RemotePageTarget();
836 // content::DevToolsTarget overrides:
837 virtual bool IsAttached() const OVERRIDE;
838 virtual bool Activate() const OVERRIDE;
839 virtual bool Close() const OVERRIDE;
841 // DevToolsTargetImpl overrides:
842 virtual void Inspect(Profile* profile) const OVERRIDE;
843 virtual void Reload() const OVERRIDE;
845 void Navigate(const std::string& url) const;
848 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser_;
849 std::string debug_url_;
850 std::string frontend_url_;
851 std::string remote_id_;
852 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget);
855 RemotePageTarget::RemotePageTarget(
856 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
857 const base::DictionaryValue& value)
858 : browser_(browser) {
860 value.GetString("id", &remote_id_);
862 value.GetString("url", &url);
864 value.GetString("title", &title_);
865 title_ = base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(title_)));
866 value.GetString("description", &description_);
867 std::string favicon_url;
868 value.GetString("faviconUrl", &favicon_url);
869 favicon_url_ = GURL(favicon_url);
870 value.GetString("webSocketDebuggerUrl", &debug_url_);
871 value.GetString("devtoolsFrontendUrl", &frontend_url_);
873 if (remote_id_.empty() && !debug_url_.empty()) {
874 // Target id is not available until Chrome 26. Use page id at the end of
875 // debug_url_ instead. For attached targets the id will remain empty.
876 std::vector<std::string> parts;
877 Tokenize(debug_url_, "/", &parts);
878 remote_id_ = parts[parts.size()-1];
881 if (debug_url_.find("ws://") == 0)
882 debug_url_ = debug_url_.substr(5);
886 size_t ws_param = frontend_url_.find("?ws");
887 if (ws_param != std::string::npos)
888 frontend_url_ = frontend_url_.substr(0, ws_param);
889 if (frontend_url_.find("http:") == 0)
890 frontend_url_ = "https:" + frontend_url_.substr(5);
892 id_ = base::StringPrintf("%s:%s:%s",
893 browser_->device()->serial().c_str(),
894 browser_->socket().c_str(),
898 RemotePageTarget::~RemotePageTarget() {
901 bool RemotePageTarget::IsAttached() const {
902 return debug_url_.empty();
905 void RemotePageTarget::Inspect(Profile* profile) const {
906 std::string request = base::StringPrintf(kActivatePageRequest,
908 base::Closure inspect_callback = base::Bind(&AgentHostDelegate::Create,
909 id_, browser_, debug_url_, frontend_url_, profile);
910 browser_->SendJsonRequest(request, inspect_callback);
913 bool RemotePageTarget::Activate() const {
914 std::string request = base::StringPrintf(kActivatePageRequest,
916 browser_->SendJsonRequest(request, base::Closure());
920 bool RemotePageTarget::Close() const {
923 std::string request = base::StringPrintf(kClosePageRequest,
925 browser_->SendJsonRequest(request, base::Closure());
929 void RemotePageTarget::Reload() const {
930 browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL);
933 void RemotePageTarget::Navigate(const std::string& url) const {
934 base::DictionaryValue params;
935 params.SetString(kUrlParam, url);
936 browser_->SendProtocolCommand(debug_url_, kPageNavigateCommand, ¶ms);
939 // DevToolsAdbBridge::RemoteBrowser -------------------------------------------
941 DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
942 scoped_refptr<RefCountedAdbThread> adb_thread,
943 scoped_refptr<AndroidDevice> device,
944 const std::string& socket)
945 : adb_thread_(adb_thread),
948 page_descriptors_(new base::ListValue()) {
951 bool DevToolsAdbBridge::RemoteBrowser::IsChrome() const {
952 return socket_.find(kChromeDefaultSocket) == 0;
955 DevToolsAdbBridge::RemoteBrowser::ParsedVersion
956 DevToolsAdbBridge::RemoteBrowser::GetParsedVersion() const {
957 ParsedVersion result;
958 std::vector<std::string> parts;
959 Tokenize(version_, ".", &parts);
960 for (size_t i = 0; i != parts.size(); ++i) {
962 base::StringToInt(parts[i], &value);
963 result.push_back(value);
968 std::vector<DevToolsTargetImpl*>
969 DevToolsAdbBridge::RemoteBrowser::CreatePageTargets() {
970 std::vector<DevToolsTargetImpl*> result;
971 for (size_t i = 0; i < page_descriptors_->GetSize(); ++i) {
973 page_descriptors_->Get(i, &item);
976 base::DictionaryValue* dict;
977 if (!item->GetAsDictionary(&dict))
979 result.push_back(new RemotePageTarget(this, *dict));
984 void DevToolsAdbBridge::RemoteBrowser::SetPageDescriptors(
985 const base::ListValue& list) {
986 page_descriptors_.reset(list.DeepCopy());
989 static void RespondOnUIThread(base::Closure callback, int, const std::string&) {
990 if (!callback.is_null())
991 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
994 void DevToolsAdbBridge::RemoteBrowser::SendJsonRequest(
995 const std::string& request, base::Closure callback) {
996 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
997 adb_thread_->message_loop()->PostTask(FROM_HERE,
998 base::Bind(&AndroidDevice::HttpQuery, device_, socket_, request,
999 base::Bind(&RespondOnUIThread, callback)));
1002 void DevToolsAdbBridge::RemoteBrowser::SendProtocolCommand(
1003 const std::string& debug_url,
1004 const std::string& method,
1005 base::DictionaryValue* params) {
1006 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1007 if (debug_url.empty())
1009 DevToolsProtocol::Command command(1, method, params);
1010 new AdbProtocolCommand(
1011 adb_thread_, device_, socket_, debug_url, command.Serialize());
1014 static void NoOp(int, const std::string&) {}
1016 void DevToolsAdbBridge::RemoteBrowser::Open(const std::string& input_url) {
1017 GURL gurl(input_url);
1018 if (!gurl.is_valid()) {
1019 gurl = GURL("http://" + input_url);
1020 if (!gurl.is_valid())
1023 std::string url = gurl.spec();
1025 ParsedVersion parsed_version = GetParsedVersion();
1027 !parsed_version.empty() &&
1028 parsed_version[0] >= kMinVersionNewWithURL) {
1029 std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
1030 std::string request =
1031 base::StringPrintf(kNewPageRequestWithURL, query.c_str());
1032 adb_thread_->message_loop()->PostTask(FROM_HERE,
1033 base::Bind(&AndroidDevice::HttpQuery,
1034 device_, socket_, request, base::Bind(&NoOp)));
1036 adb_thread_->message_loop()->PostTask(FROM_HERE,
1037 base::Bind(&AndroidDevice::HttpQuery,
1038 device_, socket_, kNewPageRequest,
1039 base::Bind(&RemoteBrowser::PageCreatedOnHandlerThread, this, url)));
1043 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnHandlerThread(
1044 const std::string& url, int result, const std::string& response) {
1047 // Navigating too soon after the page creation breaks navigation history
1048 // (crbug.com/311014). This can be avoided by adding a moderate delay.
1049 BrowserThread::PostDelayedTask(
1050 BrowserThread::UI, FROM_HERE,
1051 base::Bind(&RemoteBrowser::PageCreatedOnUIThread, this, response, url),
1052 base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs));
1055 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnUIThread(
1056 const std::string& response, const std::string& url) {
1057 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
1058 base::DictionaryValue* dict;
1059 if (value && value->GetAsDictionary(&dict)) {
1060 RemotePageTarget new_page(this, *dict);
1061 new_page.Navigate(url);
1065 DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
1069 // DevToolsAdbBridge::RemoteDevice --------------------------------------------
1071 DevToolsAdbBridge::RemoteDevice::RemoteDevice(
1072 scoped_refptr<AndroidDevice> device)
1076 std::string DevToolsAdbBridge::RemoteDevice::GetSerial() {
1077 return device_->serial();
1080 std::string DevToolsAdbBridge::RemoteDevice::GetModel() {
1081 return device_->model();
1084 bool DevToolsAdbBridge::RemoteDevice::IsConnected() {
1085 return device_->is_connected();
1088 void DevToolsAdbBridge::RemoteDevice::AddBrowser(
1089 scoped_refptr<RemoteBrowser> browser) {
1090 browsers_.push_back(browser);
1093 DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
1097 // DevToolsAdbBridge ----------------------------------------------------------
1099 DevToolsAdbBridge::DevToolsAdbBridge()
1100 : adb_thread_(RefCountedAdbThread::GetInstance()),
1101 has_message_loop_(adb_thread_->message_loop() != NULL) {
1104 void DevToolsAdbBridge::set_device_provider_for_test(
1105 scoped_refptr<AndroidDeviceProvider> device_provider) {
1106 device_providers_for_test_.clear();
1107 device_providers_for_test_.push_back(device_provider);
1110 void DevToolsAdbBridge::AddListener(Listener* listener) {
1111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1112 if (listeners_.empty())
1113 RequestRemoteDevices();
1114 listeners_.push_back(listener);
1117 void DevToolsAdbBridge::RemoveListener(Listener* listener) {
1118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1119 Listeners::iterator it =
1120 std::find(listeners_.begin(), listeners_.end(), listener);
1121 DCHECK(it != listeners_.end());
1122 listeners_.erase(it);
1125 bool DevToolsAdbBridge::HasDevToolsWindow(const std::string& agent_id) {
1126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1127 return g_host_delegates.Get().find(agent_id) != g_host_delegates.Get().end();
1130 DevToolsAdbBridge::~DevToolsAdbBridge() {
1131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1132 DCHECK(listeners_.empty());
1135 void DevToolsAdbBridge::RequestRemoteDevices() {
1136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1137 if (!has_message_loop_)
1140 new AdbPagesCommand(
1142 device_providers_for_test_.size() ?
1143 device_providers_for_test_ :
1145 base::Bind(&DevToolsAdbBridge::ReceivedRemoteDevices, this));
1148 void DevToolsAdbBridge::ReceivedRemoteDevices(RemoteDevices* devices_ptr) {
1149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1151 scoped_ptr<RemoteDevices> devices(devices_ptr);
1153 Listeners copy(listeners_);
1154 for (Listeners::iterator it = copy.begin(); it != copy.end(); ++it)
1155 (*it)->RemoteDevicesChanged(devices.get());
1157 if (listeners_.empty())
1160 BrowserThread::PostDelayedTask(
1163 base::Bind(&DevToolsAdbBridge::RequestRemoteDevices, this),
1164 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));