diff options
Diffstat (limited to 'core/java/android/net')
-rw-r--r-- | core/java/android/net/ConnectivityManager.java | 51 | ||||
-rw-r--r-- | core/java/android/net/IConnectivityManager.aidl | 4 | ||||
-rw-r--r-- | core/java/android/net/SSLCertificateSocketFactory.java | 102 | ||||
-rw-r--r-- | core/java/android/net/Uri.java | 9 | ||||
-rw-r--r-- | core/java/android/net/UrlQuerySanitizer.java | 126 | ||||
-rw-r--r-- | core/java/android/net/http/AndroidHttpClient.java | 68 | ||||
-rw-r--r-- | core/java/android/net/http/CertificateChainValidator.java | 154 | ||||
-rw-r--r-- | core/java/android/net/http/RequestHandle.java | 10 |
8 files changed, 295 insertions, 229 deletions
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 213813a..1429bc1 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -16,6 +16,8 @@ package android.net; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.os.RemoteException; /** @@ -100,6 +102,18 @@ public class ConnectivityManager */ public static final String EXTRA_EXTRA_INFO = "extraInfo"; + /** + * Broadcast Action: The setting for background data usage has changed + * values. Use {@link #getBackgroundDataSetting()} to get the current value. + * <p> + * If an application uses the network in the background, it should listen + * for this broadcast and stop using the background data if the value is + * false. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = + "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; + public static final int TYPE_MOBILE = 0; public static final int TYPE_WIFI = 1; @@ -224,6 +238,43 @@ public class ConnectivityManager } /** + * Returns the value of the setting for background data usage. If false, + * applications should not use the network if the application is not in the + * foreground. Developers should respect this setting, and check the value + * of this before performing any background data operations. + * <p> + * All applications that have background services that use the network + * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}. + * + * @return Whether background data usage is allowed. + */ + public boolean getBackgroundDataSetting() { + try { + return mService.getBackgroundDataSetting(); + } catch (RemoteException e) { + // Err on the side of safety + return false; + } + } + + /** + * Sets the value of the setting for background data usage. + * + * @param allowBackgroundData Whether an application should use data while + * it is in the background. + * + * @attr ref android.Manifest.permission#CHANGE_BACKGROUND_DATA_SETTING + * @see #getBackgroundDataSetting() + * @hide + */ + public void setBackgroundDataSetting(boolean allowBackgroundData) { + try { + mService.setBackgroundDataSetting(allowBackgroundData); + } catch (RemoteException e) { + } + } + + /** * Don't allow use of default constructor. */ @SuppressWarnings({"UnusedDeclaration"}) diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index e1d921f..de68598 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -44,4 +44,8 @@ interface IConnectivityManager int stopUsingNetworkFeature(int networkType, in String feature); boolean requestRouteToHost(int networkType, int hostAddress); + + boolean getBackgroundDataSetting(); + + void setBackgroundDataSetting(boolean allowBackgroundData); } diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index f816caa..deaa3c3 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -16,38 +16,45 @@ package android.net; -import android.util.Log; -import android.util.Config; import android.net.http.DomainNameChecker; import android.os.SystemProperties; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; +import android.util.Config; +import android.util.Log; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; -import java.security.NoSuchAlgorithmException; +import java.security.GeneralSecurityException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; -import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; +import org.apache.harmony.xnet.provider.jsse.SSLContextImpl; + +/** + * SSLSocketFactory that provides optional (on debug devices, only) skipping of ssl certificfate + * chain validation and custom read timeouts used just when connecting to the server/negotiating + * an ssl session. + * + * You can skip the ssl certificate checking at runtime by setting socket.relaxsslcheck=yes on + * devices that do not have have ro.secure set. + */ public class SSLCertificateSocketFactory extends SSLSocketFactory { - private static final boolean DBG = true; private static final String LOG_TAG = "SSLCertificateSocketFactory"; - - private static X509TrustManager sDefaultTrustManager; - private final int socketReadTimeoutForSslHandshake; + private static X509TrustManager sDefaultTrustManager; static { try { @@ -83,27 +90,56 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } }; - private SSLSocketFactory factory; + private final SSLSocketFactory mFactory; + + private final int mSocketReadTimeoutForSslHandshake; + /** + * Do not use this constructor (will be deprecated). Use {@link #getDefault(int)} instead. + */ public SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake) - throws NoSuchAlgorithmException, KeyManagementException { - SSLContext context = SSLContext.getInstance("TLS"); - context.init(null, TRUST_MANAGER, new java.security.SecureRandom()); - factory = (SSLSocketFactory) context.getSocketFactory(); - this.socketReadTimeoutForSslHandshake = socketReadTimeoutForSslHandshake; + throws NoSuchAlgorithmException, KeyManagementException { + this(socketReadTimeoutForSslHandshake, null /* cache */); + } + + private SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake, + SSLClientSessionCache cache) throws NoSuchAlgorithmException, KeyManagementException { + SSLContextImpl sslContext = new SSLContextImpl(); + sslContext.engineInit(null /* kms */, + TRUST_MANAGER, new java.security.SecureRandom(), + cache /* client cache */, null /* server cache */); + this.mFactory = sslContext.engineGetSocketFactory(); + this.mSocketReadTimeoutForSslHandshake = socketReadTimeoutForSslHandshake; } /** - * Returns a default instantiation of a new socket factory which - * only allows SSL connections with valid certificates. + * Returns a new instance of a socket factory using the specified socket read + * timeout while connecting with the server/negotiating an ssl session. * * @param socketReadTimeoutForSslHandshake the socket read timeout used for performing * ssl handshake. The socket read timeout is set back to 0 after the handshake. * @return a new SocketFactory, or null on error */ public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake) { + return getDefault(socketReadTimeoutForSslHandshake, null /* cache */); + } + + /** + * Returns a new instance of a socket factory using the specified socket read + * timeout while connecting with the server/negotiating an ssl session. + * Persists ssl sessions using the provided {@link SSLClientSessionCache}. + * + * @param socketReadTimeoutForSslHandshake the socket read timeout used for performing + * ssl handshake. The socket read timeout is set back to 0 after the handshake. + * @param cache The {@link SSLClientSessionCache} to use, if any. + * @return a new SocketFactory, or null on error + * + * @hide + */ + public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake, + SSLClientSessionCache cache) { try { - return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake); + return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake, cache); } catch (NoSuchAlgorithmException e) { Log.e(LOG_TAG, "SSLCertifcateSocketFactory.getDefault" + @@ -217,10 +253,10 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException { - SSLSocket sslSock = (SSLSocket) factory.createSocket(s, i, inaddr, j); + SSLSocket sslSock = (SSLSocket) mFactory.createSocket(s, i, inaddr, j); - if (socketReadTimeoutForSslHandshake >= 0) { - sslSock.setSoTimeout(socketReadTimeoutForSslHandshake); + if (mSocketReadTimeoutForSslHandshake >= 0) { + sslSock.setSoTimeout(mSocketReadTimeoutForSslHandshake); } validateSocket(sslSock,s); @@ -230,10 +266,10 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } public Socket createSocket(String s, int i) throws IOException { - SSLSocket sslSock = (SSLSocket) factory.createSocket(s, i); + SSLSocket sslSock = (SSLSocket) mFactory.createSocket(s, i); - if (socketReadTimeoutForSslHandshake >= 0) { - sslSock.setSoTimeout(socketReadTimeoutForSslHandshake); + if (mSocketReadTimeoutForSslHandshake >= 0) { + sslSock.setSoTimeout(mSocketReadTimeoutForSslHandshake); } validateSocket(sslSock,s); @@ -243,11 +279,11 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } public String[] getDefaultCipherSuites() { - return factory.getSupportedCipherSuites(); + return mFactory.getSupportedCipherSuites(); } public String[] getSupportedCipherSuites() { - return factory.getSupportedCipherSuites(); + return mFactory.getSupportedCipherSuites(); } } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 32a26e4..c23df21 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -2235,12 +2235,13 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } /** - * Creates a new Uri by encoding and appending a path segment to a base Uri. + * Creates a new Uri by appending an already-encoded path segment to a + * base Uri. * * @param baseUri Uri to append path segment to - * @param pathSegment to encode and append - * @return a new Uri based on baseUri with the given segment encoded and - * appended to the path + * @param pathSegment encoded path segment to append + * @return a new Uri based on baseUri with the given segment appended to + * the path * @throws NullPointerException if baseUri is null */ public static Uri withAppendedPath(Uri baseUri, String pathSegment) { diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java index 70e50b7..a6efcdd 100644 --- a/core/java/android/net/UrlQuerySanitizer.java +++ b/core/java/android/net/UrlQuerySanitizer.java @@ -23,7 +23,7 @@ import java.util.Set; import java.util.StringTokenizer; /** - * + * * Sanitizes the Query portion of a URL. Simple example: * <code> * UrlQuerySanitizer sanitizer = new UrlQuerySanitizer(); @@ -32,7 +32,7 @@ import java.util.StringTokenizer; * String name = sanitizer.getValue("name")); * // name now contains "Joe_User" * </code> - * + * * Register ValueSanitizers to customize the way individual * parameters are sanitized: * <code> @@ -46,7 +46,7 @@ import java.util.StringTokenizer; * unregistered parameter sanitizer does not allow any special characters, * and ' ' is a special character.) * </code> - * + * * There are several ways to create ValueSanitizers. In order of increasing * sophistication: * <ol> @@ -56,7 +56,7 @@ import java.util.StringTokenizer; * <li>Subclass UrlQuerySanitizer.ValueSanitizer to define your own value * sanitizer. * </ol> - * + * */ public class UrlQuerySanitizer { @@ -84,7 +84,7 @@ public class UrlQuerySanitizer { */ public String mValue; } - + final private HashMap<String, ValueSanitizer> mSanitizers = new HashMap<String, ValueSanitizer>(); final private HashMap<String, String> mEntries = @@ -95,9 +95,9 @@ public class UrlQuerySanitizer { private boolean mPreferFirstRepeatedParameter; private ValueSanitizer mUnregisteredParameterValueSanitizer = getAllIllegal(); - + /** - * A functor used to sanitize a single query value. + * A functor used to sanitize a single query value. * */ public static interface ValueSanitizer { @@ -108,7 +108,7 @@ public class UrlQuerySanitizer { */ public String sanitize(String value); } - + /** * Sanitize values based on which characters they contain. Illegal * characters are replaced with either space or '_', depending upon @@ -117,7 +117,7 @@ public class UrlQuerySanitizer { public static class IllegalCharacterValueSanitizer implements ValueSanitizer { private int mFlags; - + /** * Allow space (' ') characters. */ @@ -165,21 +165,21 @@ public class UrlQuerySanitizer { * such as "javascript:" or "vbscript:" */ public final static int SCRIPT_URL_OK = 1 << 10; - + /** * Mask with all fields set to OK */ public final static int ALL_OK = 0x7ff; - + /** * Mask with both regular space and other whitespace OK */ public final static int ALL_WHITESPACE_OK = SPACE_OK | OTHER_WHITESPACE_OK; - + // Common flag combinations: - + /** * <ul> * <li>Deny all special characters. @@ -262,18 +262,18 @@ public class UrlQuerySanitizer { */ public final static int ALL_BUT_NUL_AND_ANGLE_BRACKETS_LEGAL = ALL_OK & ~(NUL_OK | LT_OK | GT_OK); - + /** * Script URL definitions */ - + private final static String JAVASCRIPT_PREFIX = "javascript:"; - + private final static String VBSCRIPT_PREFIX = "vbscript:"; - + private final static int MIN_SCRIPT_PREFIX_LENGTH = Math.min( JAVASCRIPT_PREFIX.length(), VBSCRIPT_PREFIX.length()); - + /** * Construct a sanitizer. The parameters set the behavior of the * sanitizer. @@ -312,7 +312,7 @@ public class UrlQuerySanitizer { } } } - + // If whitespace isn't OK, get rid of whitespace at beginning // and end of value. if ( (mFlags & ALL_WHITESPACE_OK) == 0) { @@ -337,7 +337,7 @@ public class UrlQuerySanitizer { } return stringBuilder.toString(); } - + /** * Trim whitespace from the beginning and end of a string. * <p> @@ -361,7 +361,7 @@ public class UrlQuerySanitizer { } return value.substring(start, end + 1); } - + /** * Check if c is whitespace. * @param c character to test @@ -380,7 +380,7 @@ public class UrlQuerySanitizer { return false; } } - + /** * Check whether an individual character is legal. Uses the * flag bit-set passed into the constructor. @@ -400,11 +400,11 @@ public class UrlQuerySanitizer { case '%' : return (mFlags & PCT_OK) != 0; case '\0': return (mFlags & NUL_OK) != 0; default : return (c >= 32 && c < 127) || - (c >= 128 && c <= 255 && ((mFlags & NON_7_BIT_ASCII_OK) != 0)); - } + ((c >= 128) && ((mFlags & NON_7_BIT_ASCII_OK) != 0)); + } } } - + /** * Get the current value sanitizer used when processing * unregistered parameter values. @@ -412,14 +412,14 @@ public class UrlQuerySanitizer { * <b>Note:</b> The default unregistered parameter value sanitizer is * one that doesn't allow any special characters, similar to what * is returned by calling createAllIllegal. - * + * * @return the current ValueSanitizer used to sanitize unregistered * parameter values. */ public ValueSanitizer getUnregisteredParameterValueSanitizer() { return mUnregisteredParameterValueSanitizer; } - + /** * Set the value sanitizer used when processing unregistered * parameter values. @@ -430,46 +430,46 @@ public class UrlQuerySanitizer { ValueSanitizer sanitizer) { mUnregisteredParameterValueSanitizer = sanitizer; } - - + + // Private fields for singleton sanitizers: - + private static final ValueSanitizer sAllIllegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.ALL_ILLEGAL); - + private static final ValueSanitizer sAllButNulLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.ALL_BUT_NUL_LEGAL); - + private static final ValueSanitizer sAllButWhitespaceLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.ALL_BUT_WHITESPACE_LEGAL); - + private static final ValueSanitizer sURLLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.URL_LEGAL); - + private static final ValueSanitizer sUrlAndSpaceLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.URL_AND_SPACE_LEGAL); - + private static final ValueSanitizer sAmpLegal = new IllegalCharacterValueSanitizer( - IllegalCharacterValueSanitizer.AMP_LEGAL); - + IllegalCharacterValueSanitizer.AMP_LEGAL); + private static final ValueSanitizer sAmpAndSpaceLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.AMP_AND_SPACE_LEGAL); - + private static final ValueSanitizer sSpaceLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.SPACE_LEGAL); - + private static final ValueSanitizer sAllButNulAndAngleBracketsLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.ALL_BUT_NUL_AND_ANGLE_BRACKETS_LEGAL); - + /** * Return a value sanitizer that does not allow any special characters, * and also does not allow script URLs. @@ -478,7 +478,7 @@ public class UrlQuerySanitizer { public static final ValueSanitizer getAllIllegal() { return sAllIllegal; } - + /** * Return a value sanitizer that allows everything except Nul ('\0') * characters. Script URLs are allowed. @@ -547,7 +547,7 @@ public class UrlQuerySanitizer { public static final ValueSanitizer getAllButNulAndAngleBracketsLegal() { return sAllButNulAndAngleBracketsLegal; } - + /** * Constructs a UrlQuerySanitizer. * <p> @@ -560,7 +560,7 @@ public class UrlQuerySanitizer { */ public UrlQuerySanitizer() { } - + /** * Constructs a UrlQuerySanitizer and parse a URL. * This constructor is provided for convenience when the @@ -585,7 +585,7 @@ public class UrlQuerySanitizer { setAllowUnregisteredParamaters(true); parseUrl(url); } - + /** * Parse the query parameters out of an encoded URL. * Works by extracting the query portion from the URL and then @@ -604,7 +604,7 @@ public class UrlQuerySanitizer { } parseQuery(query); } - + /** * Parse a query. A query string is any number of parameter-value clauses * separated by any non-zero number of ampersands. A parameter-value clause @@ -631,7 +631,7 @@ public class UrlQuerySanitizer { } } } - + /** * Get a set of all of the parameters found in the sanitized query. * <p> @@ -641,7 +641,7 @@ public class UrlQuerySanitizer { public Set<String> getParameterSet() { return mEntries.keySet(); } - + /** * An array list of all of the parameter value pairs in the sanitized * query, in the order they appeared in the query. May contain duplicate @@ -691,7 +691,7 @@ public class UrlQuerySanitizer { } mSanitizers.put(parameter, valueSanitizer); } - + /** * Register a value sanitizer for an array of parameters. * @param parameters An array of unencoded parameter names. @@ -705,7 +705,7 @@ public class UrlQuerySanitizer { mSanitizers.put(parameters[i], valueSanitizer); } } - + /** * Set whether or not unregistered parameters are allowed. If they * are not allowed, then they will be dropped when a query is sanitized. @@ -718,7 +718,7 @@ public class UrlQuerySanitizer { boolean allowUnregisteredParamaters) { mAllowUnregisteredParamaters = allowUnregisteredParamaters; } - + /** * Get whether or not unregistered parameters are allowed. If not * allowed, they will be dropped when a query is parsed. @@ -728,10 +728,10 @@ public class UrlQuerySanitizer { public boolean getAllowUnregisteredParamaters() { return mAllowUnregisteredParamaters; } - + /** * Set whether or not the first occurrence of a repeated parameter is - * preferred. True means the first repeated parameter is preferred. + * preferred. True means the first repeated parameter is preferred. * False means that the last repeated parameter is preferred. * <p> * The preferred parameter is the one that is returned when getParameter @@ -746,7 +746,7 @@ public class UrlQuerySanitizer { boolean preferFirstRepeatedParameter) { mPreferFirstRepeatedParameter = preferFirstRepeatedParameter; } - + /** * Get whether or not the first occurrence of a repeated parameter is * preferred. @@ -757,10 +757,10 @@ public class UrlQuerySanitizer { public boolean getPreferFirstRepeatedParameter() { return mPreferFirstRepeatedParameter; } - + /** * Parse an escaped parameter-value pair. The default implementation - * unescapes both the parameter and the value, then looks up the + * unescapes both the parameter and the value, then looks up the * effective value sanitizer for the parameter and uses it to sanitize * the value. If all goes well then addSanitizedValue is called with * the unescaped parameter and the sanitized unescaped value. @@ -779,7 +779,7 @@ public class UrlQuerySanitizer { String sanitizedValue = valueSanitizer.sanitize(unescapedValue); addSanitizedEntry(unescapedParameter, sanitizedValue); } - + /** * Record a sanitized parameter-value pair. Override if you want to * do additional filtering or validation. @@ -796,7 +796,7 @@ public class UrlQuerySanitizer { } mEntries.put(parameter, value); } - + /** * Get the value sanitizer for a parameter. Returns null if there * is no value sanitizer registered for the parameter. @@ -807,7 +807,7 @@ public class UrlQuerySanitizer { public ValueSanitizer getValueSanitizer(String parameter) { return mSanitizers.get(parameter); } - + /** * Get the effective value sanitizer for a parameter. Like getValueSanitizer, * except if there is no value sanitizer registered for a parameter, and @@ -823,7 +823,7 @@ public class UrlQuerySanitizer { } return sanitizer; } - + /** * Unescape an escaped string. * <ul> @@ -867,7 +867,7 @@ public class UrlQuerySanitizer { } return stringBuilder.toString(); } - + /** * Test if a character is a hexidecimal digit. Both upper case and lower * case hex digits are allowed. @@ -877,7 +877,7 @@ public class UrlQuerySanitizer { protected boolean isHexDigit(char c) { return decodeHexDigit(c) >= 0; } - + /** * Convert a character that represents a hexidecimal digit into an integer. * If the character is not a hexidecimal digit, then -1 is returned. @@ -885,7 +885,7 @@ public class UrlQuerySanitizer { * @param c the hexidecimal digit. * @return the integer value of the hexidecimal digit. */ - + protected int decodeHexDigit(char c) { if (c >= '0' && c <= '9') { return c - '0'; @@ -900,7 +900,7 @@ public class UrlQuerySanitizer { return -1; } } - + /** * Clear the existing entries. Called to get ready to parse a new * query string. diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java index 01442ae..c2013d5 100644 --- a/core/java/android/net/http/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -26,7 +26,6 @@ import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.entity.AbstractHttpEntity; import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.ClientProtocolException; @@ -48,6 +47,8 @@ import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.BasicHttpProcessor; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.BasicHttpContext; +import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; +import org.apache.harmony.xnet.provider.jsse.SSLContextImpl; import java.io.IOException; import java.io.InputStream; @@ -56,12 +57,13 @@ import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.net.URI; -import java.util.concurrent.atomic.AtomicInteger; +import java.security.KeyManagementException; import android.util.Log; import android.content.ContentResolver; import android.provider.Settings; import android.text.TextUtils; +import android.os.SystemProperties; /** * Subclass of the Apache {@link DefaultHttpClient} that is configured with @@ -100,10 +102,13 @@ public final class AndroidHttpClient implements HttpClient { /** * Create a new HttpClient with reasonable defaults (which you can update). + * * @param userAgent to report in your HTTP requests. + * @param sessionCache persistent session cache * @return AndroidHttpClient for you to use for all your requests. */ - public static AndroidHttpClient newInstance(String userAgent) { + public static AndroidHttpClient newInstance(String userAgent, + SSLClientSessionCache sessionCache) { HttpParams params = new BasicHttpParams(); // Turn off stale checking. Our connections break all the time anyway, @@ -125,7 +130,8 @@ public final class AndroidHttpClient implements HttpClient { schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", - SSLSocketFactory.getSocketFactory(), 443)); + socketFactoryWithCache(sessionCache), 443)); + ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry); @@ -134,6 +140,41 @@ public final class AndroidHttpClient implements HttpClient { return new AndroidHttpClient(manager, params); } + /** + * Returns a socket factory backed by the given persistent session cache. + * + * @param sessionCache to retrieve sessions from, null for no cache + */ + private static SSLSocketFactory socketFactoryWithCache( + SSLClientSessionCache sessionCache) { + if (sessionCache == null) { + // Use the default factory which doesn't support persistent + // caching. + return SSLSocketFactory.getSocketFactory(); + } + + // Create a new SSL context backed by the cache. + // TODO: Keep a weak *identity* hash map of caches to engines. In the + // mean time, if we have two engines for the same cache, they'll still + // share sessions but will have to do so through the persistent cache. + SSLContextImpl sslContext = new SSLContextImpl(); + try { + sslContext.engineInit(null, null, null, sessionCache, null); + } catch (KeyManagementException e) { + throw new AssertionError(e); + } + return new SSLSocketFactory(sslContext.engineGetSocketFactory()); + } + + /** + * Create a new HttpClient with reasonable defaults (which you can update). + * @param userAgent to report in your HTTP requests. + * @return AndroidHttpClient for you to use for all your requests. + */ + public static AndroidHttpClient newInstance(String userAgent) { + return newInstance(userAgent, null /* session cache */); + } + private final HttpClient delegate; private RuntimeException mLeakedException = new IllegalStateException( @@ -347,6 +388,15 @@ public final class AndroidHttpClient implements HttpClient { } /** + * Returns true if auth logging is turned on for this configuration. Can only be set on + * insecure devices. + */ + private boolean isAuthLoggable() { + String secure = SystemProperties.get("ro.secure"); + return "0".equals(secure) && Log.isLoggable(tag + "-auth", level); + } + + /** * Prints a message using this configuration. */ private void println(String message) { @@ -392,7 +442,8 @@ public final class AndroidHttpClient implements HttpClient { if (configuration != null && configuration.isLoggable() && request instanceof HttpUriRequest) { - configuration.println(toCurl((HttpUriRequest) request)); + configuration.println(toCurl((HttpUriRequest) request, + configuration.isAuthLoggable())); } } } @@ -400,12 +451,17 @@ public final class AndroidHttpClient implements HttpClient { /** * Generates a cURL command equivalent to the given request. */ - private static String toCurl(HttpUriRequest request) throws IOException { + private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException { StringBuilder builder = new StringBuilder(); builder.append("curl "); for (Header header: request.getAllHeaders()) { + if (!logAuthToken + && (header.getName().equals("Authorization") || + header.getName().equals("Cookie"))) { + continue; + } builder.append("--header \""); builder.append(header.toString().trim()); builder.append("\" "); diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index b7f7368..0edbe5b 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -16,8 +16,6 @@ package android.net.http; -import android.os.SystemClock; - import java.io.IOException; import java.security.cert.Certificate; @@ -28,23 +26,13 @@ import java.security.cert.X509Certificate; import java.security.GeneralSecurityException; import java.security.KeyStore; -import java.util.Arrays; -import java.util.Date; -import java.util.Enumeration; - -import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -import org.apache.http.HttpHost; - -import org.bouncycastle.asn1.x509.X509Name; - /** * Class responsible for all server certificate validation functionality * @@ -52,9 +40,6 @@ import org.bouncycastle.asn1.x509.X509Name; */ class CertificateChainValidator { - private static long sTotal = 0; - private static long sTotalReused = 0; - /** * The singleton instance of the certificate chain validator */ @@ -110,91 +95,42 @@ class CertificateChainValidator { * @return An SSL error object if there is an error and null otherwise */ public SslError doHandshakeAndValidateServerCertificates( - HttpsConnection connection, SSLSocket sslSocket, String domain) - throws SSLHandshakeException, IOException { - - ++sTotal; - - SSLContext sslContext = HttpsConnection.getContext(); - if (sslContext == null) { - closeSocketThrowException(sslSocket, "SSL context is null"); - } - + HttpsConnection connection, SSLSocket sslSocket, String domain) + throws IOException { X509Certificate[] serverCertificates = null; - long sessionBeforeHandshakeLastAccessedTime = 0; - byte[] sessionBeforeHandshakeId = null; - - SSLSession sessionAfterHandshake = null; - - synchronized(sslContext) { - // get SSL session before the handshake - SSLSession sessionBeforeHandshake = - getSSLSession(sslContext, connection.getHost()); - if (sessionBeforeHandshake != null) { - sessionBeforeHandshakeLastAccessedTime = - sessionBeforeHandshake.getLastAccessedTime(); + // start handshake, close the socket if we fail + try { + sslSocket.setUseClientMode(true); + sslSocket.startHandshake(); + } catch (IOException e) { + closeSocketThrowException( + sslSocket, e.getMessage(), + "failed to perform SSL handshake"); + } - sessionBeforeHandshakeId = - sessionBeforeHandshake.getId(); - } + // retrieve the chain of the server peer certificates + Certificate[] peerCertificates = + sslSocket.getSession().getPeerCertificates(); - // start handshake, close the socket if we fail - try { - sslSocket.setUseClientMode(true); - sslSocket.startHandshake(); - } catch (IOException e) { - closeSocketThrowException( - sslSocket, e.getMessage(), - "failed to perform SSL handshake"); + if (peerCertificates == null || peerCertificates.length <= 0) { + closeSocketThrowException( + sslSocket, "failed to retrieve peer certificates"); + } else { + serverCertificates = + new X509Certificate[peerCertificates.length]; + for (int i = 0; i < peerCertificates.length; ++i) { + serverCertificates[i] = + (X509Certificate)(peerCertificates[i]); } - // retrieve the chain of the server peer certificates - Certificate[] peerCertificates = - sslSocket.getSession().getPeerCertificates(); - - if (peerCertificates == null || peerCertificates.length <= 0) { - closeSocketThrowException( - sslSocket, "failed to retrieve peer certificates"); - } else { - serverCertificates = - new X509Certificate[peerCertificates.length]; - for (int i = 0; i < peerCertificates.length; ++i) { - serverCertificates[i] = - (X509Certificate)(peerCertificates[i]); - } - - // update the SSL certificate associated with the connection - if (connection != null) { - if (serverCertificates[0] != null) { - connection.setCertificate( - new SslCertificate(serverCertificates[0])); - } + // update the SSL certificate associated with the connection + if (connection != null) { + if (serverCertificates[0] != null) { + connection.setCertificate( + new SslCertificate(serverCertificates[0])); } } - - // get SSL session after the handshake - sessionAfterHandshake = - getSSLSession(sslContext, connection.getHost()); - } - - if (sessionBeforeHandshakeLastAccessedTime != 0 && - sessionAfterHandshake != null && - Arrays.equals( - sessionBeforeHandshakeId, sessionAfterHandshake.getId()) && - sessionBeforeHandshakeLastAccessedTime < - sessionAfterHandshake.getLastAccessedTime()) { - - if (HttpLog.LOGV) { - HttpLog.v("SSL session was reused: total reused: " - + sTotalReused - + " out of total of: " + sTotal); - - ++sTotalReused; - } - - // no errors!!! - return null; } // check if the first certificate in the chain is for this site @@ -216,7 +152,6 @@ class CertificateChainValidator { } } - // // first, we validate the chain using the standard validation // solution; if we do not find any errors, we are done; if we // fail the standard validation, we re-validate again below, @@ -393,14 +328,14 @@ class CertificateChainValidator { } private void closeSocketThrowException( - SSLSocket socket, String errorMessage, String defaultErrorMessage) - throws SSLHandshakeException, IOException { + SSLSocket socket, String errorMessage, String defaultErrorMessage) + throws IOException { closeSocketThrowException( socket, errorMessage != null ? errorMessage : defaultErrorMessage); } - private void closeSocketThrowException(SSLSocket socket, String errorMessage) - throws SSLHandshakeException, IOException { + private void closeSocketThrowException(SSLSocket socket, + String errorMessage) throws IOException { if (HttpLog.LOGV) { HttpLog.v("validation error: " + errorMessage); } @@ -416,29 +351,4 @@ class CertificateChainValidator { throw new SSLHandshakeException(errorMessage); } - - /** - * @param sslContext The SSL context shared accross all the SSL sessions - * @param host The host associated with the session - * @return A suitable SSL session from the SSL context - */ - private SSLSession getSSLSession(SSLContext sslContext, HttpHost host) { - if (sslContext != null && host != null) { - Enumeration en = sslContext.getClientSessionContext().getIds(); - while (en.hasMoreElements()) { - byte[] id = (byte[]) en.nextElement(); - if (id != null) { - SSLSession session = - sslContext.getClientSessionContext().getSession(id); - if (session.isValid() && - host.getHostName().equals(session.getPeerHost()) && - host.getPort() == session.getPeerPort()) { - return session; - } - } - } - } - - return null; - } } diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index 65e6117..c4ee5b0 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -55,7 +55,7 @@ public class RequestHandle { private final static String AUTHORIZATION_HEADER = "Authorization"; private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"; - private final static int MAX_REDIRECT_COUNT = 16; + public final static int MAX_REDIRECT_COUNT = 16; /** * Creates a new request session. @@ -106,6 +106,14 @@ public class RequestHandle { return mRedirectCount >= MAX_REDIRECT_COUNT; } + public int getRedirectCount() { + return mRedirectCount; + } + + public void setRedirectCount(int count) { + mRedirectCount = count; + } + /** * Create and queue a redirect request. * |