blob: f503e6f3521db61c084c4cfa0dc9dcc85976e168 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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 android.content;
18
19import com.android.internal.util.ArrayUtils;
20
21import android.database.Cursor;
22import android.database.DatabaseUtils;
23import android.database.sqlite.SQLiteDatabase;
24import android.database.sqlite.SQLiteOpenHelper;
25import android.net.Uri;
26
27/**
28 * Extends the schema of a ContentProvider to include the _sync_state table
29 * and implements query/insert/update/delete to access that table using the
30 * authority "syncstate". This can be used to store the sync state for a
31 * set of accounts.
32 *
33 * @hide
34 */
35public class SyncStateContentProviderHelper {
36 final SQLiteOpenHelper mOpenHelper;
37
38 private static final String SYNC_STATE_AUTHORITY = "syncstate";
39 private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
40
41 private static final int STATE = 0;
42
43 private static final Uri CONTENT_URI =
44 Uri.parse("content://" + SYNC_STATE_AUTHORITY + "/state");
45
46 private static final String ACCOUNT_WHERE = "_sync_account = ?";
47
48 private final Provider mInternalProviderInterface;
49
50 private static final String SYNC_STATE_TABLE = "_sync_state";
51 private static long DB_VERSION = 2;
52
53 private static final String[] ACCOUNT_PROJECTION = new String[]{"_sync_account"};
54
55 static {
56 sURIMatcher.addURI(SYNC_STATE_AUTHORITY, "state", STATE);
57 }
58
59 public SyncStateContentProviderHelper(SQLiteOpenHelper openHelper) {
60 mOpenHelper = openHelper;
61 mInternalProviderInterface = new Provider();
62 }
63
64 public ContentProvider asContentProvider() {
65 return mInternalProviderInterface;
66 }
67
68 public void createDatabase(SQLiteDatabase db) {
69 db.execSQL("DROP TABLE IF EXISTS _sync_state");
70 db.execSQL("CREATE TABLE _sync_state (" +
71 "_id INTEGER PRIMARY KEY," +
72 "_sync_account TEXT," +
73 "data TEXT," +
74 "UNIQUE(_sync_account)" +
75 ");");
76
77 db.execSQL("DROP TABLE IF EXISTS _sync_state_metadata");
78 db.execSQL("CREATE TABLE _sync_state_metadata (" +
79 "version INTEGER" +
80 ");");
81 ContentValues values = new ContentValues();
82 values.put("version", DB_VERSION);
83 db.insert("_sync_state_metadata", "version", values);
84 }
85
86 protected void onDatabaseOpened(SQLiteDatabase db) {
87 long version = DatabaseUtils.longForQuery(db,
88 "select version from _sync_state_metadata", null);
89 if (version != DB_VERSION) {
90 createDatabase(db);
91 }
92 }
93
94 class Provider extends ContentProvider {
95 public boolean onCreate() {
96 throw new UnsupportedOperationException("not implemented");
97 }
98
99 public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
100 String sortOrder) {
101 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
102 int match = sURIMatcher.match(url);
103 switch (match) {
104 case STATE:
105 return db.query(SYNC_STATE_TABLE, projection, selection, selectionArgs,
106 null, null, sortOrder);
107 default:
108 throw new UnsupportedOperationException("Cannot query URL: " + url);
109 }
110 }
111
112 public String getType(Uri uri) {
113 throw new UnsupportedOperationException("not implemented");
114 }
115
116 public Uri insert(Uri url, ContentValues values) {
117 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
118 int match = sURIMatcher.match(url);
119 switch (match) {
120 case STATE: {
121 long id = db.insert(SYNC_STATE_TABLE, "feed", values);
122 return CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
123 }
124 default:
125 throw new UnsupportedOperationException("Cannot insert into URL: " + url);
126 }
127 }
128
129 public int delete(Uri url, String userWhere, String[] whereArgs) {
130 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
131 switch (sURIMatcher.match(url)) {
132 case STATE:
133 return db.delete(SYNC_STATE_TABLE, userWhere, whereArgs);
134 default:
135 throw new IllegalArgumentException("Unknown URL " + url);
136 }
137
138 }
139
140 public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) {
141 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
142 switch (sURIMatcher.match(url)) {
143 case STATE:
144 return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
145 default:
146 throw new UnsupportedOperationException("Cannot update URL: " + url);
147 }
148
149 }
150 }
151
152 /**
153 * Check if the url matches content that this ContentProvider manages.
154 * @param url the Uri to check
155 * @return true if this ContentProvider can handle that Uri.
156 */
157 public boolean matches(Uri url) {
158 return (SYNC_STATE_AUTHORITY.equals(url.getAuthority()));
159 }
160
161 /**
162 * Replaces the contents of the _sync_state table in the destination ContentProvider
163 * with the row that matches account, if any, in the source ContentProvider.
164 * <p>
165 * The ContentProviders must expose the _sync_state table as URI content://syncstate/state.
166 * @param dbSrc the database to read from
167 * @param dbDest the database to write to
168 * @param account the account of the row that should be copied over.
169 */
170 public void copySyncState(SQLiteDatabase dbSrc, SQLiteDatabase dbDest,
171 String account) {
172 final String[] whereArgs = new String[]{account};
173 Cursor c = dbSrc.query(SYNC_STATE_TABLE, new String[]{"_sync_account", "data"},
174 ACCOUNT_WHERE, whereArgs, null, null, null);
175 try {
176 if (c.moveToNext()) {
177 ContentValues values = new ContentValues();
178 values.put("_sync_account", c.getString(0));
179 values.put("data", c.getBlob(1));
180 dbDest.replace(SYNC_STATE_TABLE, "_sync_account", values);
181 }
182 } finally {
183 c.close();
184 }
185 }
186
187 public void onAccountsChanged(String[] accounts) {
188 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
189 Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
190 try {
191 while (c.moveToNext()) {
192 final String account = c.getString(0);
193 if (!ArrayUtils.contains(accounts, account)) {
194 db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account});
195 }
196 }
197 } finally {
198 c.close();
199 }
200 }
201
202 public void discardSyncData(SQLiteDatabase db, String account) {
203 if (account != null) {
204 db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account});
205 } else {
206 db.delete(SYNC_STATE_TABLE, null, null);
207 }
208 }
209
210 /**
211 * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
212 */
213 public byte[] readSyncDataBytes(SQLiteDatabase db, String account) {
214 Cursor c = db.query(SYNC_STATE_TABLE, null, ACCOUNT_WHERE,
215 new String[]{account}, null, null, null);
216 try {
217 if (c.moveToFirst()) {
218 return c.getBlob(c.getColumnIndexOrThrow("data"));
219 }
220 } finally {
221 c.close();
222 }
223 return null;
224 }
225
226 /**
227 * Sets the SyncData bytes for the given account. The bytes array may be null.
228 */
229 public void writeSyncDataBytes(SQLiteDatabase db, String account, byte[] data) {
230 ContentValues values = new ContentValues();
231 values.put("data", data);
232 db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE, new String[]{account});
233 }
234}