private void initCacheDatabase(Context context) {
    assert !JniUtil.useChromiumHttpStack();

    try {
      mCacheDatabase = context.openOrCreateDatabase(CACHE_DATABASE_FILE, 0, null);
    } catch (SQLiteException e) {
      // try again by deleting the old db and create a new one
      if (context.deleteDatabase(CACHE_DATABASE_FILE)) {
        mCacheDatabase = context.openOrCreateDatabase(CACHE_DATABASE_FILE, 0, null);
      }
    }
    mCacheDatabase.enableWriteAheadLogging();

    // mCacheDatabase should not be null,
    // the only case is RequestAPI test has problem to create db
    if (mCacheDatabase == null) {
      mInitialized = true;
      notify();
      return;
    }

    if (mCacheDatabase.getVersion() != CACHE_DATABASE_VERSION) {
      mCacheDatabase.beginTransactionNonExclusive();
      try {
        upgradeCacheDatabase();
        bootstrapCacheDatabase();
        mCacheDatabase.setTransactionSuccessful();
      } finally {
        mCacheDatabase.endTransaction();
      }
      // Erase the files from the file system in the
      // case that the database was updated and the
      // there were existing cache content
      CacheManager.removeAllCacheFiles();
    }

    // use read_uncommitted to speed up READ
    mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;");
    // as only READ can be called in the
    // non-WebViewWorkerThread, and read_uncommitted is used,
    // we can turn off database lock to use transaction.
    mCacheDatabase.setLockingEnabled(false);

    // use InsertHelper for faster insertion
    mCacheInserter = new DatabaseUtils.InsertHelper(mCacheDatabase, "cache");
    mCacheUrlColIndex = mCacheInserter.getColumnIndex(CACHE_URL_COL);
    mCacheFilePathColIndex = mCacheInserter.getColumnIndex(CACHE_FILE_PATH_COL);
    mCacheLastModifyColIndex = mCacheInserter.getColumnIndex(CACHE_LAST_MODIFY_COL);
    mCacheETagColIndex = mCacheInserter.getColumnIndex(CACHE_ETAG_COL);
    mCacheExpiresColIndex = mCacheInserter.getColumnIndex(CACHE_EXPIRES_COL);
    mCacheExpiresStringColIndex = mCacheInserter.getColumnIndex(CACHE_EXPIRES_STRING_COL);
    mCacheMimeTypeColIndex = mCacheInserter.getColumnIndex(CACHE_MIMETYPE_COL);
    mCacheEncodingColIndex = mCacheInserter.getColumnIndex(CACHE_ENCODING_COL);
    mCacheHttpStatusColIndex = mCacheInserter.getColumnIndex(CACHE_HTTP_STATUS_COL);
    mCacheLocationColIndex = mCacheInserter.getColumnIndex(CACHE_LOCATION_COL);
    mCacheContentLengthColIndex = mCacheInserter.getColumnIndex(CACHE_CONTENTLENGTH_COL);
    mCacheContentDispositionColIndex = mCacheInserter.getColumnIndex(CACHE_CONTENTDISPOSITION_COL);
    mCacheCrossDomainColIndex = mCacheInserter.getColumnIndex(CACHE_CROSSDOMAIN_COL);
  }
  /**
   * * Remove a cache item.
   *
   * @param url The url
   */
  void removeCache(String url) {
    assert !JniUtil.useChromiumHttpStack();

    if (url == null || !checkInitialized()) {
      return;
    }

    mCacheDatabase.execSQL("DELETE FROM cache WHERE url = ?", new String[] {url});
  }
  private synchronized void init(Context context) {
    if (mInitialized) {
      return;
    }

    initDatabase(context);
    if (!JniUtil.useChromiumHttpStack()) {
      initCacheDatabase(context);
    }

    // Thread done, notify.
    mInitialized = true;
    notify();
  }
  /**
   * * Add or update a cache. CACHE_URL_COL is unique in the table.
   *
   * @param url The url
   * @param c The CacheManager.CacheResult
   */
  void addCache(String url, CacheResult c) {
    assert !JniUtil.useChromiumHttpStack();

    if (url == null || !checkInitialized()) {
      return;
    }

    mCacheInserter.prepareForInsert();
    mCacheInserter.bind(mCacheUrlColIndex, url);
    mCacheInserter.bind(mCacheFilePathColIndex, c.localPath);
    mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified);
    mCacheInserter.bind(mCacheETagColIndex, c.etag);
    mCacheInserter.bind(mCacheExpiresColIndex, c.expires);
    mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString);
    mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType);
    mCacheInserter.bind(mCacheEncodingColIndex, c.encoding);
    mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode);
    mCacheInserter.bind(mCacheLocationColIndex, c.location);
    mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength);
    mCacheInserter.bind(mCacheContentDispositionColIndex, c.contentdisposition);
    mCacheInserter.bind(mCacheCrossDomainColIndex, c.crossDomain);
    mCacheInserter.execute();
  }
  /**
   * * Get a cache item.
   *
   * @param url The url
   * @return CacheResult The CacheManager.CacheResult
   */
  CacheResult getCache(String url) {
    assert !JniUtil.useChromiumHttpStack();

    if (url == null || !checkInitialized()) {
      return null;
    }

    Cursor cursor = null;
    final String query =
        "SELECT filepath, lastmodify, etag, expires, "
            + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
            + "contentdisposition, crossdomain FROM cache WHERE url = ?";
    try {
      cursor = mCacheDatabase.rawQuery(query, new String[] {url});
      if (cursor.moveToFirst()) {
        CacheResult ret = new CacheResult();
        ret.localPath = cursor.getString(0);
        ret.lastModified = cursor.getString(1);
        ret.etag = cursor.getString(2);
        ret.expires = cursor.getLong(3);
        ret.expiresString = cursor.getString(4);
        ret.mimeType = cursor.getString(5);
        ret.encoding = cursor.getString(6);
        ret.httpStatusCode = cursor.getInt(7);
        ret.location = cursor.getString(8);
        ret.contentLength = cursor.getLong(9);
        ret.contentdisposition = cursor.getString(10);
        ret.crossDomain = cursor.getString(11);
        return ret;
      }
    } catch (IllegalStateException e) {
      Log.e(LOGTAG, "getCache", e);
    } finally {
      if (cursor != null) cursor.close();
    }
    return null;
  }