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