Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit;
     18 
     19 import java.util.ArrayList;
     20 import java.util.HashMap;
     21 import java.util.Iterator;
     22 import java.util.List;
     23 import java.util.Set;
     24 import java.util.Map.Entry;
     25 
     26 import android.content.ContentValues;
     27 import android.content.Context;
     28 import android.database.Cursor;
     29 import android.database.DatabaseUtils;
     30 import android.database.sqlite.SQLiteDatabase;
     31 import android.database.sqlite.SQLiteException;
     32 import android.database.sqlite.SQLiteStatement;
     33 import android.util.Log;
     34 
     35 final class WebViewDatabaseClassic extends WebViewDatabase {
     36     private static final String LOGTAG = "WebViewDatabaseClassic";
     37     private static final String DATABASE_FILE = "webview.db";
     38     private static final String CACHE_DATABASE_FILE = "webviewCache.db";
     39 
     40     private static final int DATABASE_VERSION = 11;
     41     // 2 -> 3 Modified Cache table to allow cache of redirects
     42     // 3 -> 4 Added Oma-Downloads table
     43     // 4 -> 5 Modified Cache table to support persistent contentLength
     44     // 5 -> 4 Removed Oma-Downoads table
     45     // 5 -> 6 Add INDEX for cache table
     46     // 6 -> 7 Change cache localPath from int to String
     47     // 7 -> 8 Move cache to its own db
     48     // 8 -> 9 Store both scheme and host when storing passwords
     49     // 9 -> 10 Update httpauth table UNIQUE
     50     // 10 -> 11 Drop cookies and cache now managed by the chromium stack,
     51     //          and update the form data table to use the new format
     52     //          implemented for b/5265606.
     53 
     54     private static WebViewDatabaseClassic sInstance = null;
     55 
     56     private static SQLiteDatabase sDatabase = null;
     57 
     58     // synchronize locks
     59     private final Object mPasswordLock = new Object();
     60     private final Object mFormLock = new Object();
     61     private final Object mHttpAuthLock = new Object();
     62 
     63     private static final String mTableNames[] = {
     64         "password", "formurl", "formdata", "httpauth"
     65     };
     66 
     67     // Table ids (they are index to mTableNames)
     68     private static final int TABLE_PASSWORD_ID = 0;
     69     private static final int TABLE_FORMURL_ID = 1;
     70     private static final int TABLE_FORMDATA_ID = 2;
     71     private static final int TABLE_HTTPAUTH_ID = 3;
     72 
     73     // column id strings for "_id" which can be used by any table
     74     private static final String ID_COL = "_id";
     75 
     76     private static final String[] ID_PROJECTION = new String[] {
     77         "_id"
     78     };
     79 
     80     // column id strings for "password" table
     81     private static final String PASSWORD_HOST_COL = "host";
     82     private static final String PASSWORD_USERNAME_COL = "username";
     83     private static final String PASSWORD_PASSWORD_COL = "password";
     84 
     85     // column id strings for "formurl" table
     86     private static final String FORMURL_URL_COL = "url";
     87 
     88     // column id strings for "formdata" table
     89     private static final String FORMDATA_URLID_COL = "urlid";
     90     private static final String FORMDATA_NAME_COL = "name";
     91     private static final String FORMDATA_VALUE_COL = "value";
     92 
     93     // column id strings for "httpauth" table
     94     private static final String HTTPAUTH_HOST_COL = "host";
     95     private static final String HTTPAUTH_REALM_COL = "realm";
     96     private static final String HTTPAUTH_USERNAME_COL = "username";
     97     private static final String HTTPAUTH_PASSWORD_COL = "password";
     98 
     99     // Initially true until the background thread completes.
    100     private boolean mInitialized = false;
    101 
    102     WebViewDatabaseClassic(final Context context) {
    103         new Thread() {
    104             @Override
    105             public void run() {
    106                 init(context);
    107             }
    108         }.start();
    109 
    110         // Singleton only, use getInstance()
    111     }
    112 
    113     public static synchronized WebViewDatabaseClassic getInstance(Context context) {
    114         if (sInstance == null) {
    115             sInstance = new WebViewDatabaseClassic(context);
    116         }
    117         return sInstance;
    118     }
    119 
    120     private synchronized void init(Context context) {
    121         if (mInitialized) {
    122             return;
    123         }
    124 
    125         initDatabase(context);
    126         // Before using the Chromium HTTP stack, we stored the WebKit cache in
    127         // our own DB. Clean up the DB file if it's still around.
    128         context.deleteDatabase(CACHE_DATABASE_FILE);
    129 
    130         // Thread done, notify.
    131         mInitialized = true;
    132         notify();
    133     }
    134 
    135     private void initDatabase(Context context) {
    136         try {
    137             sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, null);
    138         } catch (SQLiteException e) {
    139             // try again by deleting the old db and create a new one
    140             if (context.deleteDatabase(DATABASE_FILE)) {
    141                 sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0,
    142                         null);
    143             }
    144         }
    145 
    146         // sDatabase should not be null,
    147         // the only case is RequestAPI test has problem to create db
    148         if (sDatabase == null) {
    149             mInitialized = true;
    150             notify();
    151             return;
    152         }
    153 
    154         if (sDatabase.getVersion() != DATABASE_VERSION) {
    155             sDatabase.beginTransactionNonExclusive();
    156             try {
    157                 upgradeDatabase();
    158                 sDatabase.setTransactionSuccessful();
    159             } finally {
    160                 sDatabase.endTransaction();
    161             }
    162         }
    163     }
    164 
    165     private static void upgradeDatabase() {
    166         upgradeDatabaseToV10();
    167         upgradeDatabaseFromV10ToV11();
    168         // Add future database upgrade functions here, one version at a
    169         // time.
    170         sDatabase.setVersion(DATABASE_VERSION);
    171     }
    172 
    173     private static void upgradeDatabaseFromV10ToV11() {
    174         int oldVersion = sDatabase.getVersion();
    175 
    176         if (oldVersion >= 11) {
    177             // Nothing to do.
    178             return;
    179         }
    180 
    181         // Clear out old java stack cookies - this data is now stored in
    182         // a separate database managed by the Chrome stack.
    183         sDatabase.execSQL("DROP TABLE IF EXISTS cookies");
    184 
    185         // Likewise for the old cache table.
    186         sDatabase.execSQL("DROP TABLE IF EXISTS cache");
    187 
    188         // Update form autocomplete  URLs to match new ICS formatting.
    189         Cursor c = sDatabase.query(mTableNames[TABLE_FORMURL_ID], null, null,
    190                 null, null, null, null);
    191         while (c.moveToNext()) {
    192             String urlId = Long.toString(c.getLong(c.getColumnIndex(ID_COL)));
    193             String url = c.getString(c.getColumnIndex(FORMURL_URL_COL));
    194             ContentValues cv = new ContentValues(1);
    195             cv.put(FORMURL_URL_COL, WebTextView.urlForAutoCompleteData(url));
    196             sDatabase.update(mTableNames[TABLE_FORMURL_ID], cv, ID_COL + "=?",
    197                     new String[] { urlId });
    198         }
    199         c.close();
    200     }
    201 
    202     private static void upgradeDatabaseToV10() {
    203         int oldVersion = sDatabase.getVersion();
    204 
    205         if (oldVersion >= 10) {
    206             // Nothing to do.
    207             return;
    208         }
    209 
    210         if (oldVersion != 0) {
    211             Log.i(LOGTAG, "Upgrading database from version "
    212                     + oldVersion + " to "
    213                     + DATABASE_VERSION + ", which will destroy old data");
    214         }
    215 
    216         if (9 == oldVersion) {
    217             sDatabase.execSQL("DROP TABLE IF EXISTS "
    218                     + mTableNames[TABLE_HTTPAUTH_ID]);
    219             sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID]
    220                     + " (" + ID_COL + " INTEGER PRIMARY KEY, "
    221                     + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
    222                     + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
    223                     + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
    224                     + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
    225                     + ") ON CONFLICT REPLACE);");
    226             return;
    227         }
    228 
    229         sDatabase.execSQL("DROP TABLE IF EXISTS cookies");
    230         sDatabase.execSQL("DROP TABLE IF EXISTS cache");
    231         sDatabase.execSQL("DROP TABLE IF EXISTS "
    232                 + mTableNames[TABLE_FORMURL_ID]);
    233         sDatabase.execSQL("DROP TABLE IF EXISTS "
    234                 + mTableNames[TABLE_FORMDATA_ID]);
    235         sDatabase.execSQL("DROP TABLE IF EXISTS "
    236                 + mTableNames[TABLE_HTTPAUTH_ID]);
    237         sDatabase.execSQL("DROP TABLE IF EXISTS "
    238                 + mTableNames[TABLE_PASSWORD_ID]);
    239 
    240         // formurl
    241         sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID]
    242                 + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL
    243                 + " TEXT" + ");");
    244 
    245         // formdata
    246         sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID]
    247                 + " (" + ID_COL + " INTEGER PRIMARY KEY, "
    248                 + FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL
    249                 + " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE ("
    250                 + FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", "
    251                 + FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);");
    252 
    253         // httpauth
    254         sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID]
    255                 + " (" + ID_COL + " INTEGER PRIMARY KEY, "
    256                 + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
    257                 + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
    258                 + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
    259                 + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
    260                 + ") ON CONFLICT REPLACE);");
    261         // passwords
    262         sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_PASSWORD_ID]
    263                 + " (" + ID_COL + " INTEGER PRIMARY KEY, "
    264                 + PASSWORD_HOST_COL + " TEXT, " + PASSWORD_USERNAME_COL
    265                 + " TEXT, " + PASSWORD_PASSWORD_COL + " TEXT," + " UNIQUE ("
    266                 + PASSWORD_HOST_COL + ", " + PASSWORD_USERNAME_COL
    267                 + ") ON CONFLICT REPLACE);");
    268     }
    269 
    270     // Wait for the background initialization thread to complete and check the
    271     // database creation status.
    272     private boolean checkInitialized() {
    273         synchronized (this) {
    274             while (!mInitialized) {
    275                 try {
    276                     wait();
    277                 } catch (InterruptedException e) {
    278                     Log.e(LOGTAG, "Caught exception while checking " +
    279                                   "initialization");
    280                     Log.e(LOGTAG, Log.getStackTraceString(e));
    281                 }
    282             }
    283         }
    284         return sDatabase != null;
    285     }
    286 
    287     private boolean hasEntries(int tableId) {
    288         if (!checkInitialized()) {
    289             return false;
    290         }
    291 
    292         Cursor cursor = null;
    293         boolean ret = false;
    294         try {
    295             cursor = sDatabase.query(mTableNames[tableId], ID_PROJECTION,
    296                     null, null, null, null, null);
    297             ret = cursor.moveToFirst() == true;
    298         } catch (IllegalStateException e) {
    299             Log.e(LOGTAG, "hasEntries", e);
    300         } finally {
    301             if (cursor != null) cursor.close();
    302         }
    303         return ret;
    304     }
    305 
    306     //
    307     // password functions
    308     //
    309 
    310     /**
    311      * Set password. Tuple (PASSWORD_HOST_COL, PASSWORD_USERNAME_COL) is unique.
    312      *
    313      * @param schemePlusHost The scheme and host for the password
    314      * @param username The username for the password. If it is null, it means
    315      *            password can't be saved.
    316      * @param password The password
    317      */
    318     void setUsernamePassword(String schemePlusHost, String username,
    319                 String password) {
    320         if (schemePlusHost == null || !checkInitialized()) {
    321             return;
    322         }
    323 
    324         synchronized (mPasswordLock) {
    325             final ContentValues c = new ContentValues();
    326             c.put(PASSWORD_HOST_COL, schemePlusHost);
    327             c.put(PASSWORD_USERNAME_COL, username);
    328             c.put(PASSWORD_PASSWORD_COL, password);
    329             sDatabase.insert(mTableNames[TABLE_PASSWORD_ID], PASSWORD_HOST_COL,
    330                     c);
    331         }
    332     }
    333 
    334     /**
    335      * Retrieve the username and password for a given host
    336      *
    337      * @param schemePlusHost The scheme and host which passwords applies to
    338      * @return String[] if found, String[0] is username, which can be null and
    339      *         String[1] is password. Return null if it can't find anything.
    340      */
    341     String[] getUsernamePassword(String schemePlusHost) {
    342         if (schemePlusHost == null || !checkInitialized()) {
    343             return null;
    344         }
    345 
    346         final String[] columns = new String[] {
    347                 PASSWORD_USERNAME_COL, PASSWORD_PASSWORD_COL
    348         };
    349         final String selection = "(" + PASSWORD_HOST_COL + " == ?)";
    350         synchronized (mPasswordLock) {
    351             String[] ret = null;
    352             Cursor cursor = null;
    353             try {
    354                 cursor = sDatabase.query(mTableNames[TABLE_PASSWORD_ID],
    355                         columns, selection, new String[] { schemePlusHost }, null,
    356                         null, null);
    357                 if (cursor.moveToFirst()) {
    358                     ret = new String[2];
    359                     ret[0] = cursor.getString(
    360                             cursor.getColumnIndex(PASSWORD_USERNAME_COL));
    361                     ret[1] = cursor.getString(
    362                             cursor.getColumnIndex(PASSWORD_PASSWORD_COL));
    363                 }
    364             } catch (IllegalStateException e) {
    365                 Log.e(LOGTAG, "getUsernamePassword", e);
    366             } finally {
    367                 if (cursor != null) cursor.close();
    368             }
    369             return ret;
    370         }
    371     }
    372 
    373     /**
    374      * @see WebViewDatabase#hasUsernamePassword
    375      */
    376     @Override
    377     public boolean hasUsernamePassword() {
    378         synchronized (mPasswordLock) {
    379             return hasEntries(TABLE_PASSWORD_ID);
    380         }
    381     }
    382 
    383     /**
    384      * @see WebViewDatabase#clearUsernamePassword
    385      */
    386     @Override
    387     public void clearUsernamePassword() {
    388         if (!checkInitialized()) {
    389             return;
    390         }
    391 
    392         synchronized (mPasswordLock) {
    393             sDatabase.delete(mTableNames[TABLE_PASSWORD_ID], null, null);
    394         }
    395     }
    396 
    397     //
    398     // http authentication password functions
    399     //
    400 
    401     /**
    402      * Set HTTP authentication password. Tuple (HTTPAUTH_HOST_COL,
    403      * HTTPAUTH_REALM_COL, HTTPAUTH_USERNAME_COL) is unique.
    404      *
    405      * @param host The host for the password
    406      * @param realm The realm for the password
    407      * @param username The username for the password. If it is null, it means
    408      *            password can't be saved.
    409      * @param password The password
    410      */
    411     void setHttpAuthUsernamePassword(String host, String realm, String username,
    412             String password) {
    413         if (host == null || realm == null || !checkInitialized()) {
    414             return;
    415         }
    416 
    417         synchronized (mHttpAuthLock) {
    418             final ContentValues c = new ContentValues();
    419             c.put(HTTPAUTH_HOST_COL, host);
    420             c.put(HTTPAUTH_REALM_COL, realm);
    421             c.put(HTTPAUTH_USERNAME_COL, username);
    422             c.put(HTTPAUTH_PASSWORD_COL, password);
    423             sDatabase.insert(mTableNames[TABLE_HTTPAUTH_ID], HTTPAUTH_HOST_COL,
    424                     c);
    425         }
    426     }
    427 
    428     /**
    429      * Retrieve the HTTP authentication username and password for a given
    430      * host+realm pair
    431      *
    432      * @param host The host the password applies to
    433      * @param realm The realm the password applies to
    434      * @return String[] if found, String[0] is username, which can be null and
    435      *         String[1] is password. Return null if it can't find anything.
    436      */
    437     String[] getHttpAuthUsernamePassword(String host, String realm) {
    438         if (host == null || realm == null || !checkInitialized()){
    439             return null;
    440         }
    441 
    442         final String[] columns = new String[] {
    443                 HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL
    444         };
    445         final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND ("
    446                 + HTTPAUTH_REALM_COL + " == ?)";
    447         synchronized (mHttpAuthLock) {
    448             String[] ret = null;
    449             Cursor cursor = null;
    450             try {
    451                 cursor = sDatabase.query(mTableNames[TABLE_HTTPAUTH_ID],
    452                         columns, selection, new String[] { host, realm }, null,
    453                         null, null);
    454                 if (cursor.moveToFirst()) {
    455                     ret = new String[2];
    456                     ret[0] = cursor.getString(
    457                             cursor.getColumnIndex(HTTPAUTH_USERNAME_COL));
    458                     ret[1] = cursor.getString(
    459                             cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL));
    460                 }
    461             } catch (IllegalStateException e) {
    462                 Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
    463             } finally {
    464                 if (cursor != null) cursor.close();
    465             }
    466             return ret;
    467         }
    468     }
    469 
    470     /**
    471      * @see WebViewDatabase#hasHttpAuthUsernamePassword
    472      */
    473     @Override
    474     public boolean hasHttpAuthUsernamePassword() {
    475         synchronized (mHttpAuthLock) {
    476             return hasEntries(TABLE_HTTPAUTH_ID);
    477         }
    478     }
    479 
    480     /**
    481      * @see WebViewDatabase#clearHttpAuthUsernamePassword
    482      */
    483     @Override
    484     public void clearHttpAuthUsernamePassword() {
    485         if (!checkInitialized()) {
    486             return;
    487         }
    488 
    489         synchronized (mHttpAuthLock) {
    490             sDatabase.delete(mTableNames[TABLE_HTTPAUTH_ID], null, null);
    491         }
    492     }
    493 
    494     //
    495     // form data functions
    496     //
    497 
    498     /**
    499      * Set form data for a site. Tuple (FORMDATA_URLID_COL, FORMDATA_NAME_COL,
    500      * FORMDATA_VALUE_COL) is unique
    501      *
    502      * @param url The url of the site
    503      * @param formdata The form data in HashMap
    504      */
    505     void setFormData(String url, HashMap<String, String> formdata) {
    506         if (url == null || formdata == null || !checkInitialized()) {
    507             return;
    508         }
    509 
    510         final String selection = "(" + FORMURL_URL_COL + " == ?)";
    511         synchronized (mFormLock) {
    512             long urlid = -1;
    513             Cursor cursor = null;
    514             try {
    515                 cursor = sDatabase.query(mTableNames[TABLE_FORMURL_ID],
    516                         ID_PROJECTION, selection, new String[] { url }, null, null,
    517                         null);
    518                 if (cursor.moveToFirst()) {
    519                     urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
    520                 } else {
    521                     ContentValues c = new ContentValues();
    522                     c.put(FORMURL_URL_COL, url);
    523                     urlid = sDatabase.insert(
    524                             mTableNames[TABLE_FORMURL_ID], null, c);
    525                 }
    526             } catch (IllegalStateException e) {
    527                 Log.e(LOGTAG, "setFormData", e);
    528             } finally {
    529                 if (cursor != null) cursor.close();
    530             }
    531             if (urlid >= 0) {
    532                 Set<Entry<String, String>> set = formdata.entrySet();
    533                 Iterator<Entry<String, String>> iter = set.iterator();
    534                 ContentValues map = new ContentValues();
    535                 map.put(FORMDATA_URLID_COL, urlid);
    536                 while (iter.hasNext()) {
    537                     Entry<String, String> entry = iter.next();
    538                     map.put(FORMDATA_NAME_COL, entry.getKey());
    539                     map.put(FORMDATA_VALUE_COL, entry.getValue());
    540                     sDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null, map);
    541                 }
    542             }
    543         }
    544     }
    545 
    546     /**
    547      * Get all the values for a form entry with "name" in a given site
    548      *
    549      * @param url The url of the site
    550      * @param name The name of the form entry
    551      * @return A list of values. Return empty list if nothing is found.
    552      */
    553     ArrayList<String> getFormData(String url, String name) {
    554         ArrayList<String> values = new ArrayList<String>();
    555         if (url == null || name == null || !checkInitialized()) {
    556             return values;
    557         }
    558 
    559         final String urlSelection = "(" + FORMURL_URL_COL + " == ?)";
    560         final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND ("
    561                 + FORMDATA_NAME_COL + " == ?)";
    562         synchronized (mFormLock) {
    563             Cursor cursor = null;
    564             try {
    565                 cursor = sDatabase.query(mTableNames[TABLE_FORMURL_ID],
    566                         ID_PROJECTION, urlSelection, new String[] { url }, null,
    567                         null, null);
    568                 while (cursor.moveToNext()) {
    569                     long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
    570                     Cursor dataCursor = null;
    571                     try {
    572                         dataCursor = sDatabase.query(
    573                                 mTableNames[TABLE_FORMDATA_ID],
    574                                 new String[] { ID_COL, FORMDATA_VALUE_COL },
    575                                 dataSelection,
    576                                 new String[] { Long.toString(urlid), name },
    577                                 null, null, null);
    578                         if (dataCursor.moveToFirst()) {
    579                             int valueCol = dataCursor.getColumnIndex(
    580                                     FORMDATA_VALUE_COL);
    581                             do {
    582                                 values.add(dataCursor.getString(valueCol));
    583                             } while (dataCursor.moveToNext());
    584                         }
    585                     } catch (IllegalStateException e) {
    586                         Log.e(LOGTAG, "getFormData dataCursor", e);
    587                     } finally {
    588                         if (dataCursor != null) dataCursor.close();
    589                     }
    590                 }
    591             } catch (IllegalStateException e) {
    592                 Log.e(LOGTAG, "getFormData cursor", e);
    593             } finally {
    594                 if (cursor != null) cursor.close();
    595             }
    596             return values;
    597         }
    598     }
    599 
    600     /**
    601      * @see WebViewDatabase#hasFormData
    602      */
    603     @Override
    604     public boolean hasFormData() {
    605         synchronized (mFormLock) {
    606             return hasEntries(TABLE_FORMURL_ID);
    607         }
    608     }
    609 
    610     /**
    611      * @see WebViewDatabase#clearFormData
    612      */
    613     @Override
    614     public void clearFormData() {
    615         if (!checkInitialized()) {
    616             return;
    617         }
    618 
    619         synchronized (mFormLock) {
    620             sDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null);
    621             sDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null);
    622         }
    623     }
    624 }
    625