DatabaseSessionCachepublic class DatabaseSessionCache extends Object implements org.apache.harmony.xnet.provider.jsse.SSLClientSessionCacheHook into harmony SSL cache to persist the SSL sessions.
Current implementation is suitable for saving a small number of hosts -
like google services. It can be extended with expiration and more features
to support more hosts.
{@hide} |
Fields Summary |
---|
private static final String | TAG | static DatabaseHelper | sDefaultDatabaseHelper | private DatabaseHelper | mDatabaseHelper | public static final String | SSL_CACHE_TABLETable where sessions are stored. | private static final String | SSL_CACHE_ID | private static final String | SSL_CACHE_HOSTPORTKey is host:port - port is not optional. | private static final String | SSL_CACHE_SESSIONBase64-encoded DER value of the session. | private static final String | SSL_CACHE_TIME_SECTime when the record was added - should be close to the time
of the initial session negotiation. | public static final String | DATABASE_NAME | public static final int | DATABASE_VERSION | public static final int | SSL_CACHE_ID_COLpublic for testing | public static final int | SSL_CACHE_HOSTPORT_COL | public static final int | SSL_CACHE_SESSION_COL | public static final int | SSL_CACHE_TIME_SEC_COL | private static final String | SAVE_ON_ADD | static boolean | sHookInitializationDone | public static final int | MAX_CACHE_SIZE | private static final Map | mExternalCache | static boolean | mNeedsCacheLoad | public static final String[] | PROJECTION |
Constructors Summary |
---|
public DatabaseSessionCache()This class needs to be installed as a hook, if the security property
is set. Getting the right classloader may be fun since we don't use
Provider to get its classloader, but in android this is in same
loader with AndroidHttpClient.
This constructor will use the default database. You must
call init() before to specify the context used for the database and
check settings.
Log.v(TAG, "Instance created.");
// May be null if caching is disabled - no sessions will be persisted.
this.mDatabaseHelper = sDefaultDatabaseHelper;
| public DatabaseSessionCache(android.content.Context activityContext)Create a SslSessionCache instance, using the specified context to
initialize the database.
This constructor will use the default database - created the first
time.
// Static init - only one initialization will happen.
// Each SslSessionCache is using the same DB.
init(activityContext);
// May be null if caching is disabled - no sessions will be persisted.
this.mDatabaseHelper = sDefaultDatabaseHelper;
| public DatabaseSessionCache(DatabaseHelper database)Create a SslSessionCache that uses a specific database.
this.mDatabaseHelper = database;
|
Methods Summary |
---|
public byte[] | getSessionData(java.lang.String host, int port)
// Current (simple) implementation does a single lookup to DB, then saves
// all entries to the cache.
// This works for google services - i.e. small number of certs.
// If we extend this to all processes - we should hold a separate cache
// or do lookups to DB each time.
if (mDatabaseHelper == null) {
return null;
}
synchronized(this.getClass()) {
if (mNeedsCacheLoad) {
// Don't try to load again, if something is wrong on the first
// request it'll likely be wrong each time.
mNeedsCacheLoad = false;
long t0 = System.currentTimeMillis();
Cursor cur = null;
try {
cur = mDatabaseHelper.getReadableDatabase().query(SSL_CACHE_TABLE, PROJECTION, null,
null, null, null, null);
if (cur.moveToFirst()) {
do {
String hostPort = cur.getString(SSL_CACHE_HOSTPORT_COL);
String value = cur.getString(SSL_CACHE_SESSION_COL);
if (hostPort == null || value == null) {
continue;
}
// TODO: blob support ?
byte[] der = Base64.decodeBase64(value.getBytes());
mExternalCache.put(hostPort, der);
} while (cur.moveToNext());
}
} catch (SQLException ex) {
Log.d(TAG, "Error loading SSL cached entries ", ex);
} finally {
if (cur != null) {
cur.close();
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
long t1 = System.currentTimeMillis();
Log.d(TAG, "LOADED CACHED SSL " + (t1 - t0) + " ms");
}
}
}
String key = host + ":" + port;
return mExternalCache.get(key);
}
| public byte[] | getSessionData(byte[] id)
// We support client side only - the cache will do nothing on client.
return null;
| public static synchronized void | init(android.content.Context activityContext)You must call this method to enable SSL session caching for an app.
// It is possible that multiple provider will try to install this hook.
// We want a single db per VM.
if (sHookInitializationDone) {
return;
}
// // More values can be added in future to provide different
// // behaviours, like 'batch save'.
// if (enabled(activityContext)) {
Context appContext = activityContext.getApplicationContext();
sDefaultDatabaseHelper = new DatabaseHelper(appContext);
// Set default SSLSocketFactory
// The property is defined in the javadocs for javax.net.SSLSocketFactory
// (no constant defined there)
// This should cover all code using SSLSocketFactory.getDefault(),
// including native http client and apache httpclient.
// MCS is using its own custom factory - will need special code.
// Security.setProperty("ssl.SocketFactory.provider",
// SslSocketFactoryWithCache.class.getName());
// }
// Won't try again.
sHookInitializationDone = true;
| public void | putSessionData(javax.net.ssl.SSLSession session, byte[] der)
if (mDatabaseHelper == null) {
return;
}
if (mExternalCache.size() > MAX_CACHE_SIZE) {
// remove oldest.
Cursor byTime = mDatabaseHelper.getWritableDatabase().query(SSL_CACHE_TABLE,
PROJECTION, null, null, null, null, SSL_CACHE_TIME_SEC);
byTime.moveToFirst();
// TODO: can I do byTime.deleteRow() ?
String hostPort = byTime.getString(SSL_CACHE_HOSTPORT_COL);
mDatabaseHelper.getWritableDatabase().delete(SSL_CACHE_TABLE,
SSL_CACHE_HOSTPORT + "= ?" , new String[] { hostPort });
}
// Serialize native session to standard DER encoding
long t0 = System.currentTimeMillis();
String b64 = new String(Base64.encodeBase64(der));
String key = session.getPeerHost() + ":" + session.getPeerPort();
ContentValues values = new ContentValues();
values.put(SSL_CACHE_HOSTPORT, key);
values.put(SSL_CACHE_SESSION, b64);
values.put(SSL_CACHE_TIME_SEC, System.currentTimeMillis() / 1000);
synchronized (this.getClass()) {
mExternalCache.put(key, der);
try {
mDatabaseHelper.getWritableDatabase().insert(SSL_CACHE_TABLE, null /*nullColumnHack */ , values);
} catch(SQLException ex) {
// Ignore - nothing we can do to recover, and caller shouldn't
// be affected.
Log.w(TAG, "Ignoring SQL exception when caching session", ex);
}
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
long t1 = System.currentTimeMillis();
Log.d(TAG, "New SSL session " + session.getPeerHost() +
" DER len: " + der.length + " " + (t1 - t0));
}
|
|