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.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.net.ConnectivityManager; 24 import android.net.NetworkInfo; 25 import android.net.http.*; 26 import android.os.*; 27 import android.util.Log; 28 29 import java.io.ByteArrayInputStream; 30 import java.io.InputStream; 31 import java.util.Map; 32 33 import junit.framework.Assert; 34 35 class Network { 36 37 private static final String LOGTAG = "network"; 38 39 /** 40 * Static instance of a Network object. 41 */ 42 private static Network sNetwork; 43 44 /** 45 * Flag to store the state of platform notifications, for the case 46 * when the Network object has not been constructed yet 47 */ 48 private static boolean sPlatformNotifications; 49 50 /** 51 * Reference count for platform notifications as the network class is a 52 * static and can exist over multiple activities, thus over multiple 53 * onPause/onResume pairs. 54 */ 55 private static int sPlatformNotificationEnableRefCount; 56 57 /** 58 * Proxy username if known (used for pre-emptive proxy authentication). 59 */ 60 private String mProxyUsername; 61 62 /** 63 * Proxy password if known (used for pre-emptive proxy authentication). 64 */ 65 private String mProxyPassword; 66 67 /** 68 * Network request queue (requests are added from the browser thread). 69 */ 70 private RequestQueue mRequestQueue; 71 72 /** 73 * SSL error handler: takes care of synchronization of multiple async 74 * loaders with SSL-related problems. 75 */ 76 private SslErrorHandlerImpl mSslErrorHandler; 77 78 /** 79 * HTTP authentication handler: takes care of synchronization of HTTP 80 * authentication requests. 81 */ 82 private HttpAuthHandlerImpl mHttpAuthHandler; 83 84 private Context mContext; 85 86 /** 87 * True if the currently used network connection is a roaming phone 88 * connection. 89 */ 90 private boolean mRoaming; 91 92 /** 93 * Tracks if we are roaming. 94 */ 95 private RoamingMonitor mRoamingMonitor; 96 97 /** 98 * @return The singleton instance of the network. 99 */ getInstance(Context context)100 public static synchronized Network getInstance(Context context) { 101 if (sNetwork == null) { 102 // Note Context of the Application is used here, rather than 103 // the what is passed in (usually a Context derived from an 104 // Activity) so the intent receivers belong to the application 105 // rather than an activity - this fixes the issue where 106 // Activities are created and destroyed during the lifetime of 107 // an Application 108 sNetwork = new Network(context.getApplicationContext()); 109 if (sPlatformNotifications) { 110 // Adjust the ref count before calling enable as it is already 111 // taken into account when the static function was called 112 // directly 113 --sPlatformNotificationEnableRefCount; 114 enablePlatformNotifications(); 115 } 116 } 117 return sNetwork; 118 } 119 120 121 /** 122 * Enables data state and proxy tracking 123 */ enablePlatformNotifications()124 public static void enablePlatformNotifications() { 125 if (++sPlatformNotificationEnableRefCount == 1) { 126 if (sNetwork != null) { 127 sNetwork.mRequestQueue.enablePlatformNotifications(); 128 sNetwork.monitorRoaming(); 129 } else { 130 sPlatformNotifications = true; 131 } 132 } 133 } 134 135 /** 136 * If platform notifications are enabled, this should be called 137 * from onPause() or onStop() 138 */ disablePlatformNotifications()139 public static void disablePlatformNotifications() { 140 if (--sPlatformNotificationEnableRefCount == 0) { 141 if (sNetwork != null) { 142 sNetwork.mRequestQueue.disablePlatformNotifications(); 143 sNetwork.stopMonitoringRoaming(); 144 } else { 145 sPlatformNotifications = false; 146 } 147 } 148 } 149 150 /** 151 * Creates a new Network object. 152 * XXX: Must be created in the same thread as WebCore!!!!! 153 */ Network(Context context)154 private Network(Context context) { 155 if (DebugFlags.NETWORK) { 156 Assert.assertTrue(Thread.currentThread(). 157 getName().equals(WebViewCore.THREAD_NAME)); 158 } 159 mContext = context; 160 mSslErrorHandler = new SslErrorHandlerImpl(); 161 mHttpAuthHandler = new HttpAuthHandlerImpl(this); 162 163 mRequestQueue = new RequestQueue(context); 164 } 165 166 private class RoamingMonitor extends BroadcastReceiver { 167 @Override onReceive(Context context, Intent intent)168 public void onReceive(Context context, Intent intent) { 169 if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) 170 return; 171 172 NetworkInfo info = (NetworkInfo)intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); 173 if (info != null) 174 mRoaming = info.isRoaming(); 175 }; 176 }; 177 monitorRoaming()178 private void monitorRoaming() { 179 mRoamingMonitor = new RoamingMonitor(); 180 IntentFilter filter = new IntentFilter(); 181 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 182 mContext.registerReceiver(sNetwork.mRoamingMonitor, filter); 183 } 184 stopMonitoringRoaming()185 private void stopMonitoringRoaming() { 186 if (mRoamingMonitor != null) { 187 mContext.unregisterReceiver(mRoamingMonitor); 188 mRoamingMonitor = null; 189 } 190 } 191 192 /** 193 * Request a url from either the network or the file system. 194 * @param url The url to load. 195 * @param method The http method. 196 * @param headers The http headers. 197 * @param postData The body of the request. 198 * @param loader A LoadListener for receiving the results of the request. 199 * @return True if the request was successfully queued. 200 */ requestURL(String method, Map<String, String> headers, byte [] postData, LoadListener loader)201 public boolean requestURL(String method, 202 Map<String, String> headers, 203 byte [] postData, 204 LoadListener loader) { 205 206 String url = loader.url(); 207 208 // Not a valid url, return false because we won't service the request! 209 if (!URLUtil.isValidUrl(url)) { 210 return false; 211 } 212 213 // asset, res, file system or data stream are handled in the other code 214 // path. This only handles network request. 215 if (URLUtil.isAssetUrl(url) || URLUtil.isResourceUrl(url) 216 || URLUtil.isFileUrl(url) || URLUtil.isDataUrl(url)) { 217 return false; 218 } 219 220 // If this is a prefetch, abort it if we're roaming. 221 if (mRoaming && headers.containsKey("X-Moz") && "prefetch".equals(headers.get("X-Moz"))) { 222 return false; 223 } 224 225 /* FIXME: this is lame. Pass an InputStream in, rather than 226 making this lame one here */ 227 InputStream bodyProvider = null; 228 int bodyLength = 0; 229 if (postData != null) { 230 bodyLength = postData.length; 231 bodyProvider = new ByteArrayInputStream(postData); 232 } 233 234 RequestQueue q = mRequestQueue; 235 RequestHandle handle = null; 236 if (loader.isSynchronous()) { 237 handle = q.queueSynchronousRequest(url, loader.getWebAddress(), 238 method, headers, loader, bodyProvider, bodyLength); 239 loader.attachRequestHandle(handle); 240 handle.processRequest(); 241 loader.loadSynchronousMessages(); 242 } else { 243 handle = q.queueRequest(url, loader.getWebAddress(), method, 244 headers, loader, bodyProvider, bodyLength); 245 // FIXME: Although this is probably a rare condition, normal network 246 // requests are processed in a separate thread. This means that it 247 // is possible to process part of the request before setting the 248 // request handle on the loader. We should probably refactor this to 249 // ensure the handle is attached before processing begins. 250 loader.attachRequestHandle(handle); 251 } 252 253 return true; 254 } 255 256 /** 257 * @return True iff there is a valid proxy set. 258 */ isValidProxySet()259 public boolean isValidProxySet() { 260 // The proxy host and port can be set within a different thread during 261 // an Intent broadcast. 262 synchronized (mRequestQueue) { 263 return mRequestQueue.getProxyHost() != null; 264 } 265 } 266 267 /** 268 * Get the proxy hostname. 269 * @return The proxy hostname obtained from the network queue and proxy 270 * settings. 271 */ getProxyHostname()272 public String getProxyHostname() { 273 return mRequestQueue.getProxyHost().getHostName(); 274 } 275 276 /** 277 * @return The proxy username or null if none. 278 */ getProxyUsername()279 public synchronized String getProxyUsername() { 280 return mProxyUsername; 281 } 282 283 /** 284 * Sets the proxy username. 285 * @param proxyUsername Username to use when 286 * connecting through the proxy. 287 */ setProxyUsername(String proxyUsername)288 public synchronized void setProxyUsername(String proxyUsername) { 289 if (DebugFlags.NETWORK) { 290 Assert.assertTrue(isValidProxySet()); 291 } 292 293 mProxyUsername = proxyUsername; 294 } 295 296 /** 297 * @return The proxy password or null if none. 298 */ getProxyPassword()299 public synchronized String getProxyPassword() { 300 return mProxyPassword; 301 } 302 303 /** 304 * Sets the proxy password. 305 * @param proxyPassword Password to use when 306 * connecting through the proxy. 307 */ setProxyPassword(String proxyPassword)308 public synchronized void setProxyPassword(String proxyPassword) { 309 if (DebugFlags.NETWORK) { 310 Assert.assertTrue(isValidProxySet()); 311 } 312 313 mProxyPassword = proxyPassword; 314 } 315 316 /** 317 * Saves the state of network handlers (user SSL and HTTP-authentication 318 * preferences). 319 * @param outState The out-state to save (write) to. 320 * @return True iff succeeds. 321 */ saveState(Bundle outState)322 public boolean saveState(Bundle outState) { 323 if (DebugFlags.NETWORK) { 324 Log.v(LOGTAG, "Network.saveState()"); 325 } 326 327 return mSslErrorHandler.saveState(outState); 328 } 329 330 /** 331 * Restores the state of network handlers (user SSL and HTTP-authentication 332 * preferences). 333 * @param inState The in-state to load (read) from. 334 * @return True iff succeeds. 335 */ restoreState(Bundle inState)336 public boolean restoreState(Bundle inState) { 337 if (DebugFlags.NETWORK) { 338 Log.v(LOGTAG, "Network.restoreState()"); 339 } 340 341 return mSslErrorHandler.restoreState(inState); 342 } 343 344 /** 345 * Clears user SSL-error preference table. 346 */ clearUserSslPrefTable()347 public void clearUserSslPrefTable() { 348 mSslErrorHandler.clear(); 349 } 350 351 /** 352 * Handles SSL error(s) on the way up to the user: the user must decide 353 * whether errors should be ignored or not. 354 * @param loader The loader that resulted in SSL errors. 355 */ handleSslErrorRequest(LoadListener loader)356 public void handleSslErrorRequest(LoadListener loader) { 357 if (DebugFlags.NETWORK) Assert.assertNotNull(loader); 358 if (loader != null) { 359 mSslErrorHandler.handleSslErrorRequest(loader); 360 } 361 } 362 checkSslPrefTable(LoadListener loader, SslError error)363 /* package */ boolean checkSslPrefTable(LoadListener loader, 364 SslError error) { 365 if (loader != null && error != null) { 366 return mSslErrorHandler.checkSslPrefTable(loader, error); 367 } 368 return false; 369 } 370 371 /** 372 * Handles authentication requests on their way up to the user (the user 373 * must provide credentials). 374 * @param loader The loader that resulted in an HTTP 375 * authentication request. 376 */ handleAuthRequest(LoadListener loader)377 public void handleAuthRequest(LoadListener loader) { 378 if (DebugFlags.NETWORK) Assert.assertNotNull(loader); 379 if (loader != null) { 380 mHttpAuthHandler.handleAuthRequest(loader); 381 } 382 } 383 384 // Performance probe startTiming()385 public void startTiming() { 386 mRequestQueue.startTiming(); 387 } 388 stopTiming()389 public void stopTiming() { 390 mRequestQueue.stopTiming(); 391 } 392 } 393