1078f588cef389358adabc579de00747878f3c108Dave Santoro/*
2078f588cef389358adabc579de00747878f3c108Dave Santoro * Copyright (C) 2011 The Android Open Source Project
3078f588cef389358adabc579de00747878f3c108Dave Santoro *
4078f588cef389358adabc579de00747878f3c108Dave Santoro * Licensed under the Apache License, Version 2.0 (the "License");
5078f588cef389358adabc579de00747878f3c108Dave Santoro * you may not use this file except in compliance with the License.
6078f588cef389358adabc579de00747878f3c108Dave Santoro * You may obtain a copy of the License at
7078f588cef389358adabc579de00747878f3c108Dave Santoro *
8078f588cef389358adabc579de00747878f3c108Dave Santoro *      http://www.apache.org/licenses/LICENSE-2.0
9078f588cef389358adabc579de00747878f3c108Dave Santoro *
10078f588cef389358adabc579de00747878f3c108Dave Santoro * Unless required by applicable law or agreed to in writing, software
11078f588cef389358adabc579de00747878f3c108Dave Santoro * distributed under the License is distributed on an "AS IS" BASIS,
12078f588cef389358adabc579de00747878f3c108Dave Santoro * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13078f588cef389358adabc579de00747878f3c108Dave Santoro * See the License for the specific language governing permissions and
14078f588cef389358adabc579de00747878f3c108Dave Santoro * limitations under the License
15078f588cef389358adabc579de00747878f3c108Dave Santoro */
16078f588cef389358adabc579de00747878f3c108Dave Santoro
17078f588cef389358adabc579de00747878f3c108Dave Santoropackage com.android.providers.contacts;
18078f588cef389358adabc579de00747878f3c108Dave Santoro
19078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.content.ContentProvider;
20078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.content.ContentProviderOperation;
21078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.content.ContentProviderResult;
22078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.content.ContentValues;
23078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.content.Context;
24078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.content.OperationApplicationException;
25078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.database.sqlite.SQLiteOpenHelper;
26078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.database.sqlite.SQLiteTransactionListener;
27078f588cef389358adabc579de00747878f3c108Dave Santoroimport android.net.Uri;
2874e8f30b3a67af4defd8f73c503e794785671feeMakoto Onukiimport android.util.Log;
29078f588cef389358adabc579de00747878f3c108Dave Santoro
30078f588cef389358adabc579de00747878f3c108Dave Santoroimport java.util.ArrayList;
31078f588cef389358adabc579de00747878f3c108Dave Santoro
32078f588cef389358adabc579de00747878f3c108Dave Santoro/**
33078f588cef389358adabc579de00747878f3c108Dave Santoro * A common base class for the contacts and profile providers.  This handles much of the same
34078f588cef389358adabc579de00747878f3c108Dave Santoro * logic that SQLiteContentProvider does (i.e. starting transactions on the appropriate database),
35078f588cef389358adabc579de00747878f3c108Dave Santoro * but exposes awareness of batch operations to the subclass so that cross-database operations
36078f588cef389358adabc579de00747878f3c108Dave Santoro * can be supported.
37078f588cef389358adabc579de00747878f3c108Dave Santoro */
38078f588cef389358adabc579de00747878f3c108Dave Santoropublic abstract class AbstractContactsProvider extends ContentProvider
39078f588cef389358adabc579de00747878f3c108Dave Santoro        implements SQLiteTransactionListener {
40078f588cef389358adabc579de00747878f3c108Dave Santoro
41ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki    public static final String TAG = "ContactsProvider";
4274e8f30b3a67af4defd8f73c503e794785671feeMakoto Onuki
43ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki    public static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
44ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki
45ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki    /** Set true to enable detailed transaction logging. */
46ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki    public static final boolean ENABLE_TRANSACTION_LOG = false; // Don't submit with true.
4774e8f30b3a67af4defd8f73c503e794785671feeMakoto Onuki
48078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
49078f588cef389358adabc579de00747878f3c108Dave Santoro     * Duration in ms to sleep after successfully yielding the lock during a batch operation.
50078f588cef389358adabc579de00747878f3c108Dave Santoro     */
51078f588cef389358adabc579de00747878f3c108Dave Santoro    protected static final int SLEEP_AFTER_YIELD_DELAY = 4000;
52078f588cef389358adabc579de00747878f3c108Dave Santoro
53078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
54078f588cef389358adabc579de00747878f3c108Dave Santoro     * Maximum number of operations allowed in a batch between yield points.
55078f588cef389358adabc579de00747878f3c108Dave Santoro     */
56078f588cef389358adabc579de00747878f3c108Dave Santoro    private static final int MAX_OPERATIONS_PER_YIELD_POINT = 500;
57078f588cef389358adabc579de00747878f3c108Dave Santoro
58078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
59376917cea44aec4dfd68dec23522353b142535fdDave Santoro     * Number of inserts performed in bulk to allow before yielding the transaction.
60376917cea44aec4dfd68dec23522353b142535fdDave Santoro     */
61376917cea44aec4dfd68dec23522353b142535fdDave Santoro    private static final int BULK_INSERTS_PER_YIELD_POINT = 50;
62376917cea44aec4dfd68dec23522353b142535fdDave Santoro
63376917cea44aec4dfd68dec23522353b142535fdDave Santoro    /**
64078f588cef389358adabc579de00747878f3c108Dave Santoro     * The contacts transaction that is active in this thread.
65078f588cef389358adabc579de00747878f3c108Dave Santoro     */
666efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    private ThreadLocal<ContactsTransaction> mTransactionHolder;
67078f588cef389358adabc579de00747878f3c108Dave Santoro
68078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
69078f588cef389358adabc579de00747878f3c108Dave Santoro     * The DB helper to use for this content provider.
70078f588cef389358adabc579de00747878f3c108Dave Santoro     */
71078f588cef389358adabc579de00747878f3c108Dave Santoro    private SQLiteOpenHelper mDbHelper;
72078f588cef389358adabc579de00747878f3c108Dave Santoro
73078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
74078f588cef389358adabc579de00747878f3c108Dave Santoro     * The database helper to serialize all transactions on.  If non-null, any new transaction
75078f588cef389358adabc579de00747878f3c108Dave Santoro     * created by this provider will automatically retrieve a writable database from this helper
76078f588cef389358adabc579de00747878f3c108Dave Santoro     * and initiate a transaction on that database.  This should be used to ensure that operations
77078f588cef389358adabc579de00747878f3c108Dave Santoro     * across multiple databases are all blocked on a single DB lock (to prevent deadlock cases).
78ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     *
79ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * Hint: It's always {@link ContactsDatabaseHelper}.
80ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     *
81ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * TODO Change the structure to make it obvious that it's actually always set, and is the
82ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * {@link ContactsDatabaseHelper}.
83078f588cef389358adabc579de00747878f3c108Dave Santoro     */
84078f588cef389358adabc579de00747878f3c108Dave Santoro    private SQLiteOpenHelper mSerializeOnDbHelper;
85078f588cef389358adabc579de00747878f3c108Dave Santoro
86078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
87078f588cef389358adabc579de00747878f3c108Dave Santoro     * The tag corresponding to the database used for serializing transactions.
88ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     *
89ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * Hint: It's always the contacts db helper tag.
90ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     *
91ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * See also the TODO on {@link #mSerializeOnDbHelper}.
92078f588cef389358adabc579de00747878f3c108Dave Santoro     */
93078f588cef389358adabc579de00747878f3c108Dave Santoro    private String mSerializeDbTag;
94078f588cef389358adabc579de00747878f3c108Dave Santoro
95ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki    /**
96ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * The transaction listener used with {@link #mSerializeOnDbHelper}.
97ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     *
98ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * Hint: It's always {@link ContactsProvider2}.
99ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     *
100ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * See also the TODO on {@link #mSerializeOnDbHelper}.
101ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     */
102ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki    private SQLiteTransactionListener mSerializedDbTransactionListener;
103ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki
104078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
105078f588cef389358adabc579de00747878f3c108Dave Santoro    public boolean onCreate() {
106078f588cef389358adabc579de00747878f3c108Dave Santoro        Context context = getContext();
107078f588cef389358adabc579de00747878f3c108Dave Santoro        mDbHelper = getDatabaseHelper(context);
1086efb7db26598b105342d02207e0ca1c8725c10daDave Santoro        mTransactionHolder = getTransactionHolder();
109078f588cef389358adabc579de00747878f3c108Dave Santoro        return true;
110078f588cef389358adabc579de00747878f3c108Dave Santoro    }
111078f588cef389358adabc579de00747878f3c108Dave Santoro
112078f588cef389358adabc579de00747878f3c108Dave Santoro    public SQLiteOpenHelper getDatabaseHelper() {
113078f588cef389358adabc579de00747878f3c108Dave Santoro        return mDbHelper;
114078f588cef389358adabc579de00747878f3c108Dave Santoro    }
115078f588cef389358adabc579de00747878f3c108Dave Santoro
116078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
117078f588cef389358adabc579de00747878f3c108Dave Santoro     * Specifies a database helper (and corresponding tag) to serialize all transactions on.
118ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     *
119ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki     * See also the TODO on {@link #mSerializeOnDbHelper}.
120078f588cef389358adabc579de00747878f3c108Dave Santoro     */
121ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki    public void setDbHelperToSerializeOn(SQLiteOpenHelper serializeOnDbHelper, String tag,
122ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki            SQLiteTransactionListener listener) {
123078f588cef389358adabc579de00747878f3c108Dave Santoro        mSerializeOnDbHelper = serializeOnDbHelper;
124078f588cef389358adabc579de00747878f3c108Dave Santoro        mSerializeDbTag = tag;
125ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki        mSerializedDbTransactionListener = listener;
126078f588cef389358adabc579de00747878f3c108Dave Santoro    }
127078f588cef389358adabc579de00747878f3c108Dave Santoro
128078f588cef389358adabc579de00747878f3c108Dave Santoro    public ContactsTransaction getCurrentTransaction() {
1296efb7db26598b105342d02207e0ca1c8725c10daDave Santoro        return mTransactionHolder.get();
130078f588cef389358adabc579de00747878f3c108Dave Santoro    }
131078f588cef389358adabc579de00747878f3c108Dave Santoro
132078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
133078f588cef389358adabc579de00747878f3c108Dave Santoro    public Uri insert(Uri uri, ContentValues values) {
134078f588cef389358adabc579de00747878f3c108Dave Santoro        ContactsTransaction transaction = startTransaction(false);
135078f588cef389358adabc579de00747878f3c108Dave Santoro        try {
136078f588cef389358adabc579de00747878f3c108Dave Santoro            Uri result = insertInTransaction(uri, values);
137078f588cef389358adabc579de00747878f3c108Dave Santoro            if (result != null) {
138078f588cef389358adabc579de00747878f3c108Dave Santoro                transaction.markDirty();
139078f588cef389358adabc579de00747878f3c108Dave Santoro            }
140078f588cef389358adabc579de00747878f3c108Dave Santoro            transaction.markSuccessful(false);
141078f588cef389358adabc579de00747878f3c108Dave Santoro            return result;
142078f588cef389358adabc579de00747878f3c108Dave Santoro        } finally {
143078f588cef389358adabc579de00747878f3c108Dave Santoro            endTransaction(false);
144078f588cef389358adabc579de00747878f3c108Dave Santoro        }
145078f588cef389358adabc579de00747878f3c108Dave Santoro    }
146078f588cef389358adabc579de00747878f3c108Dave Santoro
147078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
148078f588cef389358adabc579de00747878f3c108Dave Santoro    public int delete(Uri uri, String selection, String[] selectionArgs) {
149078f588cef389358adabc579de00747878f3c108Dave Santoro        ContactsTransaction transaction = startTransaction(false);
150078f588cef389358adabc579de00747878f3c108Dave Santoro        try {
151078f588cef389358adabc579de00747878f3c108Dave Santoro            int deleted = deleteInTransaction(uri, selection, selectionArgs);
152078f588cef389358adabc579de00747878f3c108Dave Santoro            if (deleted > 0) {
153078f588cef389358adabc579de00747878f3c108Dave Santoro                transaction.markDirty();
154078f588cef389358adabc579de00747878f3c108Dave Santoro            }
155078f588cef389358adabc579de00747878f3c108Dave Santoro            transaction.markSuccessful(false);
156078f588cef389358adabc579de00747878f3c108Dave Santoro            return deleted;
157078f588cef389358adabc579de00747878f3c108Dave Santoro        } finally {
158078f588cef389358adabc579de00747878f3c108Dave Santoro            endTransaction(false);
159078f588cef389358adabc579de00747878f3c108Dave Santoro        }
160078f588cef389358adabc579de00747878f3c108Dave Santoro    }
161078f588cef389358adabc579de00747878f3c108Dave Santoro
162078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
163078f588cef389358adabc579de00747878f3c108Dave Santoro    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
164078f588cef389358adabc579de00747878f3c108Dave Santoro        ContactsTransaction transaction = startTransaction(false);
165078f588cef389358adabc579de00747878f3c108Dave Santoro        try {
166078f588cef389358adabc579de00747878f3c108Dave Santoro            int updated = updateInTransaction(uri, values, selection, selectionArgs);
167078f588cef389358adabc579de00747878f3c108Dave Santoro            if (updated > 0) {
168078f588cef389358adabc579de00747878f3c108Dave Santoro                transaction.markDirty();
169078f588cef389358adabc579de00747878f3c108Dave Santoro            }
170078f588cef389358adabc579de00747878f3c108Dave Santoro            transaction.markSuccessful(false);
171078f588cef389358adabc579de00747878f3c108Dave Santoro            return updated;
172078f588cef389358adabc579de00747878f3c108Dave Santoro        } finally {
173078f588cef389358adabc579de00747878f3c108Dave Santoro            endTransaction(false);
174078f588cef389358adabc579de00747878f3c108Dave Santoro        }
175078f588cef389358adabc579de00747878f3c108Dave Santoro    }
176078f588cef389358adabc579de00747878f3c108Dave Santoro
177078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
178078f588cef389358adabc579de00747878f3c108Dave Santoro    public int bulkInsert(Uri uri, ContentValues[] values) {
179078f588cef389358adabc579de00747878f3c108Dave Santoro        ContactsTransaction transaction = startTransaction(true);
180078f588cef389358adabc579de00747878f3c108Dave Santoro        int numValues = values.length;
181376917cea44aec4dfd68dec23522353b142535fdDave Santoro        int opCount = 0;
182078f588cef389358adabc579de00747878f3c108Dave Santoro        try {
183078f588cef389358adabc579de00747878f3c108Dave Santoro            for (int i = 0; i < numValues; i++) {
184078f588cef389358adabc579de00747878f3c108Dave Santoro                insert(uri, values[i]);
185376917cea44aec4dfd68dec23522353b142535fdDave Santoro                if (++opCount >= BULK_INSERTS_PER_YIELD_POINT) {
186376917cea44aec4dfd68dec23522353b142535fdDave Santoro                    opCount = 0;
187b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                    try {
188b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                        yield(transaction);
189b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                    } catch (RuntimeException re) {
190b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                        transaction.markYieldFailed();
191b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                        throw re;
192b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                    }
193376917cea44aec4dfd68dec23522353b142535fdDave Santoro                }
194078f588cef389358adabc579de00747878f3c108Dave Santoro            }
195078f588cef389358adabc579de00747878f3c108Dave Santoro            transaction.markSuccessful(true);
196078f588cef389358adabc579de00747878f3c108Dave Santoro        } finally {
197078f588cef389358adabc579de00747878f3c108Dave Santoro            endTransaction(true);
198078f588cef389358adabc579de00747878f3c108Dave Santoro        }
199078f588cef389358adabc579de00747878f3c108Dave Santoro        return numValues;
200078f588cef389358adabc579de00747878f3c108Dave Santoro    }
201078f588cef389358adabc579de00747878f3c108Dave Santoro
202078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
203078f588cef389358adabc579de00747878f3c108Dave Santoro    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
204078f588cef389358adabc579de00747878f3c108Dave Santoro            throws OperationApplicationException {
20574e8f30b3a67af4defd8f73c503e794785671feeMakoto Onuki        if (VERBOSE_LOGGING) {
20674e8f30b3a67af4defd8f73c503e794785671feeMakoto Onuki            Log.v(TAG, "applyBatch: " + operations.size() + " ops");
20774e8f30b3a67af4defd8f73c503e794785671feeMakoto Onuki        }
208078f588cef389358adabc579de00747878f3c108Dave Santoro        int ypCount = 0;
209078f588cef389358adabc579de00747878f3c108Dave Santoro        int opCount = 0;
210078f588cef389358adabc579de00747878f3c108Dave Santoro        ContactsTransaction transaction = startTransaction(true);
211078f588cef389358adabc579de00747878f3c108Dave Santoro        try {
212078f588cef389358adabc579de00747878f3c108Dave Santoro            final int numOperations = operations.size();
213078f588cef389358adabc579de00747878f3c108Dave Santoro            final ContentProviderResult[] results = new ContentProviderResult[numOperations];
214078f588cef389358adabc579de00747878f3c108Dave Santoro            for (int i = 0; i < numOperations; i++) {
215078f588cef389358adabc579de00747878f3c108Dave Santoro                if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) {
216078f588cef389358adabc579de00747878f3c108Dave Santoro                    throw new OperationApplicationException(
217078f588cef389358adabc579de00747878f3c108Dave Santoro                            "Too many content provider operations between yield points. "
218078f588cef389358adabc579de00747878f3c108Dave Santoro                                    + "The maximum number of operations per yield point is "
219078f588cef389358adabc579de00747878f3c108Dave Santoro                                    + MAX_OPERATIONS_PER_YIELD_POINT, ypCount);
220078f588cef389358adabc579de00747878f3c108Dave Santoro                }
221078f588cef389358adabc579de00747878f3c108Dave Santoro                final ContentProviderOperation operation = operations.get(i);
222078f588cef389358adabc579de00747878f3c108Dave Santoro                if (i > 0 && operation.isYieldAllowed()) {
22374e8f30b3a67af4defd8f73c503e794785671feeMakoto Onuki                    if (VERBOSE_LOGGING) {
22474e8f30b3a67af4defd8f73c503e794785671feeMakoto Onuki                        Log.v(TAG, "applyBatch: " + opCount + " ops finished; about to yield...");
22574e8f30b3a67af4defd8f73c503e794785671feeMakoto Onuki                    }
226078f588cef389358adabc579de00747878f3c108Dave Santoro                    opCount = 0;
227b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                    try {
228b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                        if (yield(transaction)) {
229b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                            ypCount++;
230b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                        }
231b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                    } catch (RuntimeException re) {
232b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                        transaction.markYieldFailed();
233b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                        throw re;
234078f588cef389358adabc579de00747878f3c108Dave Santoro                    }
235078f588cef389358adabc579de00747878f3c108Dave Santoro                }
236078f588cef389358adabc579de00747878f3c108Dave Santoro
237078f588cef389358adabc579de00747878f3c108Dave Santoro                results[i] = operation.apply(this, results, i);
238078f588cef389358adabc579de00747878f3c108Dave Santoro            }
239078f588cef389358adabc579de00747878f3c108Dave Santoro            transaction.markSuccessful(true);
240078f588cef389358adabc579de00747878f3c108Dave Santoro            return results;
241078f588cef389358adabc579de00747878f3c108Dave Santoro        } finally {
242078f588cef389358adabc579de00747878f3c108Dave Santoro            endTransaction(true);
243078f588cef389358adabc579de00747878f3c108Dave Santoro        }
244078f588cef389358adabc579de00747878f3c108Dave Santoro    }
245078f588cef389358adabc579de00747878f3c108Dave Santoro
246078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
247078f588cef389358adabc579de00747878f3c108Dave Santoro     * If we are not yet already in a transaction, this starts one (on the DB to serialize on, if
248078f588cef389358adabc579de00747878f3c108Dave Santoro     * present) and sets the thread-local transaction variable for tracking.  If we are already in
249078f588cef389358adabc579de00747878f3c108Dave Santoro     * a transaction, this returns that transaction, and the batch parameter is ignored.
250078f588cef389358adabc579de00747878f3c108Dave Santoro     * @param callerIsBatch Whether the caller is operating in batch mode.
251078f588cef389358adabc579de00747878f3c108Dave Santoro     */
252078f588cef389358adabc579de00747878f3c108Dave Santoro    private ContactsTransaction startTransaction(boolean callerIsBatch) {
253ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki        if (ENABLE_TRANSACTION_LOG) {
254ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki            Log.i(TAG, "startTransaction " + getClass().getSimpleName() +
255ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki                    "  callerIsBatch=" + callerIsBatch, new RuntimeException("startTransaction"));
256ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki        }
2576efb7db26598b105342d02207e0ca1c8725c10daDave Santoro        ContactsTransaction transaction = mTransactionHolder.get();
258078f588cef389358adabc579de00747878f3c108Dave Santoro        if (transaction == null) {
259078f588cef389358adabc579de00747878f3c108Dave Santoro            transaction = new ContactsTransaction(callerIsBatch);
260078f588cef389358adabc579de00747878f3c108Dave Santoro            if (mSerializeOnDbHelper != null) {
261078f588cef389358adabc579de00747878f3c108Dave Santoro                transaction.startTransactionForDb(mSerializeOnDbHelper.getWritableDatabase(),
262ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki                        mSerializeDbTag, mSerializedDbTransactionListener);
263078f588cef389358adabc579de00747878f3c108Dave Santoro            }
2646efb7db26598b105342d02207e0ca1c8725c10daDave Santoro            mTransactionHolder.set(transaction);
265078f588cef389358adabc579de00747878f3c108Dave Santoro        }
266078f588cef389358adabc579de00747878f3c108Dave Santoro        return transaction;
267078f588cef389358adabc579de00747878f3c108Dave Santoro    }
268078f588cef389358adabc579de00747878f3c108Dave Santoro
269078f588cef389358adabc579de00747878f3c108Dave Santoro    /**
270078f588cef389358adabc579de00747878f3c108Dave Santoro     * Ends the current transaction and clears out the member variable.  This does not set the
271078f588cef389358adabc579de00747878f3c108Dave Santoro     * transaction as being successful.
272078f588cef389358adabc579de00747878f3c108Dave Santoro     * @param callerIsBatch Whether the caller is operating in batch mode.
273078f588cef389358adabc579de00747878f3c108Dave Santoro     */
274078f588cef389358adabc579de00747878f3c108Dave Santoro    private void endTransaction(boolean callerIsBatch) {
275ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki        if (ENABLE_TRANSACTION_LOG) {
276ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki            Log.i(TAG, "endTransaction " + getClass().getSimpleName() +
277ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki                    "  callerIsBatch=" + callerIsBatch, new RuntimeException("endTransaction"));
278ae32283e7fc5b749df96523d8bb343b9068b65baMakoto Onuki        }
2796efb7db26598b105342d02207e0ca1c8725c10daDave Santoro        ContactsTransaction transaction = mTransactionHolder.get();
280078f588cef389358adabc579de00747878f3c108Dave Santoro        if (transaction != null && (!transaction.isBatch() || callerIsBatch)) {
281b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro            try {
282b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                if (transaction.isDirty()) {
283b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                    notifyChange();
284b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                }
285b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                transaction.finish(callerIsBatch);
286b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro            } finally {
287b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                // No matter what, make sure we clear out the thread-local transaction reference.
288b91899a113c499ef96fe5719398558b4cfd128c2Dave Santoro                mTransactionHolder.set(null);
289078f588cef389358adabc579de00747878f3c108Dave Santoro            }
290078f588cef389358adabc579de00747878f3c108Dave Santoro        }
291078f588cef389358adabc579de00747878f3c108Dave Santoro    }
292078f588cef389358adabc579de00747878f3c108Dave Santoro
2936efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    /**
2946efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * Gets the database helper for this contacts provider.  This is called once, during onCreate().
2956efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     */
2966efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected abstract SQLiteOpenHelper getDatabaseHelper(Context context);
2976efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
2986efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    /**
2996efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * Gets the thread-local transaction holder to use for keeping track of the transaction.  This
3006efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * is called once, in onCreate().  If multiple classes are inheriting from this class that need
3016efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * to be kept in sync on the same transaction, they must all return the same thread-local.
3026efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     */
3036efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected abstract ThreadLocal<ContactsTransaction> getTransactionHolder();
3046efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
305078f588cef389358adabc579de00747878f3c108Dave Santoro    protected abstract Uri insertInTransaction(Uri uri, ContentValues values);
306078f588cef389358adabc579de00747878f3c108Dave Santoro
307078f588cef389358adabc579de00747878f3c108Dave Santoro    protected abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs);
308078f588cef389358adabc579de00747878f3c108Dave Santoro
309078f588cef389358adabc579de00747878f3c108Dave Santoro    protected abstract int updateInTransaction(Uri uri, ContentValues values, String selection,
310078f588cef389358adabc579de00747878f3c108Dave Santoro            String[] selectionArgs);
311078f588cef389358adabc579de00747878f3c108Dave Santoro
312078f588cef389358adabc579de00747878f3c108Dave Santoro    protected abstract boolean yield(ContactsTransaction transaction);
313078f588cef389358adabc579de00747878f3c108Dave Santoro
314078f588cef389358adabc579de00747878f3c108Dave Santoro    protected abstract void notifyChange();
315078f588cef389358adabc579de00747878f3c108Dave Santoro}
316