diff --git a/api/current.xml b/api/current.xml index b17466f1a2907..88e5025b1b6e5 100644 --- a/api/current.xml +++ b/api/current.xml @@ -17434,17 +17434,6 @@ visibility="public" > - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Type: STRING

+ */ + public static final String ACCOUNT_NAME = "account_name"; + + /** + * A reference to the type of the account to which this data belongs + *

Type: STRING

+ */ + public static final String ACCOUNT_TYPE = "account_type"; + + /** + * The sync data associated with this account. + *

Type: NONE

+ */ + public static final String DATA = "data"; + } + + public static class Constants implements Columns { + public static final String CONTENT_DIRECTORY = "syncstate"; + } + + public static final class Helpers { + private static final String[] DATA_PROJECTION = new String[]{Columns.DATA}; + private static final String SELECT_BY_ACCOUNT = + Columns.ACCOUNT_NAME + "=? AND " + Columns.ACCOUNT_TYPE + "=?"; + + /** + * Get the sync state that is associated with the account or null. + * @param provider the {@link ContentProviderClient} that is to be used to communicate + * with the {@link android.content.ContentProvider} that contains the sync state. + * @param uri the uri of the sync state + * @param account the {@link Account} whose sync state should be returned + * @return the sync state or null if there is no sync state associated with the account + * @throws RemoteException if there is a failure communicating with the remote + * {@link android.content.ContentProvider} + */ + public static byte[] get(ContentProviderClient provider, Uri uri, + Account account) throws RemoteException { + Cursor c = provider.query(uri, DATA_PROJECTION, SELECT_BY_ACCOUNT, + new String[]{account.mName, account.mType}, null); + try { + if (c.moveToNext()) { + return c.getBlob(c.getColumnIndexOrThrow(Columns.DATA)); + } + } finally { + c.close(); + } + return null; + } + + /** + * Assigns the data array as the sync state for the given account. + * @param provider the {@link ContentProviderClient} that is to be used to communicate + * with the {@link android.content.ContentProvider} that contains the sync state. + * @param uri the uri of the sync state + * @param account the {@link Account} whose sync state should be set + * @param data the byte[] that contains the sync state + * @throws RemoteException if there is a failure communicating with the remote + * {@link android.content.ContentProvider} + */ + public static void set(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.mName); + values.put(Columns.ACCOUNT_TYPE, account.mType); + provider.insert(uri, values); + } + + /** + * Creates and returns a ContentProviderOperation that assigns the data array as the + * sync state for the given account. + * @param uri the uri of the sync state + * @param account the {@link Account} whose sync state should be 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 newSetOperation(Uri uri, + Account account, byte[] data) { + ContentValues values = new ContentValues(); + values.put(Columns.DATA, data); + return ContentProviderOperation + .newInsert(uri) + .withValue(Columns.ACCOUNT_NAME, account.mName) + .withValue(Columns.ACCOUNT_TYPE, account.mType) + .withValues(values) + .build(); + } + } +} diff --git a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java new file mode 100644 index 0000000000000..d2931a4b65bf5 --- /dev/null +++ b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java @@ -0,0 +1,115 @@ +/* + * 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 com.android.internal.content; + +import com.android.internal.util.ArrayUtils; + +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDatabase; +import android.accounts.Account; +import android.content.ContentValues; +import android.provider.SyncStateContract; + +/** + * 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 { + private static final String SELECT_BY_ACCOUNT = + SyncStateContract.Columns.ACCOUNT_NAME + "=? AND " + + SyncStateContract.Columns.ACCOUNT_TYPE + "=?"; + + private static final String SYNC_STATE_TABLE = "_sync_state"; + private static final String SYNC_STATE_META_TABLE = "_sync_state_metadata"; + private static final String SYNC_STATE_META_VERSION_COLUMN = "version"; + + private static long DB_VERSION = 1; + + private static final String[] ACCOUNT_PROJECTION = + new String[]{SyncStateContract.Columns.ACCOUNT_NAME, + SyncStateContract.Columns.ACCOUNT_TYPE}; + + public static final String PATH = "syncstate"; + + public void createDatabase(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_TABLE); + db.execSQL("CREATE TABLE " + SYNC_STATE_TABLE + " (" + + SyncStateContract.Columns._ID + " INTEGER PRIMARY KEY," + + SyncStateContract.Columns.ACCOUNT_NAME + " TEXT NOT NULL," + + SyncStateContract.Columns.ACCOUNT_TYPE + " TEXT NOT NULL," + + SyncStateContract.Columns.DATA + " TEXT," + + "UNIQUE(" + SyncStateContract.Columns.ACCOUNT_NAME + ", " + + SyncStateContract.Columns.ACCOUNT_TYPE + "));"); + + db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_META_TABLE); + db.execSQL("CREATE TABLE " + SYNC_STATE_META_TABLE + " (" + + SYNC_STATE_META_VERSION_COLUMN + " INTEGER);"); + ContentValues values = new ContentValues(); + values.put(SYNC_STATE_META_VERSION_COLUMN, DB_VERSION); + db.insert(SYNC_STATE_META_TABLE, SYNC_STATE_META_VERSION_COLUMN, values); + } + + public void onDatabaseOpened(SQLiteDatabase db) { + long version = DatabaseUtils.longForQuery(db, + "SELECT " + SYNC_STATE_META_VERSION_COLUMN + " FROM " + SYNC_STATE_META_TABLE, + null); + if (version != DB_VERSION) { + createDatabase(db); + } + } + + public Cursor query(SQLiteDatabase db, String[] projection, + String selection, String[] selectionArgs, String sortOrder) { + return db.query(SYNC_STATE_TABLE, projection, selection, selectionArgs, + null, null, sortOrder); + } + + public long insert(SQLiteDatabase db, ContentValues values) { + return db.replace(SYNC_STATE_TABLE, SyncStateContract.Columns.ACCOUNT_NAME, values); + } + + public int delete(SQLiteDatabase db, String userWhere, String[] whereArgs) { + return db.delete(SYNC_STATE_TABLE, userWhere, whereArgs); + } + + public int update(SQLiteDatabase db, ContentValues values, + String selection, String[] selectionArgs) { + return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs); + } + + public void onAccountsChanged(SQLiteDatabase db, Account[] accounts) { + Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null); + try { + while (c.moveToNext()) { + final String accountName = c.getString(0); + final String accountType = c.getString(1); + Account account = new Account(accountName, accountType); + if (!ArrayUtils.contains(accounts, account)) { + db.delete(SYNC_STATE_TABLE, SELECT_BY_ACCOUNT, + new String[]{accountName, accountType}); + } + } + } finally { + c.close(); + } + } +} \ No newline at end of file