Home | History | Annotate | Download | only in calendar
      1 /*
      2  * Copyright (C) 2009 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 com.android.providers.calendar;
     18 
     19 import android.content.ContentProvider;
     20 import android.content.ContentProviderOperation;
     21 import android.content.ContentProviderResult;
     22 import android.content.ContentValues;
     23 import android.content.Context;
     24 import android.content.OperationApplicationException;
     25 import android.database.sqlite.SQLiteDatabase;
     26 import android.database.sqlite.SQLiteOpenHelper;
     27 import android.database.sqlite.SQLiteTransactionListener;
     28 import android.net.Uri;
     29 
     30 import java.util.ArrayList;
     31 
     32 /**
     33  * General purpose {@link ContentProvider} base class that uses SQLiteDatabase for storage.
     34  */
     35 public abstract class SQLiteContentProvider extends ContentProvider
     36         implements SQLiteTransactionListener {
     37 
     38     private static final String TAG = "SQLiteContentProvider";
     39 
     40     private SQLiteOpenHelper mOpenHelper;
     41     private volatile boolean mNotifyChange;
     42     protected SQLiteDatabase mDb;
     43 
     44     private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
     45     private static final int SLEEP_AFTER_YIELD_DELAY = 4000;
     46 
     47     @Override
     48     public boolean onCreate() {
     49         Context context = getContext();
     50         mOpenHelper = getDatabaseHelper(context);
     51         return true;
     52     }
     53 
     54     protected abstract SQLiteOpenHelper getDatabaseHelper(Context context);
     55 
     56     /**
     57      * The equivalent of the {@link #insert} method, but invoked within a transaction.
     58      */
     59     protected abstract Uri insertInTransaction(Uri uri, ContentValues values);
     60 
     61     /**
     62      * The equivalent of the {@link #update} method, but invoked within a transaction.
     63      */
     64     protected abstract int updateInTransaction(Uri uri, ContentValues values, String selection,
     65             String[] selectionArgs);
     66 
     67     /**
     68      * The equivalent of the {@link #delete} method, but invoked within a transaction.
     69      */
     70     protected abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs);
     71 
     72     protected abstract void notifyChange();
     73 
     74     protected SQLiteOpenHelper getDatabaseHelper() {
     75         return mOpenHelper;
     76     }
     77 
     78     private boolean applyingBatch() {
     79         return mApplyingBatch.get() != null && mApplyingBatch.get();
     80     }
     81 
     82     @Override
     83     public Uri insert(Uri uri, ContentValues values) {
     84         Uri result = null;
     85         boolean applyingBatch = applyingBatch();
     86         if (!applyingBatch) {
     87             mDb = mOpenHelper.getWritableDatabase();
     88             mDb.beginTransactionWithListener(this);
     89             try {
     90                 result = insertInTransaction(uri, values);
     91                 if (result != null) {
     92                     mNotifyChange = true;
     93                 }
     94                 mDb.setTransactionSuccessful();
     95             } finally {
     96                 mDb.endTransaction();
     97             }
     98 
     99             onEndTransaction();
    100         } else {
    101             result = insertInTransaction(uri, values);
    102             if (result != null) {
    103                 mNotifyChange = true;
    104             }
    105         }
    106         return result;
    107     }
    108 
    109     @Override
    110     public int bulkInsert(Uri uri, ContentValues[] values) {
    111         int numValues = values.length;
    112         mDb = mOpenHelper.getWritableDatabase();
    113         mDb.beginTransactionWithListener(this);
    114         try {
    115             for (int i = 0; i < numValues; i++) {
    116                 Uri result = insertInTransaction(uri, values[i]);
    117                 if (result != null) {
    118                     mNotifyChange = true;
    119                 }
    120                 mDb.yieldIfContendedSafely();
    121             }
    122             mDb.setTransactionSuccessful();
    123         } finally {
    124             mDb.endTransaction();
    125         }
    126 
    127         onEndTransaction();
    128         return numValues;
    129     }
    130 
    131     @Override
    132     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    133         int count = 0;
    134         boolean applyingBatch = applyingBatch();
    135         if (!applyingBatch) {
    136             mDb = mOpenHelper.getWritableDatabase();
    137             mDb.beginTransactionWithListener(this);
    138             try {
    139                 count = updateInTransaction(uri, values, selection, selectionArgs);
    140                 if (count > 0) {
    141                     mNotifyChange = true;
    142                 }
    143                 mDb.setTransactionSuccessful();
    144             } finally {
    145                 mDb.endTransaction();
    146             }
    147 
    148             onEndTransaction();
    149         } else {
    150             count = updateInTransaction(uri, values, selection, selectionArgs);
    151             if (count > 0) {
    152                 mNotifyChange = true;
    153             }
    154         }
    155 
    156         return count;
    157     }
    158 
    159     @Override
    160     public int delete(Uri uri, String selection, String[] selectionArgs) {
    161         int count = 0;
    162         boolean applyingBatch = applyingBatch();
    163         if (!applyingBatch) {
    164             mDb = mOpenHelper.getWritableDatabase();
    165             mDb.beginTransactionWithListener(this);
    166             try {
    167                 count = deleteInTransaction(uri, selection, selectionArgs);
    168                 if (count > 0) {
    169                     mNotifyChange = true;
    170                 }
    171                 mDb.setTransactionSuccessful();
    172             } finally {
    173                 mDb.endTransaction();
    174             }
    175 
    176             onEndTransaction();
    177         } else {
    178             count = deleteInTransaction(uri, selection, selectionArgs);
    179             if (count > 0) {
    180                 mNotifyChange = true;
    181             }
    182         }
    183         return count;
    184     }
    185 
    186     @Override
    187     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
    188             throws OperationApplicationException {
    189         mDb = mOpenHelper.getWritableDatabase();
    190         mDb.beginTransactionWithListener(this);
    191         try {
    192             mApplyingBatch.set(true);
    193             final int numOperations = operations.size();
    194             final ContentProviderResult[] results = new ContentProviderResult[numOperations];
    195             for (int i = 0; i < numOperations; i++) {
    196                 final ContentProviderOperation operation = operations.get(i);
    197                 if (i > 0 && operation.isYieldAllowed()) {
    198                     mDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY);
    199                 }
    200                 results[i] = operation.apply(this, results, i);
    201             }
    202             mDb.setTransactionSuccessful();
    203             return results;
    204         } finally {
    205             mApplyingBatch.set(false);
    206             mDb.endTransaction();
    207             onEndTransaction();
    208         }
    209     }
    210 
    211     public void onBegin() {
    212         onBeginTransaction();
    213     }
    214 
    215     public void onCommit() {
    216         beforeTransactionCommit();
    217     }
    218 
    219     public void onRollback() {
    220         // not used
    221     }
    222 
    223     protected void onBeginTransaction() {
    224     }
    225 
    226     protected void beforeTransactionCommit() {
    227     }
    228 
    229     protected void onEndTransaction() {
    230         if (mNotifyChange) {
    231             mNotifyChange = false;
    232             notifyChange();
    233         }
    234     }
    235 }
    236