188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov/*
288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * Copyright (C) 2009 The Android Open Source Project
388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov *
488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * you may not use this file except in compliance with the License.
688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * You may obtain a copy of the License at
788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov *
888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov *
1088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
1188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
1288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * See the License for the specific language governing permissions and
1488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * limitations under the License
1588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov */
1688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
1788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovpackage com.android.common.content;
1888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
1988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.content.ContentProvider;
2088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.content.ContentProviderOperation;
2188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.content.ContentProviderResult;
2288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.content.ContentValues;
2388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.content.Context;
2488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.content.OperationApplicationException;
2588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.database.sqlite.SQLiteDatabase;
2688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.database.sqlite.SQLiteOpenHelper;
2788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.database.sqlite.SQLiteTransactionListener;
2888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport android.net.Uri;
2988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
3088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovimport java.util.ArrayList;
3188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
3288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov/**
3388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov * General purpose {@link ContentProvider} base class that uses SQLiteDatabase for storage.
3488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov */
3588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikovpublic abstract class SQLiteContentProvider extends ContentProvider
3688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        implements SQLiteTransactionListener {
3788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
3888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    private static final String TAG = "SQLiteContentProvider";
3988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
4088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    private SQLiteOpenHelper mOpenHelper;
4188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    private volatile boolean mNotifyChange;
4288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected SQLiteDatabase mDb;
4388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
4488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
4588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    private static final int SLEEP_AFTER_YIELD_DELAY = 4000;
4688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
4788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    /**
4888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov     * Maximum number of operations allowed in a batch between yield points.
4988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov     */
5088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    private static final int MAX_OPERATIONS_PER_YIELD_POINT = 500;
5188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
52698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson    /**
53698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson     * @return Number of operations that can be applied at once without a yield point.
54698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson     */
55698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson    public int getMaxOperationsPerYield() {
56698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson        return MAX_OPERATIONS_PER_YIELD_POINT;
57698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson    }
58698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson
5988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    @Override
6088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public boolean onCreate() {
6188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        Context context = getContext();
6288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        mOpenHelper = getDatabaseHelper(context);
6388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        return true;
6488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
6588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
6688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected abstract SQLiteOpenHelper getDatabaseHelper(Context context);
6788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
6888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    /**
6988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov     * The equivalent of the {@link #insert} method, but invoked within a transaction.
7088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov     */
7188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected abstract Uri insertInTransaction(Uri uri, ContentValues values);
7288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
7388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    /**
7488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov     * The equivalent of the {@link #update} method, but invoked within a transaction.
7588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov     */
7688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected abstract int updateInTransaction(Uri uri, ContentValues values, String selection,
7788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            String[] selectionArgs);
7888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
7988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    /**
8088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov     * The equivalent of the {@link #delete} method, but invoked within a transaction.
8188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov     */
8288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs);
8388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
8488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected abstract void notifyChange();
8588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
8688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public SQLiteOpenHelper getDatabaseHelper() {
8788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        return mOpenHelper;
8888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
8988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
9088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    private boolean applyingBatch() {
9188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        return mApplyingBatch.get() != null && mApplyingBatch.get();
9288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
9388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
9488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    @Override
9588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
9688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        Uri result = null;
9788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        boolean applyingBatch = applyingBatch();
9888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        if (!applyingBatch) {
9988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb = mOpenHelper.getWritableDatabase();
10088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb.beginTransactionWithListener(this);
10188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            try {
10288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                result = insertInTransaction(uri, values);
10388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                if (result != null) {
10488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    mNotifyChange = true;
10588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                }
10688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mDb.setTransactionSuccessful();
10788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            } finally {
10888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mDb.endTransaction();
10988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            }
11088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
11188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            onEndTransaction();
11288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        } else {
11388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            result = insertInTransaction(uri, values);
11488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            if (result != null) {
11588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mNotifyChange = true;
11688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            }
11788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        }
11888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        return result;
11988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
12088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
12188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    @Override
12288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
12388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        int numValues = values.length;
12488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        mDb = mOpenHelper.getWritableDatabase();
12588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        mDb.beginTransactionWithListener(this);
12688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        try {
12788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            for (int i = 0; i < numValues; i++) {
12888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                Uri result = insertInTransaction(uri, values[i]);
12988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                if (result != null) {
13088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    mNotifyChange = true;
13188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                }
13288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                boolean savedNotifyChange = mNotifyChange;
13388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                SQLiteDatabase savedDb = mDb;
13488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mDb.yieldIfContendedSafely();
13588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mDb = savedDb;
13688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mNotifyChange = savedNotifyChange;
13788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            }
13888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb.setTransactionSuccessful();
13988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        } finally {
14088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb.endTransaction();
14188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        }
14288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
14388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        onEndTransaction();
14488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        return numValues;
14588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
14688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
14788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    @Override
14888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
14988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        int count = 0;
15088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        boolean applyingBatch = applyingBatch();
15188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        if (!applyingBatch) {
15288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb = mOpenHelper.getWritableDatabase();
15388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb.beginTransactionWithListener(this);
15488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            try {
15588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                count = updateInTransaction(uri, values, selection, selectionArgs);
15688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                if (count > 0) {
15788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    mNotifyChange = true;
15888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                }
15988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mDb.setTransactionSuccessful();
16088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            } finally {
16188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mDb.endTransaction();
16288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            }
16388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
16488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            onEndTransaction();
16588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        } else {
16688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            count = updateInTransaction(uri, values, selection, selectionArgs);
16788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            if (count > 0) {
16888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mNotifyChange = true;
16988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            }
17088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        }
17188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
17288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        return count;
17388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
17488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
17588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    @Override
17688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
17788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        int count = 0;
17888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        boolean applyingBatch = applyingBatch();
17988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        if (!applyingBatch) {
18088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb = mOpenHelper.getWritableDatabase();
18188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb.beginTransactionWithListener(this);
18288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            try {
18388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                count = deleteInTransaction(uri, selection, selectionArgs);
18488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                if (count > 0) {
18588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    mNotifyChange = true;
18688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                }
18788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mDb.setTransactionSuccessful();
18888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            } finally {
18988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mDb.endTransaction();
19088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            }
19188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
19288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            onEndTransaction();
19388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        } else {
19488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            count = deleteInTransaction(uri, selection, selectionArgs);
19588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            if (count > 0) {
19688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                mNotifyChange = true;
19788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            }
19888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        }
19988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        return count;
20088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
20188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
20288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    @Override
20388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
20488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            throws OperationApplicationException {
20588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        int ypCount = 0;
20688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        int opCount = 0;
20788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        mDb = mOpenHelper.getWritableDatabase();
20888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        mDb.beginTransactionWithListener(this);
20988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        try {
21088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mApplyingBatch.set(true);
21188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            final int numOperations = operations.size();
21288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            final ContentProviderResult[] results = new ContentProviderResult[numOperations];
21388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            for (int i = 0; i < numOperations; i++) {
214698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson                if (++opCount > getMaxOperationsPerYield()) {
21588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    throw new OperationApplicationException(
21688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                            "Too many content provider operations between yield points. "
21788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                                    + "The maximum number of operations per yield point is "
21888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                                    + MAX_OPERATIONS_PER_YIELD_POINT, ypCount);
21988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                }
22088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                final ContentProviderOperation operation = operations.get(i);
22188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                if (i > 0 && operation.isYieldAllowed()) {
22288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    opCount = 0;
22388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    boolean savedNotifyChange = mNotifyChange;
22488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    if (mDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY)) {
22588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                        mDb = mOpenHelper.getWritableDatabase();
22688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                        mNotifyChange = savedNotifyChange;
22788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                        ypCount++;
22888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                    }
22988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                }
23088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
23188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov                results[i] = operation.apply(this, results, i);
23288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            }
23388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb.setTransactionSuccessful();
23488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            return results;
23588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        } finally {
23688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mApplyingBatch.set(false);
23788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mDb.endTransaction();
23888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            onEndTransaction();
23988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        }
24088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
24188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
242698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson    @Override
24388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public void onBegin() {
24488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        onBeginTransaction();
24588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
24688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
247698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson    @Override
24888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public void onCommit() {
24988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        beforeTransactionCommit();
25088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
25188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
252698c55f3a36fe972b381ad216dc5de3760936858Tom Wilson    @Override
25388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    public void onRollback() {
25488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        // not used
25588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
25688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
25788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected void onBeginTransaction() {
25888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
25988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
26088287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected void beforeTransactionCommit() {
26188287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
26288287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov
26388287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    protected void onEndTransaction() {
26488287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        if (mNotifyChange) {
26588287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            mNotifyChange = false;
26688287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov            notifyChange();
26788287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov        }
26888287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov    }
26988287b58d54e7a6413992fe290832a5835fd0c0fDmitri Plotnikov}
270