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 */ 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 */ 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 */ 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 */ 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 */ 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, res, file system or data stream are handled in the other code 167 // path. This only handles network request. 168 if (URLUtil.isAssetUrl(url) || URLUtil.isResourceUrl(url) 169 || URLUtil.isFileUrl(url) || 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 RequestHandle handle = null; 184 if (loader.isSynchronous()) { 185 handle = q.queueSynchronousRequest(url, loader.getWebAddress(), 186 method, headers, loader, bodyProvider, bodyLength); 187 loader.attachRequestHandle(handle); 188 handle.processRequest(); 189 loader.loadSynchronousMessages(); 190 } else { 191 handle = q.queueRequest(url, loader.getWebAddress(), method, 192 headers, loader, bodyProvider, bodyLength); 193 // FIXME: Although this is probably a rare condition, normal network 194 // requests are processed in a separate thread. This means that it 195 // is possible to process part of the request before setting the 196 // request handle on the loader. We should probably refactor this to 197 // ensure the handle is attached before processing begins. 198 loader.attachRequestHandle(handle); 199 } 200 201 return true; 202 } 203 204 /** 205 * @return True iff there is a valid proxy set. 206 */ 207 public boolean isValidProxySet() { 208 // The proxy host and port can be set within a different thread during 209 // an Intent broadcast. 210 synchronized (mRequestQueue) { 211 return mRequestQueue.getProxyHost() != null; 212 } 213 } 214 215 /** 216 * Get the proxy hostname. 217 * @return The proxy hostname obtained from the network queue and proxy 218 * settings. 219 */ 220 public String getProxyHostname() { 221 return mRequestQueue.getProxyHost().getHostName(); 222 } 223 224 /** 225 * @return The proxy username or null if none. 226 */ 227 public synchronized String getProxyUsername() { 228 return mProxyUsername; 229 } 230 231 /** 232 * Sets the proxy username. 233 * @param proxyUsername Username to use when 234 * connecting through the proxy. 235 */ 236 public synchronized void setProxyUsername(String proxyUsername) { 237 if (DebugFlags.NETWORK) { 238 Assert.assertTrue(isValidProxySet()); 239 } 240 241 mProxyUsername = proxyUsername; 242 } 243 244 /** 245 * @return The proxy password or null if none. 246 */ 247 public synchronized String getProxyPassword() { 248 return mProxyPassword; 249 } 250 251 /** 252 * Sets the proxy password. 253 * @param proxyPassword Password to use when 254 * connecting through the proxy. 255 */ 256 public synchronized void setProxyPassword(String proxyPassword) { 257 if (DebugFlags.NETWORK) { 258 Assert.assertTrue(isValidProxySet()); 259 } 260 261 mProxyPassword = proxyPassword; 262 } 263 264 /** 265 * Saves the state of network handlers (user SSL and HTTP-authentication 266 * preferences). 267 * @param outState The out-state to save (write) to. 268 * @return True iff succeeds. 269 */ 270 public boolean saveState(Bundle outState) { 271 if (DebugFlags.NETWORK) { 272 Log.v(LOGTAG, "Network.saveState()"); 273 } 274 275 return mSslErrorHandler.saveState(outState); 276 } 277 278 /** 279 * Restores the state of network handlers (user SSL and HTTP-authentication 280 * preferences). 281 * @param inState The in-state to load (read) from. 282 * @return True iff succeeds. 283 */ 284 public boolean restoreState(Bundle inState) { 285 if (DebugFlags.NETWORK) { 286 Log.v(LOGTAG, "Network.restoreState()"); 287 } 288 289 return mSslErrorHandler.restoreState(inState); 290 } 291 292 /** 293 * Clears user SSL-error preference table. 294 */ 295 public void clearUserSslPrefTable() { 296 mSslErrorHandler.clear(); 297 } 298 299 /** 300 * Handles SSL error(s) on the way up to the user: the user must decide 301 * whether errors should be ignored or not. 302 * @param loader The loader that resulted in SSL errors. 303 */ 304 public void handleSslErrorRequest(LoadListener loader) { 305 if (DebugFlags.NETWORK) Assert.assertNotNull(loader); 306 if (loader != null) { 307 mSslErrorHandler.handleSslErrorRequest(loader); 308 } 309 } 310 311 /* package */ boolean checkSslPrefTable(LoadListener loader, 312 SslError error) { 313 if (loader != null && error != null) { 314 return mSslErrorHandler.checkSslPrefTable(loader, error); 315 } 316 return false; 317 } 318 319 /** 320 * Handles authentication requests on their way up to the user (the user 321 * must provide credentials). 322 * @param loader The loader that resulted in an HTTP 323 * authentication request. 324 */ 325 public void handleAuthRequest(LoadListener loader) { 326 if (DebugFlags.NETWORK) Assert.assertNotNull(loader); 327 if (loader != null) { 328 mHttpAuthHandler.handleAuthRequest(loader); 329 } 330 } 331 332 // Performance probe 333 public void startTiming() { 334 mRequestQueue.startTiming(); 335 } 336 337 public void stopTiming() { 338 mRequestQueue.stopTiming(); 339 } 340 } 341