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