blob: 617a3145ee519a1c9bf75f7db8dd2296c5bd523c [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.webkit;
import junit.framework.Assert;
import android.net.http.SslError;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.util.LinkedList;
import java.util.ListIterator;
/**
* SslErrorHandler: class responsible for handling SSL errors. This class is
* passed as a parameter to BrowserCallback.displaySslErrorDialog and is meant
* to receive the user's response.
*/
public class SslErrorHandler extends Handler {
/* One problem here is that there may potentially be multiple SSL errors
* coming from mutiple loaders. Therefore, we keep a queue of loaders
* that have SSL-related problems and process errors one by one in the
* order they were received.
*/
private static final String LOGTAG = "network";
/**
* Network.
*/
private Network mNetwork;
/**
* Queue of loaders that experience SSL-related problems.
*/
private LinkedList<LoadListener> mLoaderQueue;
/**
* SSL error preference table.
*/
private Bundle mSslPrefTable;
// Message id for handling the response
private final int HANDLE_RESPONSE = 100;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case HANDLE_RESPONSE:
handleSslErrorResponse(msg.arg1 == 1);
fastProcessQueuedSslErrors();
break;
}
}
/**
* Creates a new error handler with an empty loader queue.
*/
/* package */ SslErrorHandler(Network network) {
mNetwork = network;
mLoaderQueue = new LinkedList<LoadListener>();
mSslPrefTable = new Bundle();
}
/**
* Saves this handler's state into a map.
* @return True iff succeeds.
*/
/* package */ boolean saveState(Bundle outState) {
boolean success = (outState != null);
if (success) {
// TODO?
outState.putBundle("ssl-error-handler", mSslPrefTable);
}
return success;
}
/**
* Restores this handler's state from a map.
* @return True iff succeeds.
*/
/* package */ boolean restoreState(Bundle inState) {
boolean success = (inState != null);
if (success) {
success = inState.containsKey("ssl-error-handler");
if (success) {
mSslPrefTable = inState.getBundle("ssl-error-handler");
}
}
return success;
}
/**
* Clears SSL error preference table.
*/
/* package */ synchronized void clear() {
mSslPrefTable.clear();
}
/**
* Handles SSL error(s) on the way up to the user.
*/
/* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
if (WebView.LOGV_ENABLED) {
Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
"url=" + loader.url());
}
if (!loader.cancelled()) {
mLoaderQueue.offer(loader);
if (loader == mLoaderQueue.peek()) {
fastProcessQueuedSslErrors();
}
}
}
/**
* Processes queued SSL-error confirmation requests in
* a tight loop while there is no need to ask the user.
*/
/* package */void fastProcessQueuedSslErrors() {
while (processNextLoader());
}
/**
* Processes the next loader in the queue.
* @return True iff should proceed to processing the
* following loader in the queue
*/
private synchronized boolean processNextLoader() {
LoadListener loader = mLoaderQueue.peek();
if (loader != null) {
// if this loader has been cancelled
if (loader.cancelled()) {
// go to the following loader in the queue
return true;
}
SslError error = loader.sslError();
if (WebView.DEBUG) {
Assert.assertNotNull(error);
}
int primary = error.getPrimaryError();
String host = loader.host();
if (WebView.DEBUG) {
Assert.assertTrue(host != null && primary != 0);
}
if (mSslPrefTable.containsKey(host)) {
if (primary <= mSslPrefTable.getInt(host)) {
handleSslErrorResponse(true);
return true;
}
}
// if we do not have information on record, ask
// the user (display a dialog)
CallbackProxy proxy = loader.getFrame().getCallbackProxy();
proxy.onReceivedSslError(this, error);
}
// the queue must be empty, stop
return false;
}
/**
* Proceed with the SSL certificate.
*/
public void proceed() {
sendMessage(obtainMessage(HANDLE_RESPONSE, 1, 0));
}
/**
* Cancel this request and all pending requests for the WebView that had
* the error.
*/
public void cancel() {
sendMessage(obtainMessage(HANDLE_RESPONSE, 0, 0));
}
/**
* Handles SSL error(s) on the way down from the user.
*/
/* package */ synchronized void handleSslErrorResponse(boolean proceed) {
LoadListener loader = mLoaderQueue.poll();
if (WebView.DEBUG) {
Assert.assertNotNull(loader);
}
if (WebView.LOGV_ENABLED) {
Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
+ " proceed: " + proceed
+ " url:" + loader.url());
}
if (!loader.cancelled()) {
if (proceed) {
// update the user's SSL error preference table
int primary = loader.sslError().getPrimaryError();
String host = loader.host();
if (WebView.DEBUG) {
Assert.assertTrue(host != null && primary != 0);
}
boolean hasKey = mSslPrefTable.containsKey(host);
if (!hasKey ||
primary > mSslPrefTable.getInt(host)) {
mSslPrefTable.putInt(host, primary);
}
}
loader.handleSslErrorResponse(proceed);
}
}
}