summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAndroid (Google) Code Review <android-gerrit@google.com>2009-09-08 17:26:25 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2009-09-08 17:26:25 -0700
commitb0dccf73ddb5a695fe24f1435411feea5876609f (patch)
tree8ed49ad30b1188bd6a0d9b1d31d4f23dabed7602 /core
parent046263ce352912990e492eb5cfd833c21109cf9f (diff)
parentc4516a7b62de525e3d6d5e76851bdfaf12c11f05 (diff)
downloadframeworks_base-b0dccf73ddb5a695fe24f1435411feea5876609f.zip
frameworks_base-b0dccf73ddb5a695fe24f1435411feea5876609f.tar.gz
frameworks_base-b0dccf73ddb5a695fe24f1435411feea5876609f.tar.bz2
Merge change 24012 into eclair
* changes: add a transaction monitor
Diffstat (limited to 'core')
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java59
-rw-r--r--core/java/android/database/sqlite/SQLiteDebug.java13
-rw-r--r--core/java/android/database/sqlite/SQLiteTransactionListener.java21
-rw-r--r--core/java/android/provider/ContactsContract.java9
-rw-r--r--core/java/android/provider/SyncStateContract.java53
-rw-r--r--core/java/com/android/internal/content/SyncStateContentProviderHelper.java7
6 files changed, 156 insertions, 6 deletions
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 57bf3f7..4f31ef0 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -175,6 +175,11 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
private boolean mTransactionIsSuccessful;
+ /**
+ * Valid during the life of a transaction.
+ */
+ private SQLiteTransactionListener mTransactionListener;
+
/** Synchronize on this when accessing the database */
private final ReentrantLock mLock = new ReentrantLock(true);
@@ -394,6 +399,31 @@ public class SQLiteDatabase extends SQLiteClosable {
* </pre>
*/
public void beginTransaction() {
+ beginTransactionWithListener(null /* transactionStatusCallback */);
+ }
+
+ /**
+ * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
+ * the work done in that transaction and all of the nested transactions will be committed or
+ * rolled back. The changes will be rolled back if any transaction is ended without being
+ * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
+ *
+ * <p>Here is the standard idiom for transactions:
+ *
+ * <pre>
+ * db.beginTransactionWithListener(listener);
+ * try {
+ * ...
+ * db.setTransactionSuccessful();
+ * } finally {
+ * db.endTransaction();
+ * }
+ * </pre>
+ * @param transactionListener listener that should be notified when the transaction begins,
+ * commits, or is rolled back, either explicitly or by a call to
+ * {@link #yieldIfContendedSafely}.
+ */
+ public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
lockForced();
boolean ok = false;
try {
@@ -413,8 +443,17 @@ public class SQLiteDatabase extends SQLiteClosable {
// This thread didn't already have the lock, so begin a database
// transaction now.
execSQL("BEGIN EXCLUSIVE;");
+ mTransactionListener = transactionListener;
mTransactionIsSuccessful = true;
mInnerTransactionIsSuccessful = false;
+ if (transactionListener != null) {
+ try {
+ transactionListener.onBegin();
+ } catch (RuntimeException e) {
+ execSQL("ROLLBACK;");
+ throw e;
+ }
+ }
ok = true;
} finally {
if (!ok) {
@@ -442,11 +481,27 @@ public class SQLiteDatabase extends SQLiteClosable {
if (mLock.getHoldCount() != 1) {
return;
}
+ RuntimeException savedException = null;
+ if (mTransactionListener != null) {
+ try {
+ if (mTransactionIsSuccessful) {
+ mTransactionListener.onCommit();
+ } else {
+ mTransactionListener.onRollback();
+ }
+ } catch (RuntimeException e) {
+ savedException = e;
+ mTransactionIsSuccessful = false;
+ }
+ }
if (mTransactionIsSuccessful) {
execSQL("COMMIT;");
} else {
try {
execSQL("ROLLBACK;");
+ if (savedException != null) {
+ throw savedException;
+ }
} catch (SQLException e) {
if (Config.LOGD) {
Log.d(TAG, "exception during rollback, maybe the DB previously "
@@ -455,6 +510,7 @@ public class SQLiteDatabase extends SQLiteClosable {
}
}
} finally {
+ mTransactionListener = null;
unlockForced();
if (Config.LOGV) {
Log.v(TAG, "unlocked " + Thread.currentThread()
@@ -561,6 +617,7 @@ public class SQLiteDatabase extends SQLiteClosable {
return false;
}
setTransactionSuccessful();
+ SQLiteTransactionListener transactionListener = mTransactionListener;
endTransaction();
if (checkFullyYielded) {
if (this.isDbLockedByCurrentThread()) {
@@ -586,7 +643,7 @@ public class SQLiteDatabase extends SQLiteClosable {
}
}
}
- beginTransaction();
+ beginTransactionWithListener(transactionListener);
return true;
}
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index d04afb0..84d8879 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -17,6 +17,7 @@
package android.database.sqlite;
import android.util.Config;
+import android.util.Log;
/**
* Provides debugging info about all SQLite databases running in the current process.
@@ -27,23 +28,27 @@ public final class SQLiteDebug {
/**
* Controls the printing of SQL statements as they are executed.
*/
- public static final boolean DEBUG_SQL_STATEMENTS = Config.LOGV;
+ public static final boolean DEBUG_SQL_STATEMENTS =
+ Log.isLoggable("SQLiteStatements", Log.VERBOSE);
/**
* Controls the stack trace reporting of active cursors being
* finalized.
*/
- public static final boolean DEBUG_ACTIVE_CURSOR_FINALIZATION = Config.LOGV;
+ public static final boolean DEBUG_ACTIVE_CURSOR_FINALIZATION =
+ Log.isLoggable("SQLiteCursorClosing", Log.VERBOSE);
/**
* Controls the tracking of time spent holding the database lock.
*/
- public static final boolean DEBUG_LOCK_TIME_TRACKING = false;
+ public static final boolean DEBUG_LOCK_TIME_TRACKING =
+ Log.isLoggable("SQLiteLockTime", Log.VERBOSE);
/**
* Controls the printing of stack traces when tracking the time spent holding the database lock.
*/
- public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE = false;
+ public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE =
+ Log.isLoggable("SQLiteLockStackTrace", Log.VERBOSE);
/**
* Contains statistics about the active pagers in the current process.
diff --git a/core/java/android/database/sqlite/SQLiteTransactionListener.java b/core/java/android/database/sqlite/SQLiteTransactionListener.java
new file mode 100644
index 0000000..e97ece8
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteTransactionListener.java
@@ -0,0 +1,21 @@
+package android.database.sqlite;
+
+/**
+ * A listener for transaction events.
+ */
+public interface SQLiteTransactionListener {
+ /**
+ * Called immediately after the transaction begins.
+ */
+ void onBegin();
+
+ /**
+ * Called immediately before commiting the transaction.
+ */
+ void onCommit();
+
+ /**
+ * Called if the transaction is about to be rolled back.
+ */
+ void onRollback();
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index a318f08..31d43ee 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -29,6 +29,7 @@ import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.RemoteException;
import android.text.TextUtils;
+import android.util.Pair;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -72,6 +73,14 @@ public final class ContactsContract {
}
/**
+ * @see android.provider.SyncStateContract.Helpers#get
+ */
+ public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Account account)
+ throws RemoteException {
+ return SyncStateContract.Helpers.getWithUri(provider, CONTENT_URI, account);
+ }
+
+ /**
* @see android.provider.SyncStateContract.Helpers#set
*/
public static void set(ContentProviderClient provider, Account account, byte[] data)
diff --git a/core/java/android/provider/SyncStateContract.java b/core/java/android/provider/SyncStateContract.java
index 5c93af0..e8177ca 100644
--- a/core/java/android/provider/SyncStateContract.java
+++ b/core/java/android/provider/SyncStateContract.java
@@ -20,9 +20,11 @@ import android.net.Uri;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.ContentProviderOperation;
+import android.content.ContentUris;
import android.accounts.Account;
import android.database.Cursor;
import android.os.RemoteException;
+import android.util.Pair;
/**
* The ContentProvider contract for associating data with ana data array account.
@@ -54,7 +56,7 @@ public class SyncStateContract {
}
public static final class Helpers {
- private static final String[] DATA_PROJECTION = new String[]{Columns.DATA};
+ private static final String[] DATA_PROJECTION = new String[]{Columns.DATA, Columns._ID};
private static final String SELECT_BY_ACCOUNT =
Columns.ACCOUNT_NAME + "=? AND " + Columns.ACCOUNT_TYPE + "=?";
@@ -101,6 +103,38 @@ public class SyncStateContract {
provider.insert(uri, values);
}
+ public static Uri insert(ContentProviderClient provider, Uri uri,
+ Account account, byte[] data) throws RemoteException {
+ ContentValues values = new ContentValues();
+ values.put(Columns.DATA, data);
+ values.put(Columns.ACCOUNT_NAME, account.name);
+ values.put(Columns.ACCOUNT_TYPE, account.type);
+ return provider.insert(uri, values);
+ }
+
+ public static void update(ContentProviderClient provider, Uri uri, byte[] data)
+ throws RemoteException {
+ ContentValues values = new ContentValues();
+ values.put(Columns.DATA, data);
+ provider.update(uri, values, null, null);
+ }
+
+ public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Uri uri,
+ Account account) throws RemoteException {
+ Cursor c = provider.query(uri, DATA_PROJECTION, SELECT_BY_ACCOUNT,
+ new String[]{account.name, account.type}, null);
+ try {
+ if (c.moveToNext()) {
+ long rowId = c.getLong(1);
+ byte[] blob = c.getBlob(c.getColumnIndexOrThrow(Columns.DATA));
+ return Pair.create(ContentUris.withAppendedId(uri, rowId), blob);
+ }
+ } finally {
+ c.close();
+ }
+ return null;
+ }
+
/**
* Creates and returns a ContentProviderOperation that assigns the data array as the
* sync state for the given account.
@@ -121,5 +155,22 @@ public class SyncStateContract {
.withValues(values)
.build();
}
+
+ /**
+ * Creates and returns a ContentProviderOperation that assigns the data array as the
+ * sync state for the given account.
+ * @param uri the uri of the specific sync state to set
+ * @param data the byte[] that contains the sync state
+ * @return the new ContentProviderOperation that assigns the data array as the
+ * account's sync state
+ */
+ public static ContentProviderOperation newUpdateOperation(Uri uri, byte[] data) {
+ ContentValues values = new ContentValues();
+ values.put(Columns.DATA, data);
+ return ContentProviderOperation
+ .newUpdate(uri)
+ .withValues(values)
+ .build();
+ }
}
}
diff --git a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
index d2931a4..cd6a9a1 100644
--- a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
+++ b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
@@ -96,6 +96,13 @@ public class SyncStateContentProviderHelper {
return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
}
+ public void update(SQLiteDatabase db, long rowId, Object data) {
+ db.execSQL("UPDATE " + SYNC_STATE_TABLE
+ + " SET " + SyncStateContract.Columns.DATA + "=?"
+ + " WHERE " + SyncStateContract.Columns._ID + "=" + rowId,
+ new Object[]{data});
+ }
+
public void onAccountsChanged(SQLiteDatabase db, Account[] accounts) {
Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
try {