Home | History | Annotate | Download | only in contacts
      1 /*
      2  * Copyright (C) 2011 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.contacts;
     18 
     19 import com.google.android.collect.Lists;
     20 import com.google.android.collect.Maps;
     21 
     22 import android.database.sqlite.SQLiteDatabase;
     23 import android.database.sqlite.SQLiteTransactionListener;
     24 
     25 import java.util.List;
     26 import java.util.Map;
     27 
     28 /**
     29  * A transaction for interacting with a Contacts provider.  This is used to pass state around
     30  * throughout the operations comprising the transaction, including which databases the overall
     31  * transaction is involved in, and whether the operation being performed is a batch operation.
     32  */
     33 public class ContactsTransaction {
     34 
     35     /**
     36      * Whether this transaction is encompassing a batch of operations.  If we're in batch mode,
     37      * transactional operations from non-batch callers are ignored.
     38      */
     39     private final boolean mBatch;
     40 
     41     /**
     42      * The list of databases that have been enlisted in this transaction.
     43      */
     44     private List<SQLiteDatabase> mDatabasesForTransaction;
     45 
     46     /**
     47      * The mapping of tags to databases involved in this transaction.
     48      */
     49     private Map<String, SQLiteDatabase> mDatabaseTagMap;
     50 
     51     /**
     52      * Whether any actual changes have been made successfully in this transaction.
     53      */
     54     private boolean mIsDirty;
     55 
     56     /**
     57      * Whether a yield operation failed with an exception.  If this occurred, we may not have a
     58      * lock on one of the databases that we started the transaction with (the yield code cleans
     59      * that up itself), so we should do an extra check before ending transactions.
     60      */
     61     private boolean mYieldFailed;
     62 
     63     /**
     64      * Creates a new transaction object, optionally marked as a batch transaction.
     65      * @param batch Whether the transaction is in batch mode.
     66      */
     67     public ContactsTransaction(boolean batch) {
     68         mBatch = batch;
     69         mDatabasesForTransaction = Lists.newArrayList();
     70         mDatabaseTagMap = Maps.newHashMap();
     71         mIsDirty = false;
     72     }
     73 
     74     public boolean isBatch() {
     75         return mBatch;
     76     }
     77 
     78     public boolean isDirty() {
     79         return mIsDirty;
     80     }
     81 
     82     public void markDirty() {
     83         mIsDirty = true;
     84     }
     85 
     86     public void markYieldFailed() {
     87         mYieldFailed = true;
     88     }
     89 
     90     /**
     91      * If the given database has not already been enlisted in this transaction, adds it to our
     92      * list of affected databases and starts a transaction on it.  If we already have the given
     93      * database in this transaction, this is a no-op.
     94      * @param db The database to start a transaction on, if necessary.
     95      * @param tag A constant that can be used to retrieve the DB instance in this transaction.
     96      * @param listener A transaction listener to attach to this transaction.  May be null.
     97      */
     98     public void startTransactionForDb(SQLiteDatabase db, String tag,
     99             SQLiteTransactionListener listener) {
    100         if (!hasDbInTransaction(tag)) {
    101             mDatabasesForTransaction.add(db);
    102             mDatabaseTagMap.put(tag, db);
    103             if (listener != null) {
    104                 db.beginTransactionWithListener(listener);
    105             } else {
    106                 db.beginTransaction();
    107             }
    108         }
    109     }
    110 
    111     /**
    112      * Returns whether DB corresponding to the given tag is currently enlisted in this transaction.
    113      */
    114     public boolean hasDbInTransaction(String tag) {
    115         return mDatabaseTagMap.containsKey(tag);
    116     }
    117 
    118     /**
    119      * Retrieves the database enlisted in the transaction corresponding to the given tag.
    120      * @param tag The tag of the database to look up.
    121      * @return The database corresponding to the tag, or null if no database with that tag has been
    122      *     enlisted in this transaction.
    123      */
    124     public SQLiteDatabase getDbForTag(String tag) {
    125         return mDatabaseTagMap.get(tag);
    126     }
    127 
    128     /**
    129      * Removes the database corresponding to the given tag from this transaction.  It is now the
    130      * caller's responsibility to do whatever needs to happen with this database - it is no longer
    131      * a part of this transaction.
    132      * @param tag The tag of the database to remove.
    133      * @return The database corresponding to the tag, or null if no database with that tag has been
    134      *     enlisted in this transaction.
    135      */
    136     public SQLiteDatabase removeDbForTag(String tag) {
    137         SQLiteDatabase db = mDatabaseTagMap.get(tag);
    138         mDatabaseTagMap.remove(tag);
    139         mDatabasesForTransaction.remove(db);
    140         return db;
    141     }
    142 
    143     /**
    144      * Marks all active DB transactions as successful.
    145      * @param callerIsBatch Whether this is being performed in the context of a batch operation.
    146      *     If it is not, and the transaction is marked as batch, this call is a no-op.
    147      */
    148     public void markSuccessful(boolean callerIsBatch) {
    149         if (!mBatch || callerIsBatch) {
    150             for (SQLiteDatabase db : mDatabasesForTransaction) {
    151                 db.setTransactionSuccessful();
    152             }
    153         }
    154     }
    155 
    156     /**
    157      * Completes the transaction, ending the DB transactions for all associated databases.
    158      * @param callerIsBatch Whether this is being performed in the context of a batch operation.
    159      *     If it is not, and the transaction is marked as batch, this call is a no-op.
    160      */
    161     public void finish(boolean callerIsBatch) {
    162         if (!mBatch || callerIsBatch) {
    163             for (SQLiteDatabase db : mDatabasesForTransaction) {
    164                 // If an exception was thrown while yielding, it's possible that we no longer have
    165                 // a lock on this database, so we need to check before attempting to end its
    166                 // transaction.  Otherwise, we should always expect to be in a transaction (and will
    167                 // throw an exception if this is not the case).
    168                 if (mYieldFailed && !db.isDbLockedByCurrentThread()) {
    169                     // We no longer hold the lock, so don't do anything with this database.
    170                     continue;
    171                 }
    172                 db.endTransaction();
    173             }
    174             mDatabasesForTransaction.clear();
    175             mDatabaseTagMap.clear();
    176             mIsDirty = false;
    177         }
    178     }
    179 }
    180