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