Merge change 24012 into eclair
* changes: add a transaction monitor
This commit is contained in:
128
api/current.xml
128
api/current.xml
@@ -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<android.net.Uri, byte[]>"
|
||||
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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user