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"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</method>
|
</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"
|
<method name="close"
|
||||||
return="void"
|
return="void"
|
||||||
abstract="false"
|
abstract="false"
|
||||||
@@ -49507,6 +49520,47 @@
|
|||||||
>
|
>
|
||||||
</method>
|
</method>
|
||||||
</class>
|
</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>
|
||||||
<package name="android.gesture"
|
<package name="android.gesture"
|
||||||
>
|
>
|
||||||
@@ -115090,6 +115144,46 @@
|
|||||||
<exception name="RemoteException" type="android.os.RemoteException">
|
<exception name="RemoteException" type="android.os.RemoteException">
|
||||||
</exception>
|
</exception>
|
||||||
</method>
|
</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"
|
<method name="newSetOperation"
|
||||||
return="android.content.ContentProviderOperation"
|
return="android.content.ContentProviderOperation"
|
||||||
abstract="false"
|
abstract="false"
|
||||||
@@ -115107,6 +115201,21 @@
|
|||||||
<parameter name="data" type="byte[]">
|
<parameter name="data" type="byte[]">
|
||||||
</parameter>
|
</parameter>
|
||||||
</method>
|
</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"
|
<method name="set"
|
||||||
return="void"
|
return="void"
|
||||||
abstract="false"
|
abstract="false"
|
||||||
@@ -115128,6 +115237,25 @@
|
|||||||
<exception name="RemoteException" type="android.os.RemoteException">
|
<exception name="RemoteException" type="android.os.RemoteException">
|
||||||
</exception>
|
</exception>
|
||||||
</method>
|
</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>
|
||||||
<class name="UserDictionary"
|
<class name="UserDictionary"
|
||||||
extends="java.lang.Object"
|
extends="java.lang.Object"
|
||||||
|
|||||||
@@ -175,6 +175,11 @@ public class SQLiteDatabase extends SQLiteClosable {
|
|||||||
*/
|
*/
|
||||||
private boolean mTransactionIsSuccessful;
|
private boolean mTransactionIsSuccessful;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid during the life of a transaction.
|
||||||
|
*/
|
||||||
|
private SQLiteTransactionListener mTransactionListener;
|
||||||
|
|
||||||
/** Synchronize on this when accessing the database */
|
/** Synchronize on this when accessing the database */
|
||||||
private final ReentrantLock mLock = new ReentrantLock(true);
|
private final ReentrantLock mLock = new ReentrantLock(true);
|
||||||
|
|
||||||
@@ -394,6 +399,31 @@ public class SQLiteDatabase extends SQLiteClosable {
|
|||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public void beginTransaction() {
|
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();
|
lockForced();
|
||||||
boolean ok = false;
|
boolean ok = false;
|
||||||
try {
|
try {
|
||||||
@@ -413,8 +443,17 @@ public class SQLiteDatabase extends SQLiteClosable {
|
|||||||
// This thread didn't already have the lock, so begin a database
|
// This thread didn't already have the lock, so begin a database
|
||||||
// transaction now.
|
// transaction now.
|
||||||
execSQL("BEGIN EXCLUSIVE;");
|
execSQL("BEGIN EXCLUSIVE;");
|
||||||
|
mTransactionListener = transactionListener;
|
||||||
mTransactionIsSuccessful = true;
|
mTransactionIsSuccessful = true;
|
||||||
mInnerTransactionIsSuccessful = false;
|
mInnerTransactionIsSuccessful = false;
|
||||||
|
if (transactionListener != null) {
|
||||||
|
try {
|
||||||
|
transactionListener.onBegin();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
execSQL("ROLLBACK;");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
} finally {
|
} finally {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
@@ -442,11 +481,27 @@ public class SQLiteDatabase extends SQLiteClosable {
|
|||||||
if (mLock.getHoldCount() != 1) {
|
if (mLock.getHoldCount() != 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RuntimeException savedException = null;
|
||||||
|
if (mTransactionListener != null) {
|
||||||
|
try {
|
||||||
|
if (mTransactionIsSuccessful) {
|
||||||
|
mTransactionListener.onCommit();
|
||||||
|
} else {
|
||||||
|
mTransactionListener.onRollback();
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
savedException = e;
|
||||||
|
mTransactionIsSuccessful = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (mTransactionIsSuccessful) {
|
if (mTransactionIsSuccessful) {
|
||||||
execSQL("COMMIT;");
|
execSQL("COMMIT;");
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
execSQL("ROLLBACK;");
|
execSQL("ROLLBACK;");
|
||||||
|
if (savedException != null) {
|
||||||
|
throw savedException;
|
||||||
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
if (Config.LOGD) {
|
if (Config.LOGD) {
|
||||||
Log.d(TAG, "exception during rollback, maybe the DB previously "
|
Log.d(TAG, "exception during rollback, maybe the DB previously "
|
||||||
@@ -455,6 +510,7 @@ public class SQLiteDatabase extends SQLiteClosable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
mTransactionListener = null;
|
||||||
unlockForced();
|
unlockForced();
|
||||||
if (Config.LOGV) {
|
if (Config.LOGV) {
|
||||||
Log.v(TAG, "unlocked " + Thread.currentThread()
|
Log.v(TAG, "unlocked " + Thread.currentThread()
|
||||||
@@ -561,6 +617,7 @@ public class SQLiteDatabase extends SQLiteClosable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setTransactionSuccessful();
|
setTransactionSuccessful();
|
||||||
|
SQLiteTransactionListener transactionListener = mTransactionListener;
|
||||||
endTransaction();
|
endTransaction();
|
||||||
if (checkFullyYielded) {
|
if (checkFullyYielded) {
|
||||||
if (this.isDbLockedByCurrentThread()) {
|
if (this.isDbLockedByCurrentThread()) {
|
||||||
@@ -586,7 +643,7 @@ public class SQLiteDatabase extends SQLiteClosable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
beginTransaction();
|
beginTransactionWithListener(transactionListener);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package android.database.sqlite;
|
package android.database.sqlite;
|
||||||
|
|
||||||
import android.util.Config;
|
import android.util.Config;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides debugging info about all SQLite databases running in the current process.
|
* 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.
|
* 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
|
* Controls the stack trace reporting of active cursors being
|
||||||
* finalized.
|
* 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.
|
* 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.
|
* 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.
|
* 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.net.Uri;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -71,6 +72,14 @@ public final class ContactsContract {
|
|||||||
return SyncStateContract.Helpers.get(provider, CONTENT_URI, account);
|
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
|
* @see android.provider.SyncStateContract.Helpers#set
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import android.net.Uri;
|
|||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.ContentProviderOperation;
|
import android.content.ContentProviderOperation;
|
||||||
|
import android.content.ContentUris;
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ContentProvider contract for associating data with ana data array account.
|
* The ContentProvider contract for associating data with ana data array account.
|
||||||
@@ -54,7 +56,7 @@ public class SyncStateContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class Helpers {
|
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 =
|
private static final String SELECT_BY_ACCOUNT =
|
||||||
Columns.ACCOUNT_NAME + "=? AND " + Columns.ACCOUNT_TYPE + "=?";
|
Columns.ACCOUNT_NAME + "=? AND " + Columns.ACCOUNT_TYPE + "=?";
|
||||||
|
|
||||||
@@ -101,6 +103,38 @@ public class SyncStateContract {
|
|||||||
provider.insert(uri, values);
|
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
|
* Creates and returns a ContentProviderOperation that assigns the data array as the
|
||||||
* sync state for the given account.
|
* sync state for the given account.
|
||||||
@@ -121,5 +155,22 @@ public class SyncStateContract {
|
|||||||
.withValues(values)
|
.withValues(values)
|
||||||
.build();
|
.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);
|
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) {
|
public void onAccountsChanged(SQLiteDatabase db, Account[] accounts) {
|
||||||
Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
|
Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user