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 17package com.android.providers.calendar; 18 19import android.content.ContentProvider; 20import android.content.ContentProviderOperation; 21import android.content.ContentProviderResult; 22import android.content.ContentValues; 23import android.content.Context; 24import android.content.OperationApplicationException; 25import android.database.sqlite.SQLiteDatabase; 26import android.database.sqlite.SQLiteOpenHelper; 27import android.database.sqlite.SQLiteTransactionListener; 28import android.net.Uri; 29 30import java.util.ArrayList; 31 32/** 33 * General purpose {@link ContentProvider} base class that uses SQLiteDatabase for storage. 34 */ 35public 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