mobile/android/base/db/SharedBrowserDatabaseProvider.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.db;
michael@0 6
michael@0 7 import org.mozilla.gecko.db.BrowserContract.CommonColumns;
michael@0 8 import org.mozilla.gecko.db.BrowserContract.SyncColumns;
michael@0 9 import org.mozilla.gecko.db.PerProfileDatabases.DatabaseHelperFactory;
michael@0 10
michael@0 11 import android.content.Context;
michael@0 12 import android.database.Cursor;
michael@0 13 import android.database.sqlite.SQLiteDatabase;
michael@0 14 import android.net.Uri;
michael@0 15 import android.util.Log;
michael@0 16
michael@0 17 /**
michael@0 18 * A ContentProvider subclass that provides per-profile browser.db access
michael@0 19 * that can be safely shared between multiple providers.
michael@0 20 *
michael@0 21 * If multiple ContentProvider classes wish to share a database, it's
michael@0 22 * vitally important that they use the same SQLiteOpenHelpers for access.
michael@0 23 *
michael@0 24 * Failure to do so can cause accidental concurrent writes, with the result
michael@0 25 * being unexpected SQLITE_BUSY errors.
michael@0 26 *
michael@0 27 * This class provides a static {@link PerProfileDatabases} instance, lazily
michael@0 28 * initialized within {@link SharedBrowserDatabaseProvider#onCreate()}.
michael@0 29 */
michael@0 30 public abstract class SharedBrowserDatabaseProvider extends AbstractPerProfileDatabaseProvider {
michael@0 31 private static final String LOGTAG = SharedBrowserDatabaseProvider.class.getSimpleName();
michael@0 32
michael@0 33 private static PerProfileDatabases<BrowserDatabaseHelper> databases;
michael@0 34
michael@0 35 @Override
michael@0 36 protected PerProfileDatabases<BrowserDatabaseHelper> getDatabases() {
michael@0 37 return databases;
michael@0 38 }
michael@0 39
michael@0 40 @Override
michael@0 41 public boolean onCreate() {
michael@0 42 // If necessary, do the shared DB work.
michael@0 43 synchronized (SharedBrowserDatabaseProvider.class) {
michael@0 44 if (databases != null) {
michael@0 45 return true;
michael@0 46 }
michael@0 47
michael@0 48 final DatabaseHelperFactory<BrowserDatabaseHelper> helperFactory = new DatabaseHelperFactory<BrowserDatabaseHelper>() {
michael@0 49 @Override
michael@0 50 public BrowserDatabaseHelper makeDatabaseHelper(Context context, String databasePath) {
michael@0 51 return new BrowserDatabaseHelper(context, databasePath);
michael@0 52 }
michael@0 53 };
michael@0 54
michael@0 55 databases = new PerProfileDatabases<BrowserDatabaseHelper>(getContext(), BrowserDatabaseHelper.DATABASE_NAME, helperFactory);
michael@0 56 }
michael@0 57
michael@0 58 return true;
michael@0 59 }
michael@0 60
michael@0 61 /**
michael@0 62 * Clean up some deleted records from the specified table.
michael@0 63 *
michael@0 64 * If called in an existing transaction, it is the caller's responsibility
michael@0 65 * to ensure that the transaction is already upgraded to a writer, because
michael@0 66 * this method issues a read followed by a write, and thus is potentially
michael@0 67 * vulnerable to an unhandled SQLITE_BUSY failure during the upgrade.
michael@0 68 *
michael@0 69 * If not called in an existing transaction, no new explicit transaction
michael@0 70 * will be begun.
michael@0 71 */
michael@0 72 protected void cleanUpSomeDeletedRecords(Uri fromUri, String tableName) {
michael@0 73 Log.d(LOGTAG, "Cleaning up deleted records from " + tableName);
michael@0 74
michael@0 75 // We clean up records marked as deleted that are older than a
michael@0 76 // predefined max age. It's important not be too greedy here and
michael@0 77 // remove only a few old deleted records at a time.
michael@0 78
michael@0 79 // we cleanup records marked as deleted that are older than a
michael@0 80 // predefined max age. It's important not be too greedy here and
michael@0 81 // remove only a few old deleted records at a time.
michael@0 82
michael@0 83 // Maximum age of deleted records to be cleaned up (20 days in ms)
michael@0 84 final long MAX_AGE_OF_DELETED_RECORDS = 86400000 * 20;
michael@0 85
michael@0 86 // Number of records marked as deleted to be removed
michael@0 87 final long DELETED_RECORDS_PURGE_LIMIT = 5;
michael@0 88
michael@0 89 // Android SQLite doesn't have LIMIT on DELETE. Instead, query for the
michael@0 90 // IDs of matching rows, then delete them in one go.
michael@0 91 final long now = System.currentTimeMillis();
michael@0 92 final String selection = SyncColumns.IS_DELETED + " = 1 AND " +
michael@0 93 SyncColumns.DATE_MODIFIED + " <= " +
michael@0 94 (now - MAX_AGE_OF_DELETED_RECORDS);
michael@0 95
michael@0 96 final String profile = fromUri.getQueryParameter(BrowserContract.PARAM_PROFILE);
michael@0 97 final SQLiteDatabase db = getWritableDatabaseForProfile(profile, isTest(fromUri));
michael@0 98 final String[] ids;
michael@0 99 final String limit = Long.toString(DELETED_RECORDS_PURGE_LIMIT, 10);
michael@0 100 final Cursor cursor = db.query(tableName, new String[] { CommonColumns._ID }, selection, null, null, null, null, limit);
michael@0 101 try {
michael@0 102 ids = new String[cursor.getCount()];
michael@0 103 int i = 0;
michael@0 104 while (cursor.moveToNext()) {
michael@0 105 ids[i++] = Long.toString(cursor.getLong(0), 10);
michael@0 106 }
michael@0 107 } finally {
michael@0 108 cursor.close();
michael@0 109 }
michael@0 110
michael@0 111 final String inClause = computeSQLInClause(ids.length,
michael@0 112 CommonColumns._ID);
michael@0 113 db.delete(tableName, inClause, ids);
michael@0 114 }
michael@0 115 }

mercurial