Home | History | Annotate | Download | only in android
      1 // Copyright (c) 2012 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.
      4 
      5 #include "chrome/browser/android/dev_tools_server.h"
      6 
      7 #include <pwd.h>
      8 #include <cstring>
      9 
     10 #include "base/android/jni_string.h"
     11 #include "base/basictypes.h"
     12 #include "base/bind.h"
     13 #include "base/callback.h"
     14 #include "base/command_line.h"
     15 #include "base/compiler_specific.h"
     16 #include "base/files/file_path.h"
     17 #include "base/logging.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/strings/stringprintf.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "chrome/browser/android/tab_android.h"
     22 #include "chrome/browser/browser_process.h"
     23 #include "chrome/browser/history/top_sites.h"
     24 #include "chrome/browser/profiles/profile_manager.h"
     25 #include "chrome/browser/ui/android/tab_model/tab_model.h"
     26 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
     27 #include "content/public/browser/android/devtools_auth.h"
     28 #include "content/public/browser/browser_thread.h"
     29 #include "content/public/browser/devtools_agent_host.h"
     30 #include "content/public/browser/devtools_http_handler.h"
     31 #include "content/public/browser/devtools_http_handler_delegate.h"
     32 #include "content/public/browser/devtools_target.h"
     33 #include "content/public/browser/favicon_status.h"
     34 #include "content/public/browser/navigation_entry.h"
     35 #include "content/public/browser/render_view_host.h"
     36 #include "content/public/browser/web_contents.h"
     37 #include "content/public/browser/web_contents_delegate.h"
     38 #include "content/public/common/content_switches.h"
     39 #include "content/public/common/url_constants.h"
     40 #include "content/public/common/user_agent.h"
     41 #include "grit/browser_resources.h"
     42 #include "jni/DevToolsServer_jni.h"
     43 #include "net/base/net_errors.h"
     44 #include "net/socket/unix_domain_listen_socket_posix.h"
     45 #include "net/socket/unix_domain_server_socket_posix.h"
     46 #include "net/url_request/url_request_context_getter.h"
     47 #include "ui/base/resource/resource_bundle.h"
     48 
     49 using content::DevToolsAgentHost;
     50 using content::RenderViewHost;
     51 using content::WebContents;
     52 
     53 namespace {
     54 
     55 // TL;DR: Do not change this string.
     56 //
     57 // Desktop Chrome relies on this format to identify debuggable apps on Android
     58 // (see the code under chrome/browser/devtools/device).
     59 // If this string ever changes it would not be sufficient to change the
     60 // corresponding string on the client side. Since debugging an older version of
     61 // Chrome for Android from a newer version of desktop Chrome is a very common
     62 // scenario, the client code will have to be modified to recognize both the old
     63 // and the new format.
     64 const char kDevToolsChannelNameFormat[] = "%s_devtools_remote";
     65 
     66 const char kFrontEndURL[] =
     67     "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html";
     68 const char kTetheringSocketName[] = "chrome_devtools_tethering_%d_%d";
     69 
     70 bool AuthorizeSocketAccessWithDebugPermission(
     71     const net::UnixDomainServerSocket::Credentials& credentials) {
     72   JNIEnv* env = base::android::AttachCurrentThread();
     73   return Java_DevToolsServer_checkDebugPermission(
     74       env, base::android::GetApplicationContext(),
     75       credentials.process_id, credentials.user_id) ||
     76       content::CanUserConnectToDevTools(credentials);
     77 }
     78 
     79 // Delegate implementation for the devtools http handler on android. A new
     80 // instance of this gets created each time devtools is enabled.
     81 class DevToolsServerDelegate : public content::DevToolsHttpHandlerDelegate {
     82  public:
     83   explicit DevToolsServerDelegate(
     84       const net::UnixDomainServerSocket::AuthCallback& auth_callback)
     85       : last_tethering_socket_(0),
     86         auth_callback_(auth_callback) {
     87   }
     88 
     89   virtual std::string GetDiscoveryPageHTML() OVERRIDE {
     90     // TopSites updates itself after a delay. Ask TopSites to update itself
     91     // when we're about to show the remote debugging landing page.
     92     content::BrowserThread::PostTask(
     93         content::BrowserThread::UI,
     94         FROM_HERE,
     95         base::Bind(&DevToolsServerDelegate::PopulatePageThumbnails));
     96     return ResourceBundle::GetSharedInstance().GetRawDataResource(
     97         IDR_DEVTOOLS_DISCOVERY_PAGE_HTML).as_string();
     98   }
     99 
    100   virtual bool BundlesFrontendResources() OVERRIDE {
    101     return false;
    102   }
    103 
    104   virtual base::FilePath GetDebugFrontendDir() OVERRIDE {
    105     return base::FilePath();
    106   }
    107 
    108   virtual scoped_ptr<net::StreamListenSocket> CreateSocketForTethering(
    109       net::StreamListenSocket::Delegate* delegate,
    110       std::string* name) OVERRIDE {
    111     *name = base::StringPrintf(
    112         kTetheringSocketName, getpid(), ++last_tethering_socket_);
    113     return net::deprecated::UnixDomainListenSocket::
    114         CreateAndListenWithAbstractNamespace(
    115             *name,
    116             "",
    117             delegate,
    118             auth_callback_)
    119         .PassAs<net::StreamListenSocket>();
    120   }
    121 
    122  private:
    123   static void PopulatePageThumbnails() {
    124     Profile* profile =
    125         ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
    126     history::TopSites* top_sites = profile->GetTopSites();
    127     if (top_sites)
    128       top_sites->SyncWithHistory();
    129   }
    130 
    131   int last_tethering_socket_;
    132   const net::UnixDomainServerSocket::AuthCallback auth_callback_;
    133 
    134   DISALLOW_COPY_AND_ASSIGN(DevToolsServerDelegate);
    135 };
    136 
    137 // Factory for UnixDomainServerSocket. It tries a fallback socket when
    138 // original socket doesn't work.
    139 class UnixDomainServerSocketFactory
    140     : public content::DevToolsHttpHandler::ServerSocketFactory {
    141  public:
    142   UnixDomainServerSocketFactory(
    143       const std::string& socket_name,
    144       const net::UnixDomainServerSocket::AuthCallback& auth_callback)
    145       : content::DevToolsHttpHandler::ServerSocketFactory(socket_name, 0, 1),
    146         auth_callback_(auth_callback) {
    147   }
    148 
    149  private:
    150   // content::DevToolsHttpHandler::ServerSocketFactory.
    151   virtual scoped_ptr<net::ServerSocket> Create() const OVERRIDE {
    152     return scoped_ptr<net::ServerSocket>(
    153         new net::UnixDomainServerSocket(auth_callback_,
    154                                         true /* use_abstract_namespace */));
    155   }
    156 
    157   virtual scoped_ptr<net::ServerSocket> CreateAndListen() const OVERRIDE {
    158     scoped_ptr<net::ServerSocket> socket = Create();
    159     if (!socket)
    160       return scoped_ptr<net::ServerSocket>();
    161 
    162     if (socket->ListenWithAddressAndPort(address_, port_, backlog_) == net::OK)
    163       return socket.Pass();
    164 
    165     // Try a fallback socket name.
    166     const std::string fallback_address(
    167         base::StringPrintf("%s_%d", address_.c_str(), getpid()));
    168     if (socket->ListenWithAddressAndPort(fallback_address, port_, backlog_)
    169         == net::OK)
    170       return socket.Pass();
    171 
    172     return scoped_ptr<net::ServerSocket>();
    173   }
    174 
    175   const net::UnixDomainServerSocket::AuthCallback auth_callback_;
    176 
    177   DISALLOW_COPY_AND_ASSIGN(UnixDomainServerSocketFactory);
    178 };
    179 
    180 }  // namespace
    181 
    182 DevToolsServer::DevToolsServer(const std::string& socket_name_prefix)
    183     : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat,
    184                                       socket_name_prefix.c_str())),
    185       protocol_handler_(NULL) {
    186   // Override the socket name if one is specified on the command line.
    187   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    188   if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) {
    189     socket_name_ = command_line.GetSwitchValueASCII(
    190         switches::kRemoteDebuggingSocketName);
    191   }
    192 }
    193 
    194 DevToolsServer::~DevToolsServer() {
    195   Stop();
    196 }
    197 
    198 void DevToolsServer::Start(bool allow_debug_permission) {
    199   if (protocol_handler_)
    200     return;
    201 
    202   net::UnixDomainServerSocket::AuthCallback auth_callback =
    203       allow_debug_permission ?
    204           base::Bind(&AuthorizeSocketAccessWithDebugPermission) :
    205           base::Bind(&content::CanUserConnectToDevTools);
    206   scoped_ptr<content::DevToolsHttpHandler::ServerSocketFactory> factory(
    207       new UnixDomainServerSocketFactory(socket_name_, auth_callback));
    208   protocol_handler_ = content::DevToolsHttpHandler::Start(
    209       factory.Pass(),
    210       base::StringPrintf(kFrontEndURL, content::GetWebKitRevision().c_str()),
    211       new DevToolsServerDelegate(auth_callback),
    212       base::FilePath());
    213 }
    214 
    215 void DevToolsServer::Stop() {
    216   if (!protocol_handler_)
    217     return;
    218   // Note that the call to Stop() below takes care of |protocol_handler_|
    219   // deletion.
    220   protocol_handler_->Stop();
    221   protocol_handler_ = NULL;
    222 }
    223 
    224 bool DevToolsServer::IsStarted() const {
    225   return protocol_handler_;
    226 }
    227 
    228 bool RegisterDevToolsServer(JNIEnv* env) {
    229   return RegisterNativesImpl(env);
    230 }
    231 
    232 static jlong InitRemoteDebugging(JNIEnv* env,
    233                                 jobject obj,
    234                                 jstring socket_name_prefix) {
    235   DevToolsServer* server = new DevToolsServer(
    236       base::android::ConvertJavaStringToUTF8(env, socket_name_prefix));
    237   return reinterpret_cast<intptr_t>(server);
    238 }
    239 
    240 static void DestroyRemoteDebugging(JNIEnv* env, jobject obj, jlong server) {
    241   delete reinterpret_cast<DevToolsServer*>(server);
    242 }
    243 
    244 static jboolean IsRemoteDebuggingEnabled(JNIEnv* env,
    245                                          jobject obj,
    246                                          jlong server) {
    247   return reinterpret_cast<DevToolsServer*>(server)->IsStarted();
    248 }
    249 
    250 static void SetRemoteDebuggingEnabled(JNIEnv* env,
    251                                       jobject obj,
    252                                       jlong server,
    253                                       jboolean enabled,
    254                                       jboolean allow_debug_permission) {
    255   DevToolsServer* devtools_server = reinterpret_cast<DevToolsServer*>(server);
    256   if (enabled) {
    257     devtools_server->Start(allow_debug_permission);
    258   } else {
    259     devtools_server->Stop();
    260   }
    261 }
    262