1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.webkit; 18 19 import android.content.Context; 20 import android.net.http.*; 21 import android.os.*; 22 import android.util.Log; 23 24 import java.io.ByteArrayInputStream; 25 import java.io.InputStream; 26 import java.util.Map; 27 28 import junit.framework.Assert; 29 30 class Network { 31 32 private static final String LOGTAG = "network"; 33 34 /** 35 * Static instance of a Network object. 36 */ 37 private static Network sNetwork; 38 39 /** 40 * Flag to store the state of platform notifications, for the case 41 * when the Network object has not been constructed yet 42 */ 43 private static boolean sPlatformNotifications; 44 45 /** 46 * Reference count for platform notifications as the network class is a 47 * static and can exist over multiple activities, thus over multiple 48 * onPause/onResume pairs. 49 */ 50 private static int sPlatformNotificationEnableRefCount; 51 52 /** 53 * Proxy username if known (used for pre-emptive proxy authentication). 54 */ 55 private String mProxyUsername; 56 57 /** 58 * Proxy password if known (used for pre-emptive proxy authentication). 59 */ 60 private String mProxyPassword; 61 62 /** 63 * Network request queue (requests are added from the browser thread). 64 */ 65 private RequestQueue mRequestQueue; 66 67 /** 68 * SSL error handler: takes care of synchronization of multiple async 69 * loaders with SSL-related problems. 70 */ 71 private SslErrorHandler mSslErrorHandler; 72 73 /** 74 * HTTP authentication handler: takes care of synchronization of HTTP 75 * authentication requests. 76 */ 77 private HttpAuthHandler mHttpAuthHandler; 78 79 /** 80 * @return The singleton instance of the network. 81 */ getInstance(Context context)82 public static synchronized Network getInstance(Context context) { 83 if (sNetwork == null) { 84 // Note Context of the Application is used here, rather than 85 // the what is passed in (usually a Context derived from an 86 // Activity) so the intent receivers belong to the application 87 // rather than an activity - this fixes the issue where 88 // Activities are created and destroyed during the lifetime of 89 // an Application 90 sNetwork = new Network(context.getApplicationContext()); 91 if (sPlatformNotifications) { 92 // Adjust the ref count before calling enable as it is already 93 // taken into account when the static function was called 94 // directly 95 --sPlatformNotificationEnableRefCount; 96 enablePlatformNotifications(); 97 } 98 } 99 return sNetwork; 100 } 101 102 103 /** 104 * Enables data state and proxy tracking 105 */ enablePlatformNotifications()106 public static void enablePlatformNotifications() { 107 if (++sPlatformNotificationEnableRefCount == 1) { 108 if (sNetwork != null) { 109 sNetwork.mRequestQueue.enablePlatformNotifications(); 110 } else { 111 sPlatformNotifications = true; 112 } 113 } 114 } 115 116 /** 117 * If platform notifications are enabled, this should be called 118 * from onPause() or onStop() 119 */ disablePlatformNotifications()120 public static void disablePlatformNotifications() { 121 if (--sPlatformNotificationEnableRefCount == 0) { 122 if (sNetwork != null) { 123 sNetwork.mRequestQueue.disablePlatformNotifications(); 124 } else { 125 sPlatformNotifications = false; 126 } 127 } 128 } 129 130 /** 131 * Creates a new Network object. 132 * XXX: Must be created in the same thread as WebCore!!!!! 133 */ Network(Context context)134 private Network(Context context) { 135 if (DebugFlags.NETWORK) { 136 Assert.assertTrue(Thread.currentThread(). 137 getName().equals(WebViewCore.THREAD_NAME)); 138 } 139 mSslErrorHandler = new SslErrorHandler(); 140 mHttpAuthHandler = new HttpAuthHandler(this); 141 142 mRequestQueue = new RequestQueue(context); 143 } 144 145 /** 146 * Request a url from either the network or the file system. 147 * @param url The url to load. 148 * @param method The http method. 149 * @param headers The http headers. 150 * @param postData The body of the request. 151 * @param loader A LoadListener for receiving the results of the request. 152 * @return True if the request was successfully queued. 153 */ requestURL(String method, Map<String, String> headers, byte [] postData, LoadListener loader)154 public boolean requestURL(String method, 155 Map<String, String> headers, 156 byte [] postData, 157 LoadListener loader) { 158 159 String url = loader.url(); 160 161 // Not a valid url, return false because we won't service the request! 162 if (!URLUtil.isValidUrl(url)) { 163 return false; 164 } 165 166 // asset, file system or data stream are handled in the other code path. 167 // This only handles network request. 168 if (URLUtil.isAssetUrl(url) || URLUtil.isFileUrl(url) || 169 URLUtil.isDataUrl(url)) { 170 return false; 171 } 172 173 /* FIXME: this is lame. Pass an InputStream in, rather than 174 making this lame one here */ 175 InputStream bodyProvider = null; 176 int bodyLength = 0; 177 if (postData != null) { 178 bodyLength = postData.length; 179 bodyProvider = new ByteArrayInputStream(postData); 180 } 181 182 RequestQueue q = mRequestQueue; 183 if (loader.isSynchronous()) { 184 q = new RequestQueue(loader.getContext(), 1); 185 } 186 187 RequestHandle handle = q.queueRequest( 188 url, loader.getWebAddress(), method, headers, loader, 189 bodyProvider, bodyLength); 190 loader.attachRequestHandle(handle); 191 192 if (loader.isSynchronous()) { 193 handle.waitUntilComplete(); 194 loader.loadSynchronousMessages(); 195 q.shutdown(); 196 } 197 return true; 198 } 199 200 /** 201 * @return True iff there is a valid proxy set. 202 */ isValidProxySet()203 public boolean isValidProxySet() { 204 // The proxy host and port can be set within a different thread during 205 // an Intent broadcast. 206 synchronized (mRequestQueue) { 207 return mRequestQueue.getProxyHost() != null; 208 } 209 } 210 211 /** 212 * Get the proxy hostname. 213 * @return The proxy hostname obtained from the network queue and proxy 214 * settings. 215 */ getProxyHostname()216 public String getProxyHostname() { 217 return mRequestQueue.getProxyHost().getHostName(); 218 } 219 220 /** 221 * @return The proxy username or null if none. 222 */ getProxyUsername()223 public synchronized String getProxyUsername() { 224 return mProxyUsername; 225 } 226 227 /** 228 * Sets the proxy username. 229 * @param proxyUsername Username to use when 230 * connecting through the proxy. 231 */ setProxyUsername(String proxyUsername)232 public synchronized void setProxyUsername(String proxyUsername) { 233 if (DebugFlags.NETWORK) { 234 Assert.assertTrue(isValidProxySet()); 235 } 236 237 mProxyUsername = proxyUsername; 238 } 239 240 /** 241 * @return The proxy password or null if none. 242 */ getProxyPassword()243 public synchronized String getProxyPassword() { 244 return mProxyPassword; 245 } 246 247 /** 248 * Sets the proxy password. 249 * @param proxyPassword Password to use when 250 * connecting through the proxy. 251 */ setProxyPassword(String proxyPassword)252 public synchronized void setProxyPassword(String proxyPassword) { 253 if (DebugFlags.NETWORK) { 254 Assert.assertTrue(isValidProxySet()); 255 } 256 257 mProxyPassword = proxyPassword; 258 } 259 260 /** 261 * Saves the state of network handlers (user SSL and HTTP-authentication 262 * preferences). 263 * @param outState The out-state to save (write) to. 264 * @return True iff succeeds. 265 */ saveState(Bundle outState)266 public boolean saveState(Bundle outState) { 267 if (DebugFlags.NETWORK) { 268 Log.v(LOGTAG, "Network.saveState()"); 269 } 270 271 return mSslErrorHandler.saveState(outState); 272 } 273 274 /** 275 * Restores the state of network handlers (user SSL and HTTP-authentication 276 * preferences). 277 * @param inState The in-state to load (read) from. 278 * @return True iff succeeds. 279 */ restoreState(Bundle inState)280 public boolean restoreState(Bundle inState) { 281 if (DebugFlags.NETWORK) { 282 Log.v(LOGTAG, "Network.restoreState()"); 283 } 284 285 return mSslErrorHandler.restoreState(inState); 286 } 287 288 /** 289 * Clears user SSL-error preference table. 290 */ clearUserSslPrefTable()291 public void clearUserSslPrefTable() { 292 mSslErrorHandler.clear(); 293 } 294 295 /** 296 * Handles SSL error(s) on the way up to the user: the user must decide 297 * whether errors should be ignored or not. 298 * @param loader The loader that resulted in SSL errors. 299 */ handleSslErrorRequest(LoadListener loader)300 public void handleSslErrorRequest(LoadListener loader) { 301 if (DebugFlags.NETWORK) Assert.assertNotNull(loader); 302 if (loader != null) { 303 mSslErrorHandler.handleSslErrorRequest(loader); 304 } 305 } 306 checkSslPrefTable(LoadListener loader, SslError error)307 /* package */ boolean checkSslPrefTable(LoadListener loader, 308 SslError error) { 309 if (loader != null && error != null) { 310 return mSslErrorHandler.checkSslPrefTable(loader, error); 311 } 312 return false; 313 } 314 315 /** 316 * Handles authentication requests on their way up to the user (the user 317 * must provide credentials). 318 * @param loader The loader that resulted in an HTTP 319 * authentication request. 320 */ handleAuthRequest(LoadListener loader)321 public void handleAuthRequest(LoadListener loader) { 322 if (DebugFlags.NETWORK) Assert.assertNotNull(loader); 323 if (loader != null) { 324 mHttpAuthHandler.handleAuthRequest(loader); 325 } 326 } 327 328 // Performance probe startTiming()329 public void startTiming() { 330 mRequestQueue.startTiming(); 331 } 332 stopTiming()333 public void stopTiming() { 334 mRequestQueue.stopTiming(); 335 } 336 } 337