diff options
Diffstat (limited to 'src/com/android/browser/provider/SQLiteContentProvider.java')
| -rw-r--r-- | src/com/android/browser/provider/SQLiteContentProvider.java | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/com/android/browser/provider/SQLiteContentProvider.java b/src/com/android/browser/provider/SQLiteContentProvider.java new file mode 100644 index 000000000..a50894a41 --- /dev/null +++ b/src/com/android/browser/provider/SQLiteContentProvider.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.browser.provider; + +import android.content.ContentProvider; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteTransactionListener; +import android.net.Uri; + +import java.util.ArrayList; + +/** + * General purpose {@link ContentProvider} base class that uses SQLiteDatabase for storage. + */ +public abstract class SQLiteContentProvider extends ContentProvider + implements SQLiteTransactionListener { + + private static final String TAG = "SQLiteContentProvider"; + + private SQLiteOpenHelper mOpenHelper; + private volatile boolean mNotifyChange; + protected SQLiteDatabase mDb; + + private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>(); + private static final int SLEEP_AFTER_YIELD_DELAY = 4000; + + /** + * Maximum number of operations allowed in a batch between yield points. + */ + private static final int MAX_OPERATIONS_PER_YIELD_POINT = 500; + + @Override + public boolean onCreate() { + Context context = getContext(); + mOpenHelper = getDatabaseHelper(context); + return true; + } + + /** + * Returns a {@link SQLiteOpenHelper} that can open the database. + */ + public abstract SQLiteOpenHelper getDatabaseHelper(Context context); + + /** + * The equivalent of the {@link #insert} method, but invoked within a transaction. + */ + public abstract Uri insertInTransaction(Uri uri, ContentValues values, + boolean callerIsSyncAdapter); + + /** + * The equivalent of the {@link #update} method, but invoked within a transaction. + */ + public abstract int updateInTransaction(Uri uri, ContentValues values, String selection, + String[] selectionArgs, boolean callerIsSyncAdapter); + + /** + * The equivalent of the {@link #delete} method, but invoked within a transaction. + */ + public abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, + boolean callerIsSyncAdapter); + + /** + * Called when the provider needs to notify the system of a change. + * @param callerIsSyncAdapter true if the caller that caused the change was a sync adapter. + */ + public abstract void notifyChange(boolean callerIsSyncAdapter); + + public boolean isCallerSyncAdapter(Uri uri) { + return false; + } + + public SQLiteOpenHelper getDatabaseHelper() { + return mOpenHelper; + } + + private boolean applyingBatch() { + return mApplyingBatch.get() != null && mApplyingBatch.get(); + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + Uri result = null; + boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); + boolean applyingBatch = applyingBatch(); + if (!applyingBatch) { + mDb = mOpenHelper.getWritableDatabase(); + mDb.beginTransactionWithListener(this); + try { + result = insertInTransaction(uri, values, callerIsSyncAdapter); + if (result != null) { + mNotifyChange = true; + } + mDb.setTransactionSuccessful(); + } finally { + mDb.endTransaction(); + } + + onEndTransaction(callerIsSyncAdapter); + } else { + result = insertInTransaction(uri, values, callerIsSyncAdapter); + if (result != null) { + mNotifyChange = true; + } + } + return result; + } + + @Override + public int bulkInsert(Uri uri, ContentValues[] values) { + int numValues = values.length; + boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); + mDb = mOpenHelper.getWritableDatabase(); + mDb.beginTransactionWithListener(this); + try { + for (int i = 0; i < numValues; i++) { + Uri result = insertInTransaction(uri, values[i], callerIsSyncAdapter); + if (result != null) { + mNotifyChange = true; + } + mDb.yieldIfContendedSafely(); + } + mDb.setTransactionSuccessful(); + } finally { + mDb.endTransaction(); + } + + onEndTransaction(callerIsSyncAdapter); + return numValues; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + int count = 0; + boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); + boolean applyingBatch = applyingBatch(); + if (!applyingBatch) { + mDb = mOpenHelper.getWritableDatabase(); + mDb.beginTransactionWithListener(this); + try { + count = updateInTransaction(uri, values, selection, selectionArgs, + callerIsSyncAdapter); + if (count > 0) { + mNotifyChange = true; + } + mDb.setTransactionSuccessful(); + } finally { + mDb.endTransaction(); + } + + onEndTransaction(callerIsSyncAdapter); + } else { + count = updateInTransaction(uri, values, selection, selectionArgs, callerIsSyncAdapter); + if (count > 0) { + mNotifyChange = true; + } + } + + return count; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + int count = 0; + boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); + boolean applyingBatch = applyingBatch(); + if (!applyingBatch) { + mDb = mOpenHelper.getWritableDatabase(); + mDb.beginTransactionWithListener(this); + try { + count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter); + if (count > 0) { + mNotifyChange = true; + } + mDb.setTransactionSuccessful(); + } finally { + mDb.endTransaction(); + } + + onEndTransaction(callerIsSyncAdapter); + } else { + count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter); + if (count > 0) { + mNotifyChange = true; + } + } + return count; + } + + @Override + public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) + throws OperationApplicationException { + int ypCount = 0; + int opCount = 0; + boolean callerIsSyncAdapter = false; + mDb = mOpenHelper.getWritableDatabase(); + mDb.beginTransactionWithListener(this); + try { + mApplyingBatch.set(true); + final int numOperations = operations.size(); + final ContentProviderResult[] results = new ContentProviderResult[numOperations]; + for (int i = 0; i < numOperations; i++) { + if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) { + throw new OperationApplicationException( + "Too many content provider operations between yield points. " + + "The maximum number of operations per yield point is " + + MAX_OPERATIONS_PER_YIELD_POINT, ypCount); + } + final ContentProviderOperation operation = operations.get(i); + if (!callerIsSyncAdapter && isCallerSyncAdapter(operation.getUri())) { + callerIsSyncAdapter = true; + } + if (i > 0 && operation.isYieldAllowed()) { + opCount = 0; + if (mDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY)) { + ypCount++; + } + } + results[i] = operation.apply(this, results, i); + } + mDb.setTransactionSuccessful(); + return results; + } finally { + mApplyingBatch.set(false); + mDb.endTransaction(); + onEndTransaction(callerIsSyncAdapter); + } + } + + @Override + public void onBegin() { + onBeginTransaction(); + } + + @Override + public void onCommit() { + beforeTransactionCommit(); + } + + @Override + public void onRollback() { + // not used + } + + protected void onBeginTransaction() { + } + + protected void beforeTransactionCommit() { + } + + protected void onEndTransaction(boolean callerIsSyncAdapter) { + if (mNotifyChange) { + mNotifyChange = false; + notifyChange(callerIsSyncAdapter); + } + } +} |
