Home | History | Annotate | Download | only in webkit
      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