Merge change 24012 into eclair

* changes:
  add a transaction monitor
This commit is contained in:
Android (Google) Code Review
2009-09-08 17:26:25 -07:00
7 changed files with 284 additions and 6 deletions

View File

@@ -47876,6 +47876,19 @@
visibility="public"
>
</method>
<method name="beginTransactionWithListener"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="transactionListener" type="android.database.sqlite.SQLiteTransactionListener">
</parameter>
</method>
<method name="close"
return="void"
abstract="false"
@@ -49507,6 +49520,47 @@
>
</method>
</class>
<interface name="SQLiteTransactionListener"
abstract="true"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<method name="onBegin"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="onCommit"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="onRollback"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
</interface>
</package>
<package name="android.gesture"
>
@@ -115090,6 +115144,46 @@
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
<method name="getWithUri"
return="android.util.Pair&lt;android.net.Uri, byte[]&gt;"
abstract="false"
native="false"
synchronized="false"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="provider" type="android.content.ContentProviderClient">
</parameter>
<parameter name="uri" type="android.net.Uri">
</parameter>
<parameter name="account" type="android.accounts.Account">
</parameter>
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
<method name="insert"
return="android.net.Uri"
abstract="false"
native="false"
synchronized="false"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="provider" type="android.content.ContentProviderClient">
</parameter>
<parameter name="uri" type="android.net.Uri">
</parameter>
<parameter name="account" type="android.accounts.Account">
</parameter>
<parameter name="data" type="byte[]">
</parameter>
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
<method name="newSetOperation"
return="android.content.ContentProviderOperation"
abstract="false"
@@ -115107,6 +115201,21 @@
<parameter name="data" type="byte[]">
</parameter>
</method>
<method name="newUpdateOperation"
return="android.content.ContentProviderOperation"
abstract="false"
native="false"
synchronized="false"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="uri" type="android.net.Uri">
</parameter>
<parameter name="data" type="byte[]">
</parameter>
</method>
<method name="set"
return="void"
abstract="false"
@@ -115128,6 +115237,25 @@
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
<method name="update"
return="void"
abstract="false"
native="false"
synchronized="false"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="provider" type="android.content.ContentProviderClient">
</parameter>
<parameter name="uri" type="android.net.Uri">
</parameter>
<parameter name="data" type="byte[]">
</parameter>
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
</class>
<class name="UserDictionary"
extends="java.lang.Object"

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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;
@@ -71,6 +72,14 @@ public final class ContactsContract {
return SyncStateContract.Helpers.get(provider, CONTENT_URI, account);
}
/**
* @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
*/

View File

@@ -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();
}
}
}

View File

@@ -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 {