SslErrorHandler.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1/*
2 * Copyright (C) 2007 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
17package android.webkit;
18
19import junit.framework.Assert;
20
21import android.net.http.SslError;
22import android.os.Bundle;
23import android.os.Handler;
24import android.os.Message;
25import android.util.Config;
26import android.util.Log;
27
28import java.util.LinkedList;
29import java.util.ListIterator;
30
31/**
32 * SslErrorHandler: class responsible for handling SSL errors. This class is
33 * passed as a parameter to BrowserCallback.displaySslErrorDialog and is meant
34 * to receive the user's response.
35 */
36public class SslErrorHandler extends Handler {
37    /* One problem here is that there may potentially be multiple SSL errors
38     * coming from mutiple loaders. Therefore, we keep a queue of loaders
39     * that have SSL-related problems and process errors one by one in the
40     * order they were received.
41     */
42
43    private static final String LOGTAG = "network";
44
45    /**
46     * Network.
47     */
48    private Network mNetwork;
49
50    /**
51     * Queue of loaders that experience SSL-related problems.
52     */
53    private LinkedList<LoadListener> mLoaderQueue;
54
55    /**
56     * SSL error preference table.
57     */
58    private Bundle mSslPrefTable;
59
60    // Message id for handling the response
61    private final int HANDLE_RESPONSE = 100;
62
63    @Override
64    public void handleMessage(Message msg) {
65        switch (msg.what) {
66            case HANDLE_RESPONSE:
67                handleSslErrorResponse(msg.arg1 == 1);
68                fastProcessQueuedSslErrors();
69                break;
70        }
71    }
72
73    /**
74     * Creates a new error handler with an empty loader queue.
75     */
76    /* package */ SslErrorHandler(Network network) {
77        mNetwork = network;
78
79        mLoaderQueue = new LinkedList<LoadListener>();
80        mSslPrefTable = new Bundle();
81    }
82
83    /**
84     * Saves this handler's state into a map.
85     * @return True iff succeeds.
86     */
87    /* package */ boolean saveState(Bundle outState) {
88        boolean success = (outState != null);
89        if (success) {
90            // TODO?
91            outState.putBundle("ssl-error-handler", mSslPrefTable);
92        }
93
94        return success;
95    }
96
97    /**
98     * Restores this handler's state from a map.
99     * @return True iff succeeds.
100     */
101    /* package */ boolean restoreState(Bundle inState) {
102        boolean success = (inState != null);
103        if (success) {
104            success = inState.containsKey("ssl-error-handler");
105            if (success) {
106                mSslPrefTable = inState.getBundle("ssl-error-handler");
107            }
108        }
109
110        return success;
111    }
112
113    /**
114     * Clears SSL error preference table.
115     */
116    /* package */ synchronized void clear() {
117        mSslPrefTable.clear();
118    }
119
120    /**
121     * Resets the SSL error handler, removes all loaders that
122     * share the same BrowserFrame.
123     */
124    /* package */ synchronized void reset(BrowserFrame frame) {
125        ListIterator<LoadListener> i = mLoaderQueue.listIterator(0);
126        while (i.hasNext()) {
127            LoadListener loader = i.next();
128            if (frame == loader.getFrame()) {
129                i.remove();
130            }
131        }
132    }
133
134    /**
135     * Handles SSL error(s) on the way up to the user.
136     */
137    /* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
138        if (Config.LOGV) {
139            Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
140                  "url=" + loader.url());
141        }
142
143        if (!loader.cancelled()) {
144            mLoaderQueue.offer(loader);
145            if (loader == mLoaderQueue.peek()) {
146                fastProcessQueuedSslErrors();
147            }
148        }
149    }
150
151    /**
152     * Processes queued SSL-error confirmation requests in
153     * a tight loop while there is no need to ask the user.
154     */
155    /* package */void fastProcessQueuedSslErrors() {
156        while (processNextLoader());
157    }
158
159    /**
160     * Processes the next loader in the queue.
161     * @return True iff should proceed to processing the
162     * following loader in the queue
163     */
164    private synchronized boolean processNextLoader() {
165        LoadListener loader = mLoaderQueue.peek();
166        if (loader != null) {
167            // if this loader has been cancelled
168            if (loader.cancelled()) {
169                // go to the following loader in the queue
170                return true;
171            }
172
173            SslError error = loader.sslError();
174
175            if (Config.DEBUG) {
176                Assert.assertNotNull(error);
177            }
178
179            int primary = error.getPrimaryError();
180            String host = loader.host();
181
182            if (Config.DEBUG) {
183                Assert.assertTrue(host != null && primary != 0);
184            }
185
186            if (mSslPrefTable.containsKey(host)) {
187                if (primary <= mSslPrefTable.getInt(host)) {
188                    handleSslErrorResponse(true);
189                    return true;
190                }
191            }
192
193            // if we do not have information on record, ask
194            // the user (display a dialog)
195            CallbackProxy proxy = loader.getFrame().getCallbackProxy();
196            proxy.onReceivedSslError(this, error);
197        }
198
199        // the queue must be empty, stop
200        return false;
201    }
202
203    /**
204     * Proceed with the SSL certificate.
205     */
206    public void proceed() {
207        sendMessage(obtainMessage(HANDLE_RESPONSE, 1, 0));
208    }
209
210    /**
211     * Cancel this request and all pending requests for the WebView that had
212     * the error.
213     */
214    public void cancel() {
215        sendMessage(obtainMessage(HANDLE_RESPONSE, 0, 0));
216    }
217
218    /**
219     * Handles SSL error(s) on the way down from the user.
220     */
221    /* package */ synchronized void handleSslErrorResponse(boolean proceed) {
222        LoadListener loader = mLoaderQueue.poll();
223        if (Config.DEBUG) {
224            Assert.assertNotNull(loader);
225        }
226
227        if (Config.LOGV) {
228            Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
229                  + " proceed: " + proceed
230                  + " url:" + loader.url());
231        }
232
233        if (!loader.cancelled()) {
234            if (proceed) {
235                // update the user's SSL error preference table
236                int primary = loader.sslError().getPrimaryError();
237                String host = loader.host();
238
239                if (Config.DEBUG) {
240                    Assert.assertTrue(host != null && primary != 0);
241                }
242                boolean hasKey = mSslPrefTable.containsKey(host);
243                if (!hasKey ||
244                    primary > mSslPrefTable.getInt(host)) {
245                    mSslPrefTable.putInt(host, new Integer(primary));
246                }
247
248                loader.handleSslErrorResponse(proceed);
249            } else {
250                loader.handleSslErrorResponse(proceed);
251                mNetwork.resetHandlersAndStopLoading(loader.getFrame());
252            }
253        }
254    }
255}
256