• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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