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