blob: 94bf80777d0f0899d86b0fac9e87d0074d1a386d [file] [log] [blame]
/*
* 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;
// 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";
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 device connect to Google in background for various
* stuff, including GTalk, checkin, Market and data sync ...
*/
public static final String SETTING_BACKGROUND_DATA = "background_data";
/** 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);
}
/**
* A convenience method to set whether or not the device should connect to Google
* in background.
*
* @param contentResolver the ContentResolver to use to access the settings table
* @param flag true if it should connect.
*/
static public void setBackgroundData(ContentResolver contentResolver,
boolean flag) {
putBoolean(contentResolver, SETTING_BACKGROUND_DATA, 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);
}
/**
* Set whether or not the device should connect to Google in background
*
* @param flag true if it should
*/
public void setBackgroundData(boolean flag) {
Settings.setBackgroundData(mContentResolver, flag);
}
/**
* Check if the device should connect to Google in background.
* @return true if it should
*/
public boolean getBackgroundData() {
return getBoolean(SETTING_BACKGROUND_DATA, 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;
}
}
}
}