| /* |
| * 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 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 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 " + 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, |
| String account, String authority, long when) { |
| Cursor c = contentResolver.query(CONTENT_URI, new String[]{_ID}, |
| FINISHED_SINCE_WHERE_CLAUSE, |
| new String[]{Long.toString(when), account, 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 + ", " + 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(String account, String authority) { |
| Map<String, ContentValues> rows = getRows(); |
| for (ContentValues values : rows.values()) { |
| if (values.getAsString(ACCOUNT).equals(account) |
| && 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(String account, String authority) { |
| Map<String, ContentValues> rows = getRows(); |
| for (ContentValues values : rows.values()) { |
| if (values.getAsString(ACCOUNT).equals(account) |
| && 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 String getSyncingAccount() { |
| ContentValues values = getActiveSyncInfo(); |
| return (values == null) ? null : values.getAsString(ACCOUNT); |
| } |
| |
| 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; |
| } |
| } |
| } |
| } |