DatabaseSessionCache.javaAPI DocAndroid 1.5 API11227Wed May 06 22:42:02 BST 2009android.core


public class DatabaseSessionCache extends Object implements org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache
Hook 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
static DatabaseHelper
private DatabaseHelper
public static final String
Table where sessions are stored.
private static final String
private static final String
Key is host:port - port is not optional.
private static final String
Base64-encoded DER value of the session.
private static final String
Time when the record was added - should be close to the time of the initial session negotiation.
public static final String
public static final int
public static final int
public for testing
public static final int
public static final int
public static final int
private static final String
static boolean
public static final int
private static final Map
static boolean
public static final String[]
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.
        // 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) {
                            // 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) {
                    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 voidinit(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) {

//        // 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
            // (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 voidputSessionData( session, byte[] der)

        if (mDatabaseHelper == null) {
        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);
            // TODO: can I do byTime.deleteRow() ?
            String hostPort = byTime.getString(SSL_CACHE_HOSTPORT_COL);

                    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));