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