summaryrefslogtreecommitdiffstats
path: root/core/java/android/provider/Sync.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/provider/Sync.java')
-rw-r--r--core/java/android/provider/Sync.java649
1 files changed, 649 insertions, 0 deletions
diff --git a/core/java/android/provider/Sync.java b/core/java/android/provider/Sync.java
new file mode 100644
index 0000000..c9bde0e
--- /dev/null
+++ b/core/java/android/provider/Sync.java
@@ -0,0 +1,649 @@
+/*
+ * 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.provider;
+
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.accounts.Account;
+import android.text.TextUtils;
+
+import java.util.Map;
+
+/**
+ * The Sync provider stores information used in managing the syncing of the device,
+ * including the history and pending syncs.
+ *
+ * @hide
+ */
+public final class Sync {
+ // utility class
+ private Sync() {}
+
+ /**
+ * The content url for this provider.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://sync");
+
+ /**
+ * Columns from the stats table.
+ */
+ public interface StatsColumns {
+ /**
+ * The sync account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT = "account";
+
+ /**
+ * The sync account type.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * The content authority (contacts, calendar, etc.).
+ * <P>Type: TEXT</P>
+ */
+ public static final String AUTHORITY = "authority";
+ }
+
+ /**
+ * Provides constants and utility methods to access and use the stats table.
+ */
+ public static final class Stats implements BaseColumns, StatsColumns {
+
+ // utility class
+ private Stats() {}
+
+ /**
+ * The content url for this table.
+ */
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://sync/stats");
+
+ /** Projection for the _id column in the stats table. */
+ public static final String[] SYNC_STATS_PROJECTION = {_ID};
+ }
+
+ /**
+ * Columns from the history table.
+ */
+ public interface HistoryColumns {
+ /**
+ * The ID of the stats row corresponding to this event.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String STATS_ID = "stats_id";
+
+ /**
+ * The source of the sync event (LOCAL, POLL, USER, SERVER).
+ * <P>Type: INTEGER</P>
+ */
+ public static final String SOURCE = "source";
+
+ /**
+ * The type of sync event (START, STOP).
+ * <P>Type: INTEGER</P>
+ */
+ public static final String EVENT = "event";
+
+ /**
+ * The time of the event.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String EVENT_TIME = "eventTime";
+
+ /**
+ * How long this event took. This is only valid if the EVENT is EVENT_STOP.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String ELAPSED_TIME = "elapsedTime";
+
+ /**
+ * Any additional message associated with this event.
+ * <P>Type: TEXT</P>
+ */
+ public static final String MESG = "mesg";
+
+ /**
+ * How much activity was performed sending data to the server. This is sync adapter
+ * specific, but usually is something like how many record update/insert/delete attempts
+ * were carried out. This is only valid if the EVENT is EVENT_STOP.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String UPSTREAM_ACTIVITY = "upstreamActivity";
+
+ /**
+ * How much activity was performed while receiving data from the server.
+ * This is sync adapter specific, but usually is something like how many
+ * records were received from the server. This is only valid if the
+ * EVENT is EVENT_STOP.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DOWNSTREAM_ACTIVITY = "downstreamActivity";
+ }
+
+ /**
+ * Columns from the history table.
+ */
+ public interface StatusColumns {
+ /**
+ * How many syncs were completed for this account and authority.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String NUM_SYNCS = "numSyncs";
+
+ /**
+ * How long all the events for this account and authority took.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String TOTAL_ELAPSED_TIME = "totalElapsedTime";
+
+ /**
+ * The number of syncs with SOURCE_POLL.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String NUM_SOURCE_POLL = "numSourcePoll";
+
+ /**
+ * The number of syncs with SOURCE_SERVER.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String NUM_SOURCE_SERVER = "numSourceServer";
+
+ /**
+ * The number of syncs with SOURCE_LOCAL.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String NUM_SOURCE_LOCAL = "numSourceLocal";
+
+ /**
+ * The number of syncs with SOURCE_USER.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String NUM_SOURCE_USER = "numSourceUser";
+
+ /**
+ * The time in ms that the last successful sync ended. Will be null if
+ * there are no successful syncs. A successful sync is defined as one having
+ * MESG=MESG_SUCCESS.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String LAST_SUCCESS_TIME = "lastSuccessTime";
+
+ /**
+ * The SOURCE of the last successful sync. Will be null if
+ * there are no successful syncs. A successful sync is defined
+ * as one having MESG=MESG_SUCCESS.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String LAST_SUCCESS_SOURCE = "lastSuccessSource";
+
+ /**
+ * The end time in ms of the last sync that failed since the last successful sync.
+ * Will be null if there are no syncs or if the last one succeeded. A failed
+ * sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String LAST_FAILURE_TIME = "lastFailureTime";
+
+ /**
+ * The SOURCE of the last sync that failed since the last successful sync.
+ * Will be null if there are no syncs or if the last one succeeded. A failed
+ * sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String LAST_FAILURE_SOURCE = "lastFailureSource";
+
+ /**
+ * The MESG of the last sync that failed since the last successful sync.
+ * Will be null if there are no syncs or if the last one succeeded. A failed
+ * sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
+ * <P>Type: STRING</P>
+ */
+ public static final String LAST_FAILURE_MESG = "lastFailureMesg";
+
+ /**
+ * Is set to 1 if a sync is pending, 0 if not.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String PENDING = "pending";
+ }
+
+ /**
+ * Provides constants and utility methods to access and use the history
+ * table.
+ */
+ public static class History implements BaseColumns,
+ StatsColumns,
+ HistoryColumns {
+
+ /**
+ * The content url for this table.
+ */
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://sync/history");
+
+ /** Enum value for a sync start event. */
+ public static final int EVENT_START = 0;
+
+ /** Enum value for a sync stop event. */
+ public static final int EVENT_STOP = 1;
+
+ // TODO: i18n -- grab these out of resources.
+ /** String names for the sync event types. */
+ public static final String[] EVENTS = { "START", "STOP" };
+
+ /** Enum value for a server-initiated sync. */
+ public static final int SOURCE_SERVER = 0;
+
+ /** Enum value for a local-initiated sync. */
+ public static final int SOURCE_LOCAL = 1;
+ /**
+ * Enum value for a poll-based sync (e.g., upon connection to
+ * network)
+ */
+ public static final int SOURCE_POLL = 2;
+
+ /** Enum value for a user-initiated sync. */
+ public static final int SOURCE_USER = 3;
+
+ // TODO: i18n -- grab these out of resources.
+ /** String names for the sync source types. */
+ public static final String[] SOURCES = { "SERVER",
+ "LOCAL",
+ "POLL",
+ "USER" };
+
+ // Error types
+ public static final int ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
+ public static final int ERROR_AUTHENTICATION = 2;
+ public static final int ERROR_IO = 3;
+ public static final int ERROR_PARSE = 4;
+ public static final int ERROR_CONFLICT = 5;
+ public static final int ERROR_TOO_MANY_DELETIONS = 6;
+ public static final int ERROR_TOO_MANY_RETRIES = 7;
+ public static final int ERROR_INTERNAL = 8;
+
+ // The MESG column will contain one of these or one of the Error types.
+ public static final String MESG_SUCCESS = "success";
+ public static final String MESG_CANCELED = "canceled";
+
+ private static final String FINISHED_SINCE_WHERE_CLAUSE = EVENT + "=" + EVENT_STOP
+ + " AND " + EVENT_TIME + ">? AND " + ACCOUNT + "=? AND " + ACCOUNT_TYPE + "=?"
+ + " AND " + AUTHORITY + "=?";
+
+ public static String mesgToString(String mesg) {
+ if (MESG_SUCCESS.equals(mesg)) return mesg;
+ if (MESG_CANCELED.equals(mesg)) return mesg;
+ switch (Integer.parseInt(mesg)) {
+ case ERROR_SYNC_ALREADY_IN_PROGRESS: return "already in progress";
+ case ERROR_AUTHENTICATION: return "bad authentication";
+ case ERROR_IO: return "network error";
+ case ERROR_PARSE: return "parse error";
+ case ERROR_CONFLICT: return "conflict detected";
+ case ERROR_TOO_MANY_DELETIONS: return "too many deletions";
+ case ERROR_TOO_MANY_RETRIES: return "too many retries";
+ case ERROR_INTERNAL: return "internal error";
+ default: return "unknown error";
+ }
+ }
+
+ // utility class
+ private History() {}
+
+ /**
+ * returns a cursor that queries the sync history in descending event time order
+ * @param contentResolver the ContentResolver to use for the query
+ * @return the cursor on the History table
+ */
+ public static Cursor query(ContentResolver contentResolver) {
+ return contentResolver.query(CONTENT_URI, null, null, null, EVENT_TIME + " desc");
+ }
+
+ public static boolean hasNewerSyncFinished(ContentResolver contentResolver,
+ Account account, String authority, long when) {
+ Cursor c = contentResolver.query(CONTENT_URI, new String[]{_ID},
+ FINISHED_SINCE_WHERE_CLAUSE,
+ new String[]{Long.toString(when), account.mName, account.mType, authority},
+ null);
+ try {
+ return c.getCount() > 0;
+ } finally {
+ c.close();
+ }
+ }
+ }
+
+ /**
+ * Provides constants and utility methods to access and use the authority history
+ * table, which contains information about syncs aggregated by account and authority.
+ * All the HistoryColumns except for EVENT are present, plus the AuthorityHistoryColumns.
+ */
+ public static class Status extends History implements StatusColumns {
+
+ /**
+ * The content url for this table.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://sync/status");
+
+ // utility class
+ private Status() {}
+
+ /**
+ * returns a cursor that queries the authority sync history in descending event order of
+ * ACCOUNT, AUTHORITY
+ * @param contentResolver the ContentResolver to use for the query
+ * @return the cursor on the AuthorityHistory table
+ */
+ public static Cursor query(ContentResolver contentResolver) {
+ return contentResolver.query(CONTENT_URI, null, null, null,
+ ACCOUNT_TYPE + "," + ACCOUNT + "," + AUTHORITY);
+ }
+
+ public static class QueryMap extends ContentQueryMap {
+ public QueryMap(ContentResolver contentResolver,
+ boolean keepUpdated,
+ Handler handlerForUpdateNotifications) {
+ super(contentResolver.query(CONTENT_URI, null, null, null, null),
+ _ID, keepUpdated, handlerForUpdateNotifications);
+ }
+
+ public ContentValues get(Account account, String authority) {
+ Map<String, ContentValues> rows = getRows();
+ for (ContentValues values : rows.values()) {
+ if (values.getAsString(ACCOUNT).equals(account.mName)
+ && values.getAsString(ACCOUNT_TYPE).equals(account.mType)
+ && values.getAsString(AUTHORITY).equals(authority)) {
+ return values;
+ }
+ }
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Provides constants and utility methods to access and use the pending syncs table
+ */
+ public static final class Pending implements BaseColumns,
+ StatsColumns {
+
+ /**
+ * The content url for this table.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://sync/pending");
+
+ // utility class
+ private Pending() {}
+
+ public static class QueryMap extends ContentQueryMap {
+ public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
+ Handler handlerForUpdateNotifications) {
+ super(contentResolver.query(CONTENT_URI, null, null, null, null), _ID, keepUpdated,
+ handlerForUpdateNotifications);
+ }
+
+ public boolean isPending(Account account, String authority) {
+ Map<String, ContentValues> rows = getRows();
+ for (ContentValues values : rows.values()) {
+ if (values.getAsString(ACCOUNT).equals(account.mName)
+ && values.getAsString(ACCOUNT_TYPE).equals(account.mType)
+ && values.getAsString(AUTHORITY).equals(authority)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Columns from the history table.
+ */
+ public interface ActiveColumns {
+ /**
+ * The wallclock time of when the active sync started.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String START_TIME = "startTime";
+ }
+
+ /**
+ * Provides constants and utility methods to access and use the pending syncs table
+ */
+ public static final class Active implements BaseColumns,
+ StatsColumns,
+ ActiveColumns {
+
+ /**
+ * The content url for this table.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://sync/active");
+
+ // utility class
+ private Active() {}
+
+ public static class QueryMap extends ContentQueryMap {
+ public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
+ Handler handlerForUpdateNotifications) {
+ super(contentResolver.query(CONTENT_URI, null, null, null, null), _ID, keepUpdated,
+ handlerForUpdateNotifications);
+ }
+
+ public ContentValues getActiveSyncInfo() {
+ Map<String, ContentValues> rows = getRows();
+ for (ContentValues values : rows.values()) {
+ return values;
+ }
+ return null;
+ }
+
+ public Account getSyncingAccount() {
+ ContentValues values = getActiveSyncInfo();
+ if (values == null || TextUtils.isEmpty(values.getAsString(ACCOUNT))) {
+ return null;
+ }
+ return new Account(values.getAsString(ACCOUNT), values.getAsString(ACCOUNT_TYPE));
+ }
+
+ public String getSyncingAuthority() {
+ ContentValues values = getActiveSyncInfo();
+ return (values == null) ? null : values.getAsString(AUTHORITY);
+ }
+
+ public long getSyncStartTime() {
+ ContentValues values = getActiveSyncInfo();
+ return (values == null) ? -1 : values.getAsLong(START_TIME);
+ }
+ }
+ }
+
+ /**
+ * Columns in the settings table, which holds key/value pairs of settings.
+ */
+ public interface SettingsColumns {
+ /**
+ * The key of the setting
+ * <P>Type: TEXT</P>
+ */
+ public static final String KEY = "name";
+
+ /**
+ * The value of the settings
+ * <P>Type: TEXT</P>
+ */
+ public static final String VALUE = "value";
+ }
+
+ /**
+ * Provides constants and utility methods to access and use the settings
+ * table.
+ */
+ public static final class Settings implements BaseColumns, SettingsColumns {
+ /**
+ * The Uri of the settings table. This table behaves a little differently than
+ * normal tables. Updates are not allowed, only inserts, and inserts cause a replace
+ * to be performed, which first deletes the row if it is already present.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://sync/settings");
+
+ /** controls whether or not the device listens for sync tickles */
+ public static final String SETTING_LISTEN_FOR_TICKLES = "listen_for_tickles";
+
+ /** controls whether or not the individual provider is synced when tickles are received */
+ public static final String SETTING_SYNC_PROVIDER_PREFIX = "sync_provider_";
+
+ /** query column project */
+ private static final String[] PROJECTION = { KEY, VALUE };
+
+ /**
+ * Convenience function for updating a single settings value as a
+ * boolean. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param contentResolver the ContentResolver to use to access the settings table
+ * @param name The name of the setting to modify.
+ * @param val The new value for the setting.
+ */
+ static private void putBoolean(ContentResolver contentResolver, String name, boolean val) {
+ ContentValues values = new ContentValues();
+ values.put(KEY, name);
+ values.put(VALUE, Boolean.toString(val));
+ // this insert is translated into an update by the underlying Sync provider
+ contentResolver.insert(CONTENT_URI, values);
+ }
+
+ /**
+ * Convenience function for getting a setting value as a boolean without using the
+ * QueryMap for light-weight setting querying.
+ * @param contentResolver The ContentResolver for querying the setting.
+ * @param name The name of the setting to query
+ * @param def The default value for the setting.
+ * @return The value of the setting.
+ */
+ static public boolean getBoolean(ContentResolver contentResolver,
+ String name, boolean def) {
+ Cursor cursor = contentResolver.query(
+ CONTENT_URI,
+ PROJECTION,
+ KEY + "=?",
+ new String[] { name },
+ null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ return Boolean.parseBoolean(cursor.getString(1));
+ }
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ return def;
+ }
+
+ /**
+ * A convenience method to set whether or not the provider is synced when
+ * it receives a network tickle.
+ *
+ * @param contentResolver the ContentResolver to use to access the settings table
+ * @param providerName the provider whose behavior is being controlled
+ * @param sync true if the provider should be synced when tickles are received for it
+ */
+ static public void setSyncProviderAutomatically(ContentResolver contentResolver,
+ String providerName, boolean sync) {
+ putBoolean(contentResolver, SETTING_SYNC_PROVIDER_PREFIX + providerName, sync);
+ }
+
+ /**
+ * A convenience method to set whether or not the device should listen to tickles.
+ *
+ * @param contentResolver the ContentResolver to use to access the settings table
+ * @param flag true if it should listen.
+ */
+ static public void setListenForNetworkTickles(ContentResolver contentResolver,
+ boolean flag) {
+ putBoolean(contentResolver, SETTING_LISTEN_FOR_TICKLES, flag);
+ }
+
+ public static class QueryMap extends ContentQueryMap {
+ private ContentResolver mContentResolver;
+
+ public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
+ Handler handlerForUpdateNotifications) {
+ super(contentResolver.query(CONTENT_URI, null, null, null, null), KEY, keepUpdated,
+ handlerForUpdateNotifications);
+ mContentResolver = contentResolver;
+ }
+
+ /**
+ * Check if the provider should be synced when a network tickle is received
+ * @param providerName the provider whose setting we are querying
+ * @return true of the provider should be synced when a network tickle is received
+ */
+ public boolean getSyncProviderAutomatically(String providerName) {
+ return getBoolean(SETTING_SYNC_PROVIDER_PREFIX + providerName, true);
+ }
+
+ /**
+ * Set whether or not the provider is synced when it receives a network tickle.
+ *
+ * @param providerName the provider whose behavior is being controlled
+ * @param sync true if the provider should be synced when tickles are received for it
+ */
+ public void setSyncProviderAutomatically(String providerName, boolean sync) {
+ Settings.setSyncProviderAutomatically(mContentResolver, providerName, sync);
+ }
+
+ /**
+ * Set whether or not the device should listen for tickles.
+ *
+ * @param flag true if it should listen.
+ */
+ public void setListenForNetworkTickles(boolean flag) {
+ Settings.setListenForNetworkTickles(mContentResolver, flag);
+ }
+
+ /**
+ * Check if the device should listen to tickles.
+
+ * @return true if it should
+ */
+ public boolean getListenForNetworkTickles() {
+ return getBoolean(SETTING_LISTEN_FOR_TICKLES, true);
+ }
+
+ /**
+ * Convenience function for retrieving a single settings value
+ * as a boolean.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ * @return The setting's current value, or 'def' if it is not defined.
+ */
+ private boolean getBoolean(String name, boolean def) {
+ ContentValues values = getValues(name);
+ return values != null ? values.getAsBoolean(VALUE) : def;
+ }
+ }
+ }
+}