• 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.browser.provider;
18 
19 import android.content.ContentProvider;
20 import android.content.ContentProviderOperation;
21 import android.content.ContentProviderResult;
22 import android.content.ContentResolver;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.OperationApplicationException;
26 import android.database.sqlite.SQLiteDatabase;
27 import android.database.sqlite.SQLiteOpenHelper;
28 import android.net.Uri;
29 
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.Set;
33 
34 /**
35  * General purpose {@link ContentProvider} base class that uses SQLiteDatabase for storage.
36  */
37 public abstract class SQLiteContentProvider extends ContentProvider {
38 
39     private static final String TAG = "SQLiteContentProvider";
40 
41     private SQLiteOpenHelper mOpenHelper;
42     private Set<Uri> mChangedUris;
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     /**
49      * Maximum number of operations allowed in a batch between yield points.
50      */
51     private static final int MAX_OPERATIONS_PER_YIELD_POINT = 500;
52 
53     @Override
onCreate()54     public boolean onCreate() {
55         Context context = getContext();
56         mOpenHelper = getDatabaseHelper(context);
57         mChangedUris = new HashSet<Uri>();
58         return true;
59     }
60 
61     /**
62      * Returns a {@link SQLiteOpenHelper} that can open the database.
63      */
getDatabaseHelper(Context context)64     public abstract SQLiteOpenHelper getDatabaseHelper(Context context);
65 
66     /**
67      * The equivalent of the {@link #insert} method, but invoked within a transaction.
68      */
insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter)69     public abstract Uri insertInTransaction(Uri uri, ContentValues values,
70             boolean callerIsSyncAdapter);
71 
72     /**
73      * The equivalent of the {@link #update} method, but invoked within a transaction.
74      */
updateInTransaction(Uri uri, ContentValues values, String selection, String[] selectionArgs, boolean callerIsSyncAdapter)75     public abstract int updateInTransaction(Uri uri, ContentValues values, String selection,
76             String[] selectionArgs, boolean callerIsSyncAdapter);
77 
78     /**
79      * The equivalent of the {@link #delete} method, but invoked within a transaction.
80      */
deleteInTransaction(Uri uri, String selection, String[] selectionArgs, boolean callerIsSyncAdapter)81     public abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs,
82             boolean callerIsSyncAdapter);
83 
84     /**
85      * Call this to add a URI to the list of URIs to be notified when the transaction
86      * is committed.
87      */
postNotifyUri(Uri uri)88     protected void postNotifyUri(Uri uri) {
89         synchronized (mChangedUris) {
90             mChangedUris.add(uri);
91         }
92     }
93 
isCallerSyncAdapter(Uri uri)94     public boolean isCallerSyncAdapter(Uri uri) {
95         return false;
96     }
97 
getDatabaseHelper()98     public SQLiteOpenHelper getDatabaseHelper() {
99         return mOpenHelper;
100     }
101 
applyingBatch()102     private boolean applyingBatch() {
103         return mApplyingBatch.get() != null && mApplyingBatch.get();
104     }
105 
106     @Override
insert(Uri uri, ContentValues values)107     public Uri insert(Uri uri, ContentValues values) {
108         Uri result = null;
109         boolean callerIsSyncAdapter = isCallerSyncAdapter(uri);
110         boolean applyingBatch = applyingBatch();
111         if (!applyingBatch) {
112             mDb = mOpenHelper.getWritableDatabase();
113             mDb.beginTransaction();
114             try {
115                 result = insertInTransaction(uri, values, callerIsSyncAdapter);
116                 mDb.setTransactionSuccessful();
117             } finally {
118                 mDb.endTransaction();
119             }
120 
121             onEndTransaction(callerIsSyncAdapter);
122         } else {
123             result = insertInTransaction(uri, values, callerIsSyncAdapter);
124         }
125         return result;
126     }
127 
128     @Override
bulkInsert(Uri uri, ContentValues[] values)129     public int bulkInsert(Uri uri, ContentValues[] values) {
130         int numValues = values.length;
131         boolean callerIsSyncAdapter = isCallerSyncAdapter(uri);
132         mDb = mOpenHelper.getWritableDatabase();
133         mDb.beginTransaction();
134         try {
135             for (int i = 0; i < numValues; i++) {
136                 Uri result = insertInTransaction(uri, values[i], callerIsSyncAdapter);
137                 mDb.yieldIfContendedSafely();
138             }
139             mDb.setTransactionSuccessful();
140         } finally {
141             mDb.endTransaction();
142         }
143 
144         onEndTransaction(callerIsSyncAdapter);
145         return numValues;
146     }
147 
148     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)149     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
150         int count = 0;
151         boolean callerIsSyncAdapter = isCallerSyncAdapter(uri);
152         boolean applyingBatch = applyingBatch();
153         if (!applyingBatch) {
154             mDb = mOpenHelper.getWritableDatabase();
155             mDb.beginTransaction();
156             try {
157                 count = updateInTransaction(uri, values, selection, selectionArgs,
158                         callerIsSyncAdapter);
159                 mDb.setTransactionSuccessful();
160             } finally {
161                 mDb.endTransaction();
162             }
163 
164             onEndTransaction(callerIsSyncAdapter);
165         } else {
166             count = updateInTransaction(uri, values, selection, selectionArgs, callerIsSyncAdapter);
167         }
168 
169         return count;
170     }
171 
172     @Override
delete(Uri uri, String selection, String[] selectionArgs)173     public int delete(Uri uri, String selection, String[] selectionArgs) {
174         int count = 0;
175         boolean callerIsSyncAdapter = isCallerSyncAdapter(uri);
176         boolean applyingBatch = applyingBatch();
177         if (!applyingBatch) {
178             mDb = mOpenHelper.getWritableDatabase();
179             mDb.beginTransaction();
180             try {
181                 count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter);
182                 mDb.setTransactionSuccessful();
183             } finally {
184                 mDb.endTransaction();
185             }
186 
187             onEndTransaction(callerIsSyncAdapter);
188         } else {
189             count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter);
190         }
191         return count;
192     }
193 
194     @Override
applyBatch(ArrayList<ContentProviderOperation> operations)195     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
196             throws OperationApplicationException {
197         int ypCount = 0;
198         int opCount = 0;
199         boolean callerIsSyncAdapter = false;
200         mDb = mOpenHelper.getWritableDatabase();
201         mDb.beginTransaction();
202         try {
203             mApplyingBatch.set(true);
204             final int numOperations = operations.size();
205             final ContentProviderResult[] results = new ContentProviderResult[numOperations];
206             for (int i = 0; i < numOperations; i++) {
207                 if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) {
208                     throw new OperationApplicationException(
209                             "Too many content provider operations between yield points. "
210                                     + "The maximum number of operations per yield point is "
211                                     + MAX_OPERATIONS_PER_YIELD_POINT, ypCount);
212                 }
213                 final ContentProviderOperation operation = operations.get(i);
214                 if (!callerIsSyncAdapter && isCallerSyncAdapter(operation.getUri())) {
215                     callerIsSyncAdapter = true;
216                 }
217                 if (i > 0 && operation.isYieldAllowed()) {
218                     opCount = 0;
219                     if (mDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY)) {
220                         ypCount++;
221                     }
222                 }
223                 results[i] = operation.apply(this, results, i);
224             }
225             mDb.setTransactionSuccessful();
226             return results;
227         } finally {
228             mApplyingBatch.set(false);
229             mDb.endTransaction();
230             onEndTransaction(callerIsSyncAdapter);
231         }
232     }
233 
onEndTransaction(boolean callerIsSyncAdapter)234     protected void onEndTransaction(boolean callerIsSyncAdapter) {
235         Set<Uri> changed;
236         synchronized (mChangedUris) {
237             changed = new HashSet<Uri>(mChangedUris);
238             mChangedUris.clear();
239         }
240         ContentResolver resolver = getContext().getContentResolver();
241         for (Uri uri : changed) {
242             boolean syncToNetwork = !callerIsSyncAdapter && syncToNetwork(uri);
243             resolver.notifyChange(uri, null, syncToNetwork);
244         }
245     }
246 
syncToNetwork(Uri uri)247     protected boolean syncToNetwork(Uri uri) {
248         return false;
249     }
250 }
251