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