DbSSLSessionCachepublic class DbSSLSessionCache 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 | 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 | public static final int | MAX_CACHE_SIZE | private final Map | mExternalCache | private DatabaseHelper | mDatabaseHelper | private boolean | mNeedsCacheLoad | public static final String[] | PROJECTION | private static final Map | sInstances |
Constructors Summary |
---|
private DbSSLSessionCache(android.content.Context activityContext)Create a SslSessionCache instance, using the specified context to
initialize the database.
This constructor will use the default database - created for the application
context.
Context appContext = activityContext.getApplicationContext();
mDatabaseHelper = new DatabaseHelper(appContext);
| public DbSSLSessionCache(DatabaseHelper database)Create a SslSessionCache that uses a specific database.
this.mDatabaseHelper = database;
|
Methods Summary |
---|
public void | clear()Reset the database and internal state.
Used for testing or to free space.
synchronized(this) {
try {
mExternalCache.clear();
mNeedsCacheLoad = true;
mDatabaseHelper.getWritableDatabase().delete(SSL_CACHE_TABLE,
null, null);
} catch (SQLException ex) {
Log.d(TAG, "Error removing SSL cached entries ", ex);
// ignore - nothing we can do about it
}
}
| public static synchronized com.android.internal.net.DbSSLSessionCache | getInstanceForPackage(android.content.Context context)Returns a singleton instance of the DbSSLSessionCache that should be used for this
context's package.
String packageName = context.getPackageName();
if (sInstances.containsKey(packageName)) {
return sInstances.get(packageName);
}
DbSSLSessionCache cache = new DbSSLSessionCache(context);
sInstances.put(packageName, cache);
return cache;
| 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 for
// server-side sessions.
return null;
| public void | putSessionData(javax.net.ssl.SSLSession session, byte[] der)
if (mDatabaseHelper == null) {
return;
}
synchronized (this.getClass()) {
SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
if (mExternalCache.size() == MAX_CACHE_SIZE) {
// remove oldest.
// TODO: check if the new one is in cached already ( i.e. update ).
Cursor byTime = mDatabaseHelper.getReadableDatabase().query(SSL_CACHE_TABLE,
PROJECTION, null, null, null, null, SSL_CACHE_TIME_SEC);
if (byTime.moveToFirst()) {
// TODO: can I do byTime.deleteRow() ?
String hostPort = byTime.getString(SSL_CACHE_HOSTPORT_COL);
db.delete(SSL_CACHE_TABLE,
SSL_CACHE_HOSTPORT + "= ?" , new String[] { hostPort });
mExternalCache.remove(hostPort);
} else {
Log.w(TAG, "No rows found");
// something is wrong, clear it
clear();
}
}
// 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);
mExternalCache.put(key, der);
try {
db.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));
}
}
|
|