SyncManagerpublic class SyncManager extends Object
Fields Summary |
---|
private static final String | TAG | private static final long | MILLIS_IN_HOUR | private static final long | MILLIS_IN_DAY | private static final long | MILLIS_IN_WEEK | private static final long | MILLIS_IN_4WEEKS | private static final long | LOCAL_SYNC_DELAYDelay a sync due to local changes this long. In milliseconds | private static final long | MAX_TIME_PER_SYNCIf a sync takes longer than this and the sync queue is not empty then we will
cancel it and add it back to the end of the sync queue. In milliseconds. | private static final long | SYNC_NOTIFICATION_DELAY | private static final long | INITIAL_SYNC_RETRY_TIME_IN_MSWhen retrying a sync for the first time use this delay. After that
the retry time will double until it reached MAX_SYNC_RETRY_TIME.
In milliseconds. | private static final long | DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDSDefault the max sync retry time to this value. | private static final long | ERROR_NOTIFICATION_DELAY_MSAn error notification is sent if sync of any of the providers has been failing for this long. | private static final String | SYNC_WAKE_LOCK | private static final String | HANDLE_SYNC_ALARM_WAKE_LOCK | private Context | mContext | private ContentResolver | mContentResolver | private String | mStatusText | private long | mHeartbeatTime | private android.accounts.AccountMonitor | mAccountMonitor | private volatile String[] | mAccounts | private volatile PowerManager.WakeLock | mSyncWakeLock | private volatile PowerManager.WakeLock | mHandleAlarmWakeLock | private volatile boolean | mDataConnectionIsConnected | private volatile boolean | mStorageIsLow | private Sync.Settings.QueryMap | mSyncSettings | private final android.app.NotificationManager | mNotificationMgr | private android.app.AlarmManager | mAlarmService | private android.os.HandlerThread | mSyncThread | private volatile android.content.pm.IPackageManager | mPackageManager | private final SyncStorageEngine | mSyncStorageEngine | private final SyncQueue | mSyncQueue | private ActiveSyncContext | mActiveSyncContext | private boolean | mNeedSyncErrorNotification | private boolean | mNeedSyncActiveNotification | private volatile boolean | mSyncPollInitialized | private final android.app.PendingIntent | mSyncAlarmIntent | private final android.app.PendingIntent | mSyncPollAlarmIntent | private BroadcastReceiver | mStorageIntentReceiver | private BroadcastReceiver | mConnectivityIntentReceiver | private static final String | ACTION_SYNC_ALARM | private static final String | SYNC_POLL_ALARM | private final SyncHandler | mSyncHandler | private static final String[] | SYNC_ACTIVE_PROJECTION | private static final String[] | SYNC_PENDING_PROJECTION | private static final int | MAX_SYNC_POLL_DELAY_SECONDS | private static final int | MIN_SYNC_POLL_DELAY_SECONDS | private static final String | SYNCMANAGER_PREFS_FILENAME | private static String[] | STATUS_PROJECTION |
Constructors Summary |
---|
public SyncManager(Context context, boolean factoryTest)
// Initialize the SyncStorageEngine first, before registering observers
// and creating threads and so on; it may fail if the disk is full.
SyncStorageEngine.init(context);
mSyncStorageEngine = SyncStorageEngine.getSingleton();
mSyncQueue = new SyncQueue(mSyncStorageEngine);
mContext = context;
mSyncThread = new HandlerThread("SyncHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
mSyncThread.start();
mSyncHandler = new SyncHandler(mSyncThread.getLooper());
mPackageManager = null;
mSyncAlarmIntent = PendingIntent.getBroadcast(
mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
mSyncPollAlarmIntent = PendingIntent.getBroadcast(
mContext, 0 /* ignored */, new Intent(SYNC_POLL_ALARM), 0);
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
context.registerReceiver(mStorageIntentReceiver, intentFilter);
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
context.registerReceiver(new SyncAlarmIntentReceiver(),
new IntentFilter(ACTION_SYNC_ALARM));
} else {
mNotificationMgr = null;
}
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mSyncWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, SYNC_WAKE_LOCK);
mSyncWakeLock.setReferenceCounted(false);
// This WakeLock is used to ensure that we stay awake between the time that we receive
// a sync alarm notification and when we finish processing it. We need to do this
// because we don't do the work in the alarm handler, rather we do it in a message
// handler.
mHandleAlarmWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
HANDLE_SYNC_ALARM_WAKE_LOCK);
mHandleAlarmWakeLock.setReferenceCounted(false);
if (!factoryTest) {
AccountMonitorListener listener = new AccountMonitorListener() {
public void onAccountsUpdated(String[] accounts) {
final boolean hadAccountsAlready = mAccounts != null;
// copy the accounts into a new array and change mAccounts to point to it
String[] newAccounts = new String[accounts.length];
System.arraycopy(accounts, 0, newAccounts, 0, accounts.length);
mAccounts = newAccounts;
// if a sync is in progress yet it is no longer in the accounts list, cancel it
ActiveSyncContext activeSyncContext = mActiveSyncContext;
if (activeSyncContext != null) {
if (!ArrayUtils.contains(newAccounts,
activeSyncContext.mSyncOperation.account)) {
Log.d(TAG, "canceling sync since the account has been removed");
sendSyncFinishedOrCanceledMessage(activeSyncContext,
null /* no result since this is a cancel */);
}
}
// we must do this since we don't bother scheduling alarms when
// the accounts are not set yet
sendCheckAlarmsMessage();
mSyncStorageEngine.doDatabaseCleanup(accounts);
if (hadAccountsAlready && mAccounts.length > 0) {
// request a sync so that if the password was changed we will retry any sync
// that failed when it was wrong
startSync(null /* all providers */, null /* no extras */);
}
}
};
mAccountMonitor = new AccountMonitor(context, listener);
}
|
Methods Summary |
---|
public void | cancelActiveSync(android.net.Uri uri)Cancel the active sync if it matches the uri. The uri corresponds to the one passed
in to startSync().
ActiveSyncContext activeSyncContext = mActiveSyncContext;
if (activeSyncContext != null) {
// if a Uri was specified then only cancel the sync if it matches the the uri
if (uri != null) {
if (!uri.getAuthority().equals(activeSyncContext.mSyncOperation.authority)) {
return;
}
}
sendSyncFinishedOrCanceledMessage(activeSyncContext,
null /* no result since this is a cancel */);
}
| public void | clearScheduledSyncOperations(android.net.Uri uri)Remove any scheduled sync operations that match uri. The uri corresponds to the one passed
in to startSync().
synchronized (mSyncQueue) {
mSyncQueue.clear(null, uri != null ? uri.getAuthority() : null);
}
| protected void | dump(java.io.FileDescriptor fd, java.io.PrintWriter pw)
StringBuilder sb = new StringBuilder();
dumpSyncState(sb);
sb.append("\n");
if (isSyncEnabled()) {
dumpSyncHistory(sb);
}
pw.println(sb.toString());
| protected void | dumpSyncHistory(java.lang.StringBuilder sb)
Cursor c = mSyncStorageEngine.query(Sync.History.CONTENT_URI, null, "event=?",
new String[]{String.valueOf(Sync.History.EVENT_STOP)},
Sync.HistoryColumns.EVENT_TIME + " desc");
try {
long numSyncsLastHour = 0, durationLastHour = 0;
long numSyncsLastDay = 0, durationLastDay = 0;
long numSyncsLastWeek = 0, durationLastWeek = 0;
long numSyncsLast4Weeks = 0, durationLast4Weeks = 0;
long numSyncsTotal = 0, durationTotal = 0;
long now = System.currentTimeMillis();
int indexEventTime = c.getColumnIndexOrThrow(Sync.History.EVENT_TIME);
int indexElapsedTime = c.getColumnIndexOrThrow(Sync.History.ELAPSED_TIME);
while (c.moveToNext()) {
long duration = c.getLong(indexElapsedTime);
long endTime = c.getLong(indexEventTime) + duration;
long millisSinceStart = now - endTime;
numSyncsTotal++;
durationTotal += duration;
if (millisSinceStart < MILLIS_IN_HOUR) {
numSyncsLastHour++;
durationLastHour += duration;
}
if (millisSinceStart < MILLIS_IN_DAY) {
numSyncsLastDay++;
durationLastDay += duration;
}
if (millisSinceStart < MILLIS_IN_WEEK) {
numSyncsLastWeek++;
durationLastWeek += duration;
}
if (millisSinceStart < MILLIS_IN_4WEEKS) {
numSyncsLast4Weeks++;
durationLast4Weeks += duration;
}
}
dumpSyncIntervalHeader(sb);
dumpSyncInterval(sb, "hour", MILLIS_IN_HOUR, numSyncsLastHour, durationLastHour);
dumpSyncInterval(sb, "day", MILLIS_IN_DAY, numSyncsLastDay, durationLastDay);
dumpSyncInterval(sb, "week", MILLIS_IN_WEEK, numSyncsLastWeek, durationLastWeek);
dumpSyncInterval(sb, "4 weeks",
MILLIS_IN_4WEEKS, numSyncsLast4Weeks, durationLast4Weeks);
dumpSyncInterval(sb, "total", 0, numSyncsTotal, durationTotal);
dumpSyncIntervalFooter(sb);
} finally {
c.close();
}
| private void | dumpSyncHistoryFooter(java.lang.StringBuilder sb)
sb.append(" |___________________________________________________________________________________________________________________________|\n");
| private void | dumpSyncHistoryHeader(java.lang.StringBuilder sb, java.lang.String account)
sb.append(" Account: ").append(account).append("\n");
sb.append(" ___________________________________________________________________________________________________________________________\n");
sb.append(" | | num times synced | total | last success | |\n");
sb.append(" | authority | local | poll | server | user | total | duration | source | time | result if failing |\n");
| private void | dumpSyncHistoryRow(java.lang.StringBuilder sb, android.database.Cursor c)
boolean hasSuccess = !c.isNull(9);
boolean hasFailure = !c.isNull(11);
Time timeSuccess = new Time();
if (hasSuccess) timeSuccess.set(c.getLong(9));
Time timeFailure = new Time();
if (hasFailure) timeFailure.set(c.getLong(11));
sb.append(String.format(" | %-15s | %5d | %5d | %6d | %5d | %5d | %8s | %7s | %19s | %19s |\n",
c.getString(1),
c.getLong(4),
c.getLong(5),
c.getLong(6),
c.getLong(7),
c.getLong(2),
DateUtils.formatElapsedTime(c.getLong(3)/1000),
hasSuccess ? Sync.History.SOURCES[c.getInt(8)] : "",
hasSuccess ? timeSuccess.format("%Y-%m-%d %H:%M:%S") : "",
hasFailure ? History.mesgToString(c.getString(12)) : ""));
| private void | dumpSyncInterval(java.lang.StringBuilder sb, java.lang.String label, long interval, long numSyncs, long duration)
sb.append(String.format(" | %-8s | %6d | %8.1f | %8.1f",
label, numSyncs, ((float)duration/numSyncs)/1000, (float)duration/1000));
if (interval > 0) {
sb.append(String.format(" | %13.2f |\n", ((float)duration/interval)*100.0));
} else {
sb.append(String.format(" | %13s |\n", "na"));
}
| private void | dumpSyncIntervalFooter(java.lang.StringBuilder sb)
sb.append(" |_________________________________________________________|\n");
| private void | dumpSyncIntervalHeader(java.lang.StringBuilder sb)
sb.append("Sync Stats\n");
sb.append(" ___________________________________________________________\n");
sb.append(" | | | duration in sec | |\n");
sb.append(" | interval | count | average | total | % of interval |\n");
| private void | dumpSyncPendingFooter(java.lang.StringBuilder sb)
sb.append(" |__________________________________________________|\n");
| private void | dumpSyncPendingHeader(java.lang.StringBuilder sb)
sb.append(" ____________________________________________________\n");
sb.append(" | account | authority |\n");
| private void | dumpSyncPendingRow(java.lang.StringBuilder sb, android.database.Cursor c)
sb.append(String.format(" | %-30s | %-15s |\n", c.getString(0), c.getString(1)));
| protected void | dumpSyncState(java.lang.StringBuilder sb)
sb.append("sync enabled: ").append(isSyncEnabled()).append("\n");
sb.append("data connected: ").append(mDataConnectionIsConnected).append("\n");
sb.append("memory low: ").append(mStorageIsLow).append("\n");
final String[] accounts = mAccounts;
sb.append("accounts: ");
if (accounts != null) {
sb.append(accounts.length);
} else {
sb.append("none");
}
sb.append("\n");
final long now = SystemClock.elapsedRealtime();
sb.append("now: ").append(now).append("\n");
sb.append("uptime: ").append(DateUtils.formatElapsedTime(now/1000)).append(" (HH:MM:SS)\n");
sb.append("time spent syncing : ")
.append(DateUtils.formatElapsedTime(
mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000))
.append(" (HH:MM:SS), sync ")
.append(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ")
.append("in progress").append("\n");
if (mSyncHandler.mAlarmScheduleTime != null) {
sb.append("next alarm time: ").append(mSyncHandler.mAlarmScheduleTime)
.append(" (")
.append(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000))
.append(" (HH:MM:SS) from now)\n");
} else {
sb.append("no alarm is scheduled (there had better not be any pending syncs)\n");
}
sb.append("active sync: ").append(mActiveSyncContext).append("\n");
sb.append("notification info: ");
mSyncHandler.mSyncNotificationInfo.toString(sb);
sb.append("\n");
synchronized (mSyncQueue) {
sb.append("sync queue: ");
mSyncQueue.dump(sb);
}
Cursor c = mSyncStorageEngine.query(Sync.Active.CONTENT_URI,
SYNC_ACTIVE_PROJECTION, null, null, null);
sb.append("\n");
try {
if (c.moveToNext()) {
final long durationInSeconds = (now - c.getLong(2)) / 1000;
sb.append("Active sync: ").append(c.getString(0))
.append(" ").append(c.getString(1))
.append(", duration is ")
.append(DateUtils.formatElapsedTime(durationInSeconds)).append(".\n");
} else {
sb.append("No sync is in progress.\n");
}
} finally {
c.close();
}
c = mSyncStorageEngine.query(Sync.Pending.CONTENT_URI,
SYNC_PENDING_PROJECTION, null, null, "account, authority");
sb.append("\nPending Syncs\n");
try {
if (c.getCount() != 0) {
dumpSyncPendingHeader(sb);
while (c.moveToNext()) {
dumpSyncPendingRow(sb, c);
}
dumpSyncPendingFooter(sb);
} else {
sb.append("none\n");
}
} finally {
c.close();
}
String currentAccount = null;
c = mSyncStorageEngine.query(Sync.Status.CONTENT_URI,
STATUS_PROJECTION, null, null, "account, authority");
sb.append("\nSync history by account and authority\n");
try {
while (c.moveToNext()) {
if (!TextUtils.equals(currentAccount, c.getString(0))) {
if (currentAccount != null) {
dumpSyncHistoryFooter(sb);
}
currentAccount = c.getString(0);
dumpSyncHistoryHeader(sb, currentAccount);
}
dumpSyncHistoryRow(sb, c);
}
if (c.getCount() > 0) dumpSyncHistoryFooter(sb);
} finally {
c.close();
}
| private void | ensureAlarmService()
if (mAlarmService == null) {
mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
}
| private void | ensureContentResolver()
if (mContentResolver == null) {
mContentResolver = mContext.getContentResolver();
}
| public android.content.SyncManager$ActiveSyncContext | getActiveSyncContext()
return mActiveSyncContext;
| private android.content.pm.IPackageManager | getPackageManager()
// Don't bother synchronizing on this. The worst that can happen is that two threads
// can try to get the package manager at the same time but only one result gets
// used. Since there is only one package manager in the system this doesn't matter.
if (mPackageManager == null) {
IBinder b = ServiceManager.getService("package");
mPackageManager = IPackageManager.Stub.asInterface(b);
}
return mPackageManager;
| private Sync.Settings.QueryMap | getSyncSettings()
if (mSyncSettings == null) {
mSyncSettings = new Sync.Settings.QueryMap(mContext.getContentResolver(), true,
new Handler());
mSyncSettings.addObserver(new Observer(){
public void update(Observable o, Object arg) {
// force the sync loop to run if the settings change
sendCheckAlarmsMessage();
}
});
}
return mSyncSettings;
| public java.lang.String | getSyncingAccount()
ActiveSyncContext activeSyncContext = mActiveSyncContext;
return (activeSyncContext != null) ? activeSyncContext.mSyncOperation.account : null;
| private void | handleSyncPollAlarm()
// determine the next poll time
long delayMs = jitterize(MIN_SYNC_POLL_DELAY_SECONDS, MAX_SYNC_POLL_DELAY_SECONDS) * 1000;
long nextRelativePollTimeMs = SystemClock.elapsedRealtime() + delayMs;
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "handleSyncPollAlarm: delay " + delayMs);
// write the absolute time to shared preferences
writeSyncPollTime(System.currentTimeMillis() + delayMs);
// schedule an alarm for the next poll time
scheduleSyncPollAlarm(nextRelativePollTimeMs);
// perform a poll
scheduleSync(null /* sync all syncable providers */, new Bundle(), 0 /* no delay */);
| private synchronized void | initializeSyncPoll()
if (mSyncPollInitialized) return;
mSyncPollInitialized = true;
mContext.registerReceiver(new SyncPollAlarmReceiver(), new IntentFilter(SYNC_POLL_ALARM));
// load the next poll time from shared preferences
long absoluteAlarmTime = readSyncPollTime();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "initializeSyncPoll: absoluteAlarmTime is " + absoluteAlarmTime);
}
// Convert absoluteAlarmTime to elapsed realtime. If this time was in the past then
// schedule the poll immediately, if it is too far in the future then cap it at
// MAX_SYNC_POLL_DELAY_SECONDS.
long absoluteNow = System.currentTimeMillis();
long relativeNow = SystemClock.elapsedRealtime();
long relativeAlarmTime = relativeNow;
if (absoluteAlarmTime > absoluteNow) {
long delayInMs = absoluteAlarmTime - absoluteNow;
final int maxDelayInMs = MAX_SYNC_POLL_DELAY_SECONDS * 1000;
if (delayInMs > maxDelayInMs) {
delayInMs = MAX_SYNC_POLL_DELAY_SECONDS * 1000;
}
relativeAlarmTime += delayInMs;
}
// schedule an alarm for the next poll time
scheduleSyncPollAlarm(relativeAlarmTime);
| private boolean | isSyncEnabled()Returns whether or not sync is enabled. Sync can be enabled by
setting the system property "ro.config.sync" to the value "yes".
This is normally done at boot time on builds that support sync.
// Require the precise value "yes" to discourage accidental activation.
return "yes".equals(SystemProperties.get("ro.config.sync"));
| private long | jitterize(long minValue, long maxValue)Return a random value v that satisfies minValue <= v < maxValue. The difference between
maxValue and minValue must be less than Integer.MAX_VALUE.
Random random = new Random(SystemClock.elapsedRealtime());
long spread = maxValue - minValue;
if (spread > Integer.MAX_VALUE) {
throw new IllegalArgumentException("the difference between the maxValue and the "
+ "minValue must be less than " + Integer.MAX_VALUE);
}
return minValue + random.nextInt((int)spread);
| void | maybeRescheduleSync(SyncResult syncResult, android.content.SyncManager$SyncOperation previousSyncOperation)
boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
if (isLoggable) {
Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", "
+ previousSyncOperation);
}
// If the operation succeeded to some extent then retry immediately.
// If this was a two-way sync then retry soft errors with an exponential backoff.
// If this was an upward sync then schedule a two-way sync immediately.
// Otherwise do not reschedule.
if (syncResult.madeSomeProgress()) {
if (isLoggable) {
Log.d(TAG, "retrying sync operation immediately because "
+ "even though it had an error it achieved some success");
}
rescheduleImmediately(previousSyncOperation);
} else if (previousSyncOperation.extras.getBoolean(
ContentResolver.SYNC_EXTRAS_UPLOAD, false)) {
final SyncOperation newSyncOperation = new SyncOperation(previousSyncOperation);
newSyncOperation.extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
newSyncOperation.setDelay(0);
if (Config.LOGD) {
Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
+ "encountered an error: " + previousSyncOperation);
}
scheduleSyncOperation(newSyncOperation);
} else if (syncResult.hasSoftError()) {
long delay = rescheduleWithDelay(previousSyncOperation);
if (delay >= 0) {
if (isLoggable) {
Log.d(TAG, "retrying sync operation in " + delay + " ms because "
+ "it encountered a soft error: " + previousSyncOperation);
}
}
} else {
if (Config.LOGD) {
Log.d(TAG, "not retrying sync operation because the error is a hard error: "
+ previousSyncOperation);
}
}
| private void | populateProvidersList(android.net.Uri url, java.util.List names, java.util.List providers)
try {
final IPackageManager packageManager = getPackageManager();
if (url == null) {
packageManager.querySyncProviders(names, providers);
} else {
final String authority = url.getAuthority();
ProviderInfo info = packageManager.resolveContentProvider(url.getAuthority(), 0);
if (info != null) {
// only set this provider if the requested authority is the primary authority
String[] providerNames = info.authority.split(";");
if (url.getAuthority().equals(providerNames[0])) {
names.add(authority);
providers.add(info);
}
}
}
} catch (RemoteException ex) {
// we should really never get this, but if we do then clear the lists, which
// will result in the dropping of the sync request
Log.e(TAG, "error trying to get the ProviderInfo for " + url, ex);
names.clear();
providers.clear();
}
| private long | readSyncPollTime()
File f = new File(SYNCMANAGER_PREFS_FILENAME);
DataInputStream str = null;
try {
str = new DataInputStream(new FileInputStream(f));
return str.readLong();
} catch (FileNotFoundException e) {
writeSyncPollTime(0);
} catch (IOException e) {
Log.w(TAG, "error reading file " + f, e);
} finally {
if (str != null) {
try {
str.close();
} catch (IOException e) {
Log.w(TAG, "error closing file " + f, e);
}
}
}
return 0;
| private void | rescheduleImmediately(android.content.SyncManager$SyncOperation syncOperation)
SyncOperation rescheduledSyncOperation = new SyncOperation(syncOperation);
rescheduledSyncOperation.setDelay(0);
scheduleSyncOperation(rescheduledSyncOperation);
| private long | rescheduleWithDelay(android.content.SyncManager$SyncOperation syncOperation)
long newDelayInMs;
if (syncOperation.delay == 0) {
// The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS
newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS,
(long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1));
} else {
// Subsequent delays are the double of the previous delay
newDelayInMs = syncOperation.delay * 2;
}
// Cap the delay
ensureContentResolver();
long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContentResolver,
Settings.Gservices.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
}
SyncOperation rescheduledSyncOperation = new SyncOperation(syncOperation);
rescheduledSyncOperation.setDelay(newDelayInMs);
scheduleSyncOperation(rescheduledSyncOperation);
return newDelayInMs;
| public void | scheduleLocalSync(android.net.Uri url)
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
scheduleSync(url, extras, LOCAL_SYNC_DELAY);
| public void | scheduleSync(android.net.Uri url, android.os.Bundle extras, long delay)Initiate a sync. This can start a sync for all providers
(pass null to url, set onlyTicklable to false), only those
providers that are marked as ticklable (pass null to url,
set onlyTicklable to true), or a specific provider (set url
to the content url of the provider).
If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is
true then initiate a sync that just checks for local changes to send
to the server, otherwise initiate a sync that first gets any
changes from the server before sending local changes back to
the server.
If a specific provider is being synced (the url is non-null)
then the extras can contain SyncAdapter-specific information
to control what gets synced (e.g. which specific feed to sync).
You'll start getting callbacks after this.
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (isLoggable) {
Log.v(TAG, "scheduleSync:"
+ " delay " + delay
+ ", url " + ((url == null) ? "(null)" : url)
+ ", extras " + ((extras == null) ? "(null)" : extras));
}
if (!isSyncEnabled()) {
if (isLoggable) {
Log.v(TAG, "not syncing because sync is disabled");
}
setStatusText("Sync is disabled.");
return;
}
if (mAccounts == null) setStatusText("The accounts aren't known yet.");
if (!mDataConnectionIsConnected) setStatusText("No data connection");
if (mStorageIsLow) setStatusText("Memory low");
if (extras == null) extras = new Bundle();
Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
if (expedited) {
delay = -1; // this means schedule at the front of the queue
}
String[] accounts;
String accountFromExtras = extras.getString(ContentResolver.SYNC_EXTRAS_ACCOUNT);
if (!TextUtils.isEmpty(accountFromExtras)) {
accounts = new String[]{accountFromExtras};
} else {
// if the accounts aren't configured yet then we can't support an account-less
// sync request
accounts = mAccounts;
if (accounts == null) {
// not ready yet
if (isLoggable) {
Log.v(TAG, "scheduleSync: no accounts yet, dropping");
}
return;
}
if (accounts.length == 0) {
if (isLoggable) {
Log.v(TAG, "scheduleSync: no accounts configured, dropping");
}
setStatusText("No accounts are configured.");
return;
}
}
final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
final boolean force = extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
int source;
if (uploadOnly) {
source = Sync.History.SOURCE_LOCAL;
} else if (force) {
source = Sync.History.SOURCE_USER;
} else if (url == null) {
source = Sync.History.SOURCE_POLL;
} else {
// this isn't strictly server, since arbitrary callers can (and do) request
// a non-forced two-way sync on a specific url
source = Sync.History.SOURCE_SERVER;
}
List<String> names = new ArrayList<String>();
List<ProviderInfo> providers = new ArrayList<ProviderInfo>();
populateProvidersList(url, names, providers);
final int numProviders = providers.size();
for (int i = 0; i < numProviders; i++) {
if (!providers.get(i).isSyncable) continue;
final String name = names.get(i);
for (String account : accounts) {
scheduleSyncOperation(new SyncOperation(account, source, name, extras, delay));
// TODO: remove this when Calendar supports multiple accounts. Until then
// pretend that only the first account exists when syncing calendar.
if ("calendar".equals(name)) {
break;
}
}
}
| public void | scheduleSyncOperation(android.content.SyncManager$SyncOperation syncOperation)Create and schedule a SyncOperation.
// If this operation is expedited and there is a sync in progress then
// reschedule the current operation and send a cancel for it.
final boolean expedited = syncOperation.delay < 0;
final ActiveSyncContext activeSyncContext = mActiveSyncContext;
if (expedited && activeSyncContext != null) {
final boolean activeIsExpedited = activeSyncContext.mSyncOperation.delay < 0;
final boolean hasSameKey =
activeSyncContext.mSyncOperation.key.equals(syncOperation.key);
// This request is expedited and there is a sync in progress.
// Interrupt the current sync only if it is not expedited and if it has a different
// key than the one we are scheduling.
if (!activeIsExpedited && !hasSameKey) {
rescheduleImmediately(activeSyncContext.mSyncOperation);
sendSyncFinishedOrCanceledMessage(activeSyncContext,
null /* no result since this is a cancel */);
}
}
boolean operationEnqueued;
synchronized (mSyncQueue) {
operationEnqueued = mSyncQueue.add(syncOperation);
}
if (operationEnqueued) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation);
}
sendCheckAlarmsMessage();
} else {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation "
+ syncOperation);
}
}
| private void | scheduleSyncPollAlarm(long relativeAlarmTime)
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "scheduleSyncPollAlarm: relativeAlarmTime is " + relativeAlarmTime
+ ", now is " + SystemClock.elapsedRealtime()
+ ", delay is " + (relativeAlarmTime - SystemClock.elapsedRealtime()));
}
ensureAlarmService();
mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, relativeAlarmTime,
mSyncPollAlarmIntent);
| private void | sendCheckAlarmsMessage()
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS");
mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS);
| private void | sendSyncAlarmMessage()
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM");
mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM);
| private void | sendSyncFinishedOrCanceledMessage(android.content.SyncManager$ActiveSyncContext syncContext, SyncResult syncResult)
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_FINISHED");
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
msg.obj = new SyncHandlerMessagePayload(syncContext, syncResult);
mSyncHandler.sendMessage(msg);
| private void | setStatusText(java.lang.String message)
mStatusText = message;
| public void | startSync(android.net.Uri url, android.os.Bundle extras)Initiate a sync for this given URL, or pass null for a full sync.
You'll start getting callbacks after this.
scheduleSync(url, extras, 0 /* no delay */);
| public void | updateHeartbeatTime()
mHeartbeatTime = SystemClock.elapsedRealtime();
ensureContentResolver();
mContentResolver.notifyChange(Sync.Active.CONTENT_URI,
null /* this change wasn't made through an observer */);
| private void | writeSyncPollTime(long when)
File f = new File(SYNCMANAGER_PREFS_FILENAME);
DataOutputStream str = null;
try {
str = new DataOutputStream(new FileOutputStream(f));
str.writeLong(when);
} catch (FileNotFoundException e) {
Log.w(TAG, "error writing to file " + f, e);
} catch (IOException e) {
Log.w(TAG, "error writing to file " + f, e);
} finally {
if (str != null) {
try {
str.close();
} catch (IOException e) {
Log.w(TAG, "error closing file " + f, e);
}
}
}
|
|