blob: f503e6f3521db61c084c4cfa0dc9dcc85976e168 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content;
import com.android.internal.util.ArrayUtils;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
/**
* Extends the schema of a ContentProvider to include the _sync_state table
* and implements query/insert/update/delete to access that table using the
* authority "syncstate". This can be used to store the sync state for a
* set of accounts.
*
* @hide
*/
public class SyncStateContentProviderHelper {
final SQLiteOpenHelper mOpenHelper;
private static final String SYNC_STATE_AUTHORITY = "syncstate";
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int STATE = 0;
private static final Uri CONTENT_URI =
Uri.parse("content://" + SYNC_STATE_AUTHORITY + "/state");
private static final String ACCOUNT_WHERE = "_sync_account = ?";
private final Provider mInternalProviderInterface;
private static final String SYNC_STATE_TABLE = "_sync_state";
private static long DB_VERSION = 2;
private static final String[] ACCOUNT_PROJECTION = new String[]{"_sync_account"};
static {
sURIMatcher.addURI(SYNC_STATE_AUTHORITY, "state", STATE);
}
public SyncStateContentProviderHelper(SQLiteOpenHelper openHelper) {
mOpenHelper = openHelper;
mInternalProviderInterface = new Provider();
}
public ContentProvider asContentProvider() {
return mInternalProviderInterface;
}
public void createDatabase(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS _sync_state");
db.execSQL("CREATE TABLE _sync_state (" +
"_id INTEGER PRIMARY KEY," +
"_sync_account TEXT," +
"data TEXT," +
"UNIQUE(_sync_account)" +
");");
db.execSQL("DROP TABLE IF EXISTS _sync_state_metadata");
db.execSQL("CREATE TABLE _sync_state_metadata (" +
"version INTEGER" +
");");
ContentValues values = new ContentValues();
values.put("version", DB_VERSION);
db.insert("_sync_state_metadata", "version", values);
}
protected void onDatabaseOpened(SQLiteDatabase db) {
long version = DatabaseUtils.longForQuery(db,
"select version from _sync_state_metadata", null);
if (version != DB_VERSION) {
createDatabase(db);
}
}
class Provider extends ContentProvider {
public boolean onCreate() {
throw new UnsupportedOperationException("not implemented");
}
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
int match = sURIMatcher.match(url);
switch (match) {
case STATE:
return db.query(SYNC_STATE_TABLE, projection, selection, selectionArgs,
null, null, sortOrder);
default:
throw new UnsupportedOperationException("Cannot query URL: " + url);
}
}
public String getType(Uri uri) {
throw new UnsupportedOperationException("not implemented");
}
public Uri insert(Uri url, ContentValues values) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int match = sURIMatcher.match(url);
switch (match) {
case STATE: {
long id = db.insert(SYNC_STATE_TABLE, "feed", values);
return CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
}
default:
throw new UnsupportedOperationException("Cannot insert into URL: " + url);
}
}
public int delete(Uri url, String userWhere, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
switch (sURIMatcher.match(url)) {
case STATE:
return db.delete(SYNC_STATE_TABLE, userWhere, whereArgs);
default:
throw new IllegalArgumentException("Unknown URL " + url);
}
}
public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
switch (sURIMatcher.match(url)) {
case STATE:
return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
default:
throw new UnsupportedOperationException("Cannot update URL: " + url);
}
}
}
/**
* Check if the url matches content that this ContentProvider manages.
* @param url the Uri to check
* @return true if this ContentProvider can handle that Uri.
*/
public boolean matches(Uri url) {
return (SYNC_STATE_AUTHORITY.equals(url.getAuthority()));
}
/**
* Replaces the contents of the _sync_state table in the destination ContentProvider
* with the row that matches account, if any, in the source ContentProvider.
* <p>
* The ContentProviders must expose the _sync_state table as URI content://syncstate/state.
* @param dbSrc the database to read from
* @param dbDest the database to write to
* @param account the account of the row that should be copied over.
*/
public void copySyncState(SQLiteDatabase dbSrc, SQLiteDatabase dbDest,
String account) {
final String[] whereArgs = new String[]{account};
Cursor c = dbSrc.query(SYNC_STATE_TABLE, new String[]{"_sync_account", "data"},
ACCOUNT_WHERE, whereArgs, null, null, null);
try {
if (c.moveToNext()) {
ContentValues values = new ContentValues();
values.put("_sync_account", c.getString(0));
values.put("data", c.getBlob(1));
dbDest.replace(SYNC_STATE_TABLE, "_sync_account", values);
}
} finally {
c.close();
}
}
public void onAccountsChanged(String[] accounts) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
try {
while (c.moveToNext()) {
final String account = c.getString(0);
if (!ArrayUtils.contains(accounts, account)) {
db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account});
}
}
} finally {
c.close();
}
}
public void discardSyncData(SQLiteDatabase db, String account) {
if (account != null) {
db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account});
} else {
db.delete(SYNC_STATE_TABLE, null, null);
}
}
/**
* Retrieves the SyncData bytes for the given account. The byte array returned may be null.
*/
public byte[] readSyncDataBytes(SQLiteDatabase db, String account) {
Cursor c = db.query(SYNC_STATE_TABLE, null, ACCOUNT_WHERE,
new String[]{account}, null, null, null);
try {
if (c.moveToFirst()) {
return c.getBlob(c.getColumnIndexOrThrow("data"));
}
} finally {
c.close();
}
return null;
}
/**
* Sets the SyncData bytes for the given account. The bytes array may be null.
*/
public void writeSyncDataBytes(SQLiteDatabase db, String account, byte[] data) {
ContentValues values = new ContentValues();
values.put("data", data);
db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE, new String[]{account});
}
}