• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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