1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.content;
18
19import com.android.internal.util.ArrayUtils;
20
21import android.database.Cursor;
22import android.database.DatabaseUtils;
23import android.database.sqlite.SQLiteDatabase;
24import android.accounts.Account;
25import android.content.ContentValues;
26import android.provider.SyncStateContract;
27
28/**
29 * Extends the schema of a ContentProvider to include the _sync_state table
30 * and implements query/insert/update/delete to access that table using the
31 * authority "syncstate". This can be used to store the sync state for a
32 * set of accounts.
33 *
34 * @hide
35 */
36public class SyncStateContentProviderHelper {
37    private static final String SELECT_BY_ACCOUNT =
38            SyncStateContract.Columns.ACCOUNT_NAME + "=? AND "
39                    + SyncStateContract.Columns.ACCOUNT_TYPE + "=?";
40
41    private static final String SYNC_STATE_TABLE = "_sync_state";
42    private static final String SYNC_STATE_META_TABLE = "_sync_state_metadata";
43    private static final String SYNC_STATE_META_VERSION_COLUMN = "version";
44
45    private static long DB_VERSION = 1;
46
47    private static final String[] ACCOUNT_PROJECTION =
48            new String[]{SyncStateContract.Columns.ACCOUNT_NAME,
49                    SyncStateContract.Columns.ACCOUNT_TYPE};
50
51    public static final String PATH = "syncstate";
52
53    private static final String QUERY_COUNT_SYNC_STATE_ROWS =
54            "SELECT count(*)"
55                    + " FROM " + SYNC_STATE_TABLE
56                    + " WHERE " + SyncStateContract.Columns._ID + "=?";
57
58    public void createDatabase(SQLiteDatabase db) {
59        db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_TABLE);
60        db.execSQL("CREATE TABLE " + SYNC_STATE_TABLE + " ("
61                + SyncStateContract.Columns._ID + " INTEGER PRIMARY KEY,"
62                + SyncStateContract.Columns.ACCOUNT_NAME + " TEXT NOT NULL,"
63                + SyncStateContract.Columns.ACCOUNT_TYPE + " TEXT NOT NULL,"
64                + SyncStateContract.Columns.DATA + " TEXT,"
65                + "UNIQUE(" + SyncStateContract.Columns.ACCOUNT_NAME + ", "
66                + SyncStateContract.Columns.ACCOUNT_TYPE + "));");
67
68        db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_META_TABLE);
69        db.execSQL("CREATE TABLE " + SYNC_STATE_META_TABLE + " ("
70                + SYNC_STATE_META_VERSION_COLUMN + " INTEGER);");
71        ContentValues values = new ContentValues();
72        values.put(SYNC_STATE_META_VERSION_COLUMN, DB_VERSION);
73        db.insert(SYNC_STATE_META_TABLE, SYNC_STATE_META_VERSION_COLUMN, values);
74    }
75
76    public void onDatabaseOpened(SQLiteDatabase db) {
77        long version = DatabaseUtils.longForQuery(db,
78                "SELECT " + SYNC_STATE_META_VERSION_COLUMN + " FROM " + SYNC_STATE_META_TABLE,
79                null);
80        if (version != DB_VERSION) {
81            createDatabase(db);
82        }
83    }
84
85    public Cursor query(SQLiteDatabase db, String[] projection,
86            String selection, String[] selectionArgs, String sortOrder) {
87        return db.query(SYNC_STATE_TABLE, projection, selection, selectionArgs,
88                null, null, sortOrder);
89    }
90
91    public long insert(SQLiteDatabase db, ContentValues values) {
92        return db.replace(SYNC_STATE_TABLE, SyncStateContract.Columns.ACCOUNT_NAME, values);
93    }
94
95    public int delete(SQLiteDatabase db, String userWhere, String[] whereArgs) {
96        return db.delete(SYNC_STATE_TABLE, userWhere, whereArgs);
97    }
98
99    public int update(SQLiteDatabase db, ContentValues values,
100            String selection, String[] selectionArgs) {
101        return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
102    }
103
104    public int update(SQLiteDatabase db, long rowId, Object data) {
105        if (DatabaseUtils.longForQuery(db, QUERY_COUNT_SYNC_STATE_ROWS,
106                new String[]{Long.toString(rowId)}) < 1) {
107            return 0;
108        }
109        db.execSQL("UPDATE " + SYNC_STATE_TABLE
110                + " SET " + SyncStateContract.Columns.DATA + "=?"
111                + " WHERE " + SyncStateContract.Columns._ID + "=" + rowId,
112                new Object[]{data});
113        // assume a row was modified since we know it exists
114        return 1;
115    }
116
117    public void onAccountsChanged(SQLiteDatabase db, Account[] accounts) {
118        Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
119        try {
120            while (c.moveToNext()) {
121                final String accountName = c.getString(0);
122                final String accountType = c.getString(1);
123                Account account = new Account(accountName, accountType);
124                if (!ArrayUtils.contains(accounts, account)) {
125                    db.delete(SYNC_STATE_TABLE, SELECT_BY_ACCOUNT,
126                            new String[]{accountName, accountType});
127                }
128            }
129        } finally {
130            c.close();
131        }
132    }
133}