/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* src/com/vodafone360/people/VODAFONE.LICENSE.txt or
* http://github.com/360/360-Engine-for-Android
* See the License for the specific language governing permissions and
* limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each file and
* include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the fields
* enclosed by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2010 Vodafone Sales & Services Ltd. All rights reserved.
* Use is subject to license terms.
*/
package com.vodafone360.people.engine.contactsync;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import com.vodafone360.people.ApplicationCache;
import com.vodafone360.people.Settings;
import com.vodafone360.people.database.DatabaseHelper;
import com.vodafone360.people.database.DatabaseHelper.DatabaseChangeType;
import com.vodafone360.people.datatypes.BaseDataType;
import com.vodafone360.people.datatypes.PushEvent;
import com.vodafone360.people.engine.BaseEngine;
import com.vodafone360.people.engine.EngineManager;
import com.vodafone360.people.engine.EngineManager.EngineId;
import com.vodafone360.people.engine.IEngineEventCallback;
import com.vodafone360.people.engine.content.ThumbnailHandler;
import com.vodafone360.people.service.PersistSettings;
import com.vodafone360.people.service.ServiceStatus;
import com.vodafone360.people.service.ServiceUiRequest;
import com.vodafone360.people.service.agent.NetworkAgent;
import com.vodafone360.people.service.agent.NetworkAgent.AgentState;
import com.vodafone360.people.service.agent.UiAgent;
import com.vodafone360.people.service.io.ResponseQueue;
import com.vodafone360.people.service.io.ResponseQueue.DecodedResponse;
import com.vodafone360.people.utils.LogUtils;
import com.vodafone360.people.utils.VersionUtils;
/**
* Implementation of engine handling Contact-sync. Contact sync is a multi-stage
* process, involving sync of contacts from the native database, sync of server
* contacts, fetching of groups and thumbnails. Each phase is handled by a
* separate processor created and managed by the Contact sync engine.
*/
public class ContactSyncEngine extends BaseEngine implements IContactSyncCallback,
NativeContactsApi.ContactsObserver {
/**
* Definition of states for Contact sync.
*/
public static enum State {
IDLE,
FETCHING_NATIVE_CONTACTS,
UPDATING_NATIVE_CONTACTS,
FETCHING_SERVER_CONTACTS,
UPDATING_SERVER_CONTACTS,
}
/**
* Defines the contact sync mode. The mode determines the sequence in which
* the contact sync processors are run.
*/
public static enum Mode {
NONE,
FULL_SYNC_FIRST_TIME,
SERVER_SYNC,
THUMBNAIL_SYNC,
FETCH_NATIVE_SYNC,
UPDATE_NATIVE_SYNC
}
/**
* Mutex for thread synchronization
*/
private final Object mMutex = new Object();
private final UiAgent mUiAgent = mEventCallback.getUiAgent();
private final ApplicationCache mCache = mEventCallback.getApplicationCache();
/** The last known status of the contacts sync. */
private ServiceStatus mLastStatus = ServiceStatus.SUCCESS;
/**
* Observer interface allowing interested parties to receive notification of
* changes in Contact sync state.
*/
public static interface IContactSyncObserver {
/**
* Called a contact sync finishes.
*
* @param status SUCCESS if the sync was successful, a suitable error
* code otherwise.
*/
void onSyncComplete(ServiceStatus status);
/**
* Called when the contact sync engine changes state or mode
*
* @param mode Current mode
* @param oldState Previous state
* @param newState New state
*/
void onContactSyncStateChange(Mode mode, State oldState, State newState);
/**
* Called to update interested parties on contact sync progress. This is
* made up of two parts the state and the percentage. Each time the
* state changes the percentage value will go back to 0.
*
* @param currentState What the contact sync engine is currently doing
* @param percent Percentage complete for the current task
*/
void onProgressEvent(State currentState, int percent);
}
/**
* Number of retries when first time sync fails
*/
private static final long FULL_SYNC_RETRIES = 3;
/**
* Counter for first time sync failures
*/
private int mServerSyncRetryCount = 0;
/**
* Current state of the contact sync engine (determines which processor is
* currently active)
*/
private State mState = State.IDLE;
/**
* Last state of the contact sync engine (to check if engine was paused).
*/
private State mLastState = State.IDLE;
/**
* Current mode (or stragegy) the contact sync engine is in. The mode
* determines the order which the processors are run.
*/
private Mode mMode = Mode.NONE;
/**
* A failure list (currently containing unlocalised English strings) of
* contacts which could not be sync'ed to the server.
*/
private String mFailureList;
/**
* Database changed flag. Will be set to true if at any stage of the contact
* sync the NowPlus database is changed.
*/
private boolean mDatabaseChanged;
/**
* Last time the database was updated (in milliseconds)
*/
private Long mLastDbUpdateTime;
/**
* DatabaseHelper object used for accessing NowPlus database.
*/
private DatabaseHelper mDb;
/**
* Currently active processor (the processor which is running) or null
*/
private BaseSyncProcessor mActiveProcessor;
/**
* The factory class which is used for creating processors for a particular
* state.
*/
private ProcessorFactory mProcessorFactory;
/**
* If a contacts sync is triggered by the Contact tab, we should wait a
* little bit of time before beginning any heavy background work. This
* will give the main thread a chance to render its UI.
*/
private static final long UI_PING_SYNC_DELAY = 3 * 1000L;
/**
* Time to wait after the user modifies a contact before a contact sync with
* the server will be initiated (in milliseconds). The timer will be reset
* each time a modification takes place.
*/
private static final long SERVER_CONTACT_SYNC_TIMEOUT_MS = 30000L;
/**
* Time to wait after the user modifies a contact before a contact sync with
* the native database will be initiated (in milliseconds). The timer will
* be reset each time a modification takes place.
*/
private static final long NATIVE_CONTACT_SYNC_TIMEOUT_MS = 30000L;
/**
* The time to wait before requesting a new server sync when the user is
* using the application.
*/
private static final long USER_ACTIVITY_SERVER_SYNC_TIMEOUT_MS = 20 * 60 * 1000;
/**
* Determines the time that should be waited between publishing database
* change events. This is to prevent the UI updating too frequently during a
* contact sync. The time is specified in nanoseconds.
*/
private static final long UI_REFRESH_WAIT_TIME_NANO = 5000000000L;
private static final long SYNC_ERROR_WAIT_TIMEOUT = 300000L;
/**
* Specifies the time that a server sync should be started relative to
* current time in milliseconds. Will be NULL when a server sync timeout is
* not required.
*/
private volatile Long mServerSyncTimeout;
/**
* Specifies the time that a fetch native sync should be started relative to
* current time in milliseconds. Will be NULL when a fetch native sync
* timeout is not required.
*/
private volatile Long mFetchNativeSyncTimeout;
/**
* Specifies the time that a update native sync should be started relative
* to current time in milliseconds. Will be NULL when a update native sync
* timeout is not required.
*/
private volatile Long mUpdateNativeSyncTimeout;
/**
* Keeps track of the last time a server sync happened.
*/
private long mLastServerSyncTime = 0L;
/**
* Flag which matches the persisted equivalent in the NowPlus database state
* table. Will be set to true when the first time sync is completed and will
* remain true until a remove user data is performed.
*/
private boolean mFirstTimeSyncComplete;
/**
* Flag which matches the persisted equivalent in the NowPlus database state
* table. Will be set to true when the first time sync is started and will
* remain true until a remove user data is performed.
*/
private boolean mFirstTimeSyncStarted;
/**
* Flag which matches the persisted equivalent in the NowPlus database state
* table. Will be set to true when the part of the first time sync to fetch
* native contacts is started and will remain true until a remove user data
* is performed. Once this flag has been set to true, the next time a full
* sync is started the full sync normal mode will be used instead of full
* sync first time.
*/
private volatile boolean mFirstTimeNativeSyncComplete;
/**
* True if a server sync should be started as soon as possible
*/
private boolean mServerSyncRequired;
/**
* True if a native fetch sync should be started as soon as possible
*/
private boolean mNativeFetchSyncRequired;
/**
* True if a native update sync should be started as soon as possible
*/
private boolean mNativeUpdateSyncRequired;
/**
* True if a server thumbnail (avatar) sync should be started as soon as
* possible
*/
private boolean mThumbnailSyncRequired;
/**
* Maintains a list of contact sync observers
*/
private final ArrayList<IContactSyncObserver> mEventCallbackList = new ArrayList<IContactSyncObserver>();
/**
* Current progress value (used to check if the progress has changed)
*/
private int mCurrentProgressPercent = 0;
/**
* Flag which is set when the current processor changes the database
*/
private boolean mDbChangedByProcessor;
/**
* Backup of the previous active request before processing the new one.
*/
private ServiceUiRequest mActiveUiRequestBackup = null;
/**
* Native Contacts API access. The appropriate version should be used
* depending on the SDK.
*/
private final NativeContactsApi mNativeContactsApi = NativeContactsApi.getInstance();
/**
* True if changes on native contacts shall be detected.
*/
private final boolean mFetchNativeContactsOnChange;
/**
* True if native contacts shall be fetched from native.
*/
private final boolean mFetchNativeContacts;
/**
* True if changes on 360 contacts shall be forwarded to native contacts.
*/
private final boolean mUpdateNativeContacts;
/**
* WakeLock to be used during full sync.
*/
private PowerManager.WakeLock mWakeLock = null;
/**
* Service Context.
*/
private Context mContext = null;
/**
* Check if sync is paused.
*/
private boolean mIsSyncPaused = false;
/**
* Copy paused state in mIsSyncPausedLast to resume sync in engine's run method.
*/
private boolean mIsSyncPausedLast = false;
/**
* Used to listen for NowPlus database change events. Such events will be
* received when the user modifies a contact in the people application.
*/
private final Handler mDbChangeHandler = new Handler() {
/**
* Processes a database change event
*/
@Override
public void handleMessage(Message msg) {
processDbMessage(msg);
}
};
/**
* ContactSyncEngine constructor.
*
* @param eventCallback Engine-event callback interface allowing engine to
* report back to client on request completion.
* @param context Context.
* @param db Handle to People database.
* @param processorFactory the processor factory
*/
public ContactSyncEngine(Context context, IEngineEventCallback eventCallback, DatabaseHelper db,
ProcessorFactory factory) {
super(eventCallback);
mDb = db;
mEngineId = EngineId.CONTACT_SYNC_ENGINE;
mContext = context;
final boolean enableNativeSync = VersionUtils.is2XPlatform() || !Settings.DISABLE_NATIVE_SYNC_AFTER_IMPORT_ON_ANDROID_1X;
mFetchNativeContactsOnChange = Settings.ENABLE_FETCH_NATIVE_CONTACTS_ON_CHANGE && enableNativeSync;
mFetchNativeContacts = Settings.ENABLE_FETCH_NATIVE_CONTACTS && enableNativeSync;
mUpdateNativeContacts = Settings.ENABLE_UPDATE_NATIVE_CONTACTS && enableNativeSync;
// use standard processor factory if provided one is null
mProcessorFactory = (factory != null) ? factory : new DefaultProcessorFactory();
}
/**
* Called after the engine has been created to do some extra initialisation.
*/
@Override
public void onCreate() {
mDb.addEventCallback(mDbChangeHandler);
PersistSettings setting1 = mDb.fetchOption(PersistSettings.Option.FIRST_TIME_SYNC_STARTED);
PersistSettings setting2 = mDb.fetchOption(PersistSettings.Option.FIRST_TIME_SYNC_COMPLETE);
PersistSettings setting3 = mDb.fetchOption(PersistSettings.Option.FIRST_TIME_NATIVE_SYNC_COMPLETE);
if (setting1 != null) {
mFirstTimeSyncStarted = setting1.getFirstTimeSyncStarted();
}
if (setting2 != null) {
mFirstTimeSyncComplete = setting2.getFirstTimeSyncComplete();
}
if (setting3 != null) {
mFirstTimeNativeSyncComplete = setting3.getFirstTimeNativeSyncComplete();
}
LogUtils.logI("ContactSyncEngine.onCreate() " +
"[mFirstTimeSyncStarted==" + mFirstTimeSyncStarted +
", mFirstTimeSyncComplete==" + mFirstTimeSyncComplete +
", mFirstTimeNativeSyncComplete==" + mFirstTimeNativeSyncComplete + "]");
if (mFetchNativeContactsOnChange) {
mNativeContactsApi.registerObserver(this);
}
if (mFirstTimeSyncComplete) {
// native sync shall be performed only if the first time sync has
// been completed
startUpdateNativeContactSyncTimer();
startFetchNativeContactSyncTimer();
}
}
/**
* Called just before engine is about to be closed. Cleans up resources.
*/
@Override
public void onDestroy() {
if (mFetchNativeContactsOnChange) {
mNativeContactsApi.unregisterObserver();
}
mDb.removeEventCallback(mDbChangeHandler);
}
/**
* Triggers a full contact sync from the UI (via the service interface).
* Will start a first time sync if necessary, otherwise a normal full sync
* will be executed. A {@link ServiceUiRequest#NOWPLUSSYNC} event will be
* sent to notify the UI when the sync has completed.
*/
public void addUiStartFullSync() {
// acquire wake lock before full contact sync is started.
acquireSyncLock();
// reset last status to enable synchronization of contacts again
mLastStatus = ServiceStatus.SUCCESS;
LogUtils.logI("ContactSyncEngine.addUiStartFullSync()");
emptyUiRequestQueue();
addUiRequestToQueue(ServiceUiRequest.NOWPLUSSYNC, null);
}
/**
* Tells the ContactSyncEngine that the user is actively using the service
* and adjust sync timeout accordingly. Note: A server sync should occur
* every 20 minutes during application intensive usage or immediately if the
* application is used again after sleeping more than 20 minutes.
*/
public void pingUserActivity() {
LogUtils.logI("ContactSyncEngine.pingUserActivity()");
long delay;
synchronized (this) {
final long currentDelay = System.currentTimeMillis() - mLastServerSyncTime;
if ((mMode == Mode.FULL_SYNC_FIRST_TIME || mMode == Mode.SERVER_SYNC)
&& mState != State.IDLE) {
// Already performing a sync, scheduling a new one in
// USER_ACTIVITY_SERVER_SYNC_TIMEOUT_MS
delay = USER_ACTIVITY_SERVER_SYNC_TIMEOUT_MS;
} else if (currentDelay >= USER_ACTIVITY_SERVER_SYNC_TIMEOUT_MS) {
// Last sync timeout has passed, schedule a new one now
delay = UI_PING_SYNC_DELAY;
} else if ((currentDelay < USER_ACTIVITY_SERVER_SYNC_TIMEOUT_MS)
&& (mServerSyncTimeout == null)) {
// Last sync timeout has not passed but no new one is scheduled,
// schedule one to happen accordingly with the timeout
delay = USER_ACTIVITY_SERVER_SYNC_TIMEOUT_MS - currentDelay;
} else {
// Nothing to do, a timeout will trigger the new sync
LogUtils.logD("A new sync is already scheduled in "
+ (USER_ACTIVITY_SERVER_SYNC_TIMEOUT_MS - currentDelay) + " milliseconds");
return;
}
}
LogUtils.logD("Scheduling a new sync in " + delay + " milliseconds");
addUiRequestToQueue(ServiceUiRequest.NOWPLUSSYNC, delay);
}
/**
* Determines if the first time contact sync has been completed.
*
* @return true if completed.
*/
public synchronized boolean isFirstTimeSyncComplete() {
return mFirstTimeSyncComplete;
}
/**
* Add observer of Contact-sync.
*
* @param observer IContactSyncObserver handle.
*/
public synchronized void addEventCallback(IContactSyncObserver observer) {
if (!mEventCallbackList.contains(observer)) {
mEventCallbackList.add(observer);
}
}
/**
* Starts a timer to trigger a server contact sync in a short while
* (normally around 30 seconds).
*/
private void startServerContactSyncTimer(long delay) {
if (!Settings.ENABLE_SERVER_CONTACT_SYNC) {
return;
}
synchronized (this) {
if (mServerSyncTimeout == null) {
LogUtils
.logI("ContactSyncEngine - will sync contacts with server shortly... (in about "
+ delay + " milliseconds)");
}
mServerSyncTimeout = System.currentTimeMillis() + delay;
if (mCurrentTimeout == null || mCurrentTimeout.compareTo(mServerSyncTimeout) > 0) {
mCurrentTimeout = mServerSyncTimeout;
}
}
if (mCurrentTimeout.equals(mServerSyncTimeout)) {
mEventCallback.kickWorkerThread();
}
}
/**
* Starts a timer to trigger a fetch native contact sync in a short while
* (normally around 30 seconds).
*/
private void startFetchNativeContactSyncTimer() {
if (!mFetchNativeContacts) {
return;
}
synchronized (this) {
if (mFetchNativeSyncTimeout == null) {
LogUtils.logI("ContactSyncEngine - will fetch native contacts shortly...");
}
mFetchNativeSyncTimeout = System.currentTimeMillis() + NATIVE_CONTACT_SYNC_TIMEOUT_MS;
if (mCurrentTimeout == null || mCurrentTimeout.compareTo(mFetchNativeSyncTimeout) > 0) {
mCurrentTimeout = mFetchNativeSyncTimeout;
mEventCallback.kickWorkerThread();
}
}
}
/**
* Starts a timer to trigger a update native contact sync in a short while
* (normally around 30 seconds).
*/
private void startUpdateNativeContactSyncTimer() {
if (!mUpdateNativeContacts) {
return;
}
synchronized (this) {
if (mUpdateNativeSyncTimeout == null) {
LogUtils.logI("ContactSyncEngine - will update native contacts shortly...");
}
mUpdateNativeSyncTimeout = System.currentTimeMillis() + NATIVE_CONTACT_SYNC_TIMEOUT_MS;
if (mCurrentTimeout == null || mCurrentTimeout.compareTo(mUpdateNativeSyncTimeout) > 0) {
mCurrentTimeout = mUpdateNativeSyncTimeout;
}
}
if (mCurrentTimeout.equals(mUpdateNativeSyncTimeout)) {
mEventCallback.kickWorkerThread();
}
}
/**
* Helper function to start a processor running.
*
* @param processor Processor which was created by processor factory.
*/
private void startProcessor(BaseSyncProcessor processor) {
if (mActiveProcessor != null) {
LogUtils.logE("ContactSyncEngine.startProcessor - Cannot start " + processor.getClass()
+ ", because the processor " + mActiveProcessor.getClass() + " is running");
throw new RuntimeException(
"ContactSyncEngine - Cannot start processor while another is active");
}
mActiveProcessor = processor;
mCurrentProgressPercent = -1;
mDbChangedByProcessor = false;
processor.start();
}
/**
* Framework function to determine when the contact sync engine next needs
* to run.
*
* @return -1 if the engine does not need to run 0 if the engine needs to
* run as soon as possible x where x > 0, the engine needs to run
* when current time in milliseconds >= x
*/
@Override
public long getNextRunTime() {
if (mLastStatus != ServiceStatus.SUCCESS) {
return getCurrentTimeout();
}
if (isCommsResponseOutstanding()) {
return 0;
}
if (isUiRequestOutstanding() && mActiveUiRequest == null) {
return 0;
}
if (readyToStartServerSync()) {
if (mServerSyncRequired || mThumbnailSyncRequired) {
return 0;
} else if (mFirstTimeSyncStarted && !mFirstTimeSyncComplete
&& mServerSyncRetryCount < FULL_SYNC_RETRIES) {
mServerSyncRetryCount++;
mServerSyncRequired = true;
return 0;
}
}
if (mNativeFetchSyncRequired && readyToStartFetchNativeSync()) {
return 0;
}
if (mNativeUpdateSyncRequired && readyToStartUpdateNativeSync()) {
return 0;
}
return getCurrentTimeout();
}
/**
* Called by framework when {@link #getNextRunTime()} reports that the
* engine needs to run, to carry out the next task. Each task should not
* take longer than a second to complete.
*/
@Override
public void run() {
// Pause contact sync engine.
if (mIsSyncPaused) {
return;
}
if (mIsSyncPausedLast) {
mIsSyncPausedLast = false;
if (mFirstTimeSyncComplete) {
startServerSync();
} else {
resumeFirstTimeSync();
}
return;
}
if (processTimeout()) {
return;
}
if (isUiRequestOutstanding()) {
mActiveUiRequestBackup = mActiveUiRequest;
if (processUiQueue()) {
return;
}
}
if (isCommsResponseOutstanding() && processCommsInQueue()) {
return;
}
if (readyToStartServerSync()) {
if (mThumbnailSyncRequired) {
startThumbnailSync();
return;
}
if (mServerSyncRequired) {
startServerSync();
return;
}
}
if (mNativeFetchSyncRequired && readyToStartFetchNativeSync()) {
startFetchNativeSync();
return;
}
if (mNativeUpdateSyncRequired && readyToStartUpdateNativeSync()) {
startUpdateNativeSync();
return;
}
}
/**
* Called by base class when a contact sync UI request has been completed.
* Not currently used.
*/
@Override
protected void onRequestComplete() {
}
/**
* Called by base class when a timeout has been completed. If there is an
* active processor the timeout event will be passed to it, otherwise the
* engine will check if it needs to schedule a new sync and set the next
* pending timeout.
*/
@Override
protected void onTimeoutEvent() {
if (mActiveProcessor != null) {
mActiveProcessor.onTimeoutEvent();
} else {
startSyncIfRequired();
setTimeoutIfRequired();
}
}
/**
* Based on current timeout values schedules a new sync if required.
*/
private void startSyncIfRequired() {
if (mFirstTimeSyncStarted && !mFirstTimeSyncComplete) {
mServerSyncRequired = true;
mServerSyncRetryCount = 0;
}
long currentTimeMs = System.currentTimeMillis();
if (mServerSyncTimeout != null && mServerSyncTimeout.longValue() < currentTimeMs) {
mServerSyncRequired = true;
mServerSyncTimeout = null;
} else if (mFetchNativeSyncTimeout != null
&& mFetchNativeSyncTimeout.longValue() < currentTimeMs) {
mNativeFetchSyncRequired = true;
mFetchNativeSyncTimeout = null;
} else if (mUpdateNativeSyncTimeout != null
&& mUpdateNativeSyncTimeout.longValue() < currentTimeMs) {
mNativeUpdateSyncRequired = true;
mUpdateNativeSyncTimeout = null;
}
}
/**
* Called when a response to a request or a push message is received from
* the server. Push messages are processed by the engine, responses are
* passed to the active processor.
*
* @param resp Response or push message received
*/
@Override
protected void processCommsResponse(DecodedResponse resp) {
if (processPushEvent(resp)) {
return;
}
if (resp.mDataTypes != null && resp.mDataTypes.size() > 0) {
LogUtils.logD("ContactSyncEngine.processCommsResponse: Req ID = " + resp.mReqId
+ ", type = " + resp.mDataTypes.get(0).getType());
} else {
LogUtils.logD("ContactSyncEngine.processCommsResponse: Req ID = " + resp.mReqId
+ ", type = NULL");
}
if (mActiveProcessor != null) {
mActiveProcessor.processCommsResponse(resp);
}
}
/**
* Determines if a given response is a push message and processes in this
* case TODO: we need the check for Me Profile be migrated to he new engine
*
* @param resp Response to check and process
* @return true if the response was processed
*/
private boolean processPushEvent(DecodedResponse resp) {
if (resp.mDataTypes == null || resp.mDataTypes.size() == 0) {
return false;
}
BaseDataType dataType = resp.mDataTypes.get(0);
if ((dataType == null) || dataType.getType() != BaseDataType.PUSH_EVENT_DATA_TYPE) {
return false;
}
PushEvent pushEvent = (PushEvent)dataType;
LogUtils.logV("Push Event Type = " + pushEvent.mMessageType);
switch (pushEvent.mMessageType) {
case CONTACTS_CHANGE:
LogUtils.logI("ContactSyncEngine.processCommsResponse - Contacts changed push message received");
mServerSyncRequired = true;
// fetch the newest groups
EngineManager.getInstance().getGroupsEngine().addUiGetGroupsRequest();
mEventCallback.kickWorkerThread();
break;
case SYSTEM_NOTIFICATION:
LogUtils.logI("ContactSyncEngine.processCommsResponse - System notification push message received");
break;
default:
// do nothing.
break;
}
return true;
}
/**
* Called by base class to process a NOWPLUSSYNC UI request. This will be
* called to process a full sync or server sync UI request.
*
* @param requestId ID of the request to process, only
* ServiceUiRequest.NOWPLUSSYNC is currently supported.
* @param data Type is determined by request ID, in case of NOWPLUSSYNC this
* is a flag which determines if a full sync is required (true =
* full sync, false = server sync).
*/
@Override
protected void processUiRequest(ServiceUiRequest requestId, Object data) {
switch (requestId) {
case NOWPLUSSYNC:
if (data == null) {
clearCurrentSyncAndPatchBaseEngine();
mServerSyncRetryCount = 0;
startServerSync();
}
else {
startServerContactSyncTimer((Long) data);
}
break;
default:
// do nothing.
break;
}
}
/**
* Clears the current sync and make sure that if we cancel a previous sync,
* it doesn't notify a wrong UI request. TODO: Find another way to not have
* to hack the BaseEngine!
*/
private void clearCurrentSyncAndPatchBaseEngine() {
// Cancel background sync
if (mActiveProcessor != null) {
// the mActiveUiRequest is already the new one so if
// onCompleteUiRequest(Error) is called,
// this will reset it to null even if we didn't start to process it.
ServiceUiRequest newActiveUiRequest = mActiveUiRequest;
mActiveUiRequest = mActiveUiRequestBackup;
mActiveProcessor.cancel();
// cancelSync();
// restore the active UI request...
mActiveUiRequest = newActiveUiRequest;
mActiveProcessor = null;
}
newState(State.IDLE);
}
/**
* Checks if a server sync can be started based on network conditions and
* engine state
*
* @return true if a sync can be started, false otherwise.
*/
private boolean readyToStartServerSync() {
if (!Settings.ENABLE_SERVER_CONTACT_SYNC) {
return false;
}
if (!EngineManager.getInstance().getSyncMeEngine().isFirstTimeMeSyncComplete()) {
return false;
}
if (!mFirstTimeSyncStarted) {
return false;
}
if (mState != State.IDLE || NetworkAgent.getAgentState() != AgentState.CONNECTED) {
return false;
}
return true;
}
/**
* Checks if a fetch native sync can be started based on network conditions
* and engine state
*
* @return true if a sync can be started, false otherwise.
*/
private boolean readyToStartFetchNativeSync() {
if (!mFetchNativeContacts) {
return false;
}
if (!mFirstTimeSyncStarted) {
return false;
}
if (mState != State.IDLE) {
return false;
}
return true;
}
/**
* Checks if a update native sync can be started based on network conditions
* and engine state
*
* @return true if a sync can be started, false otherwise.
*/
private boolean readyToStartUpdateNativeSync() {
if (!mUpdateNativeContacts) {
return false;
}
if (!mFirstTimeSyncStarted) {
return false;
}
if (mState != State.IDLE) {
return false;
}
return true;
}
/**
* This is added to support resuming of contact sync from where it is paused during first time sync.
*/
public void resumeFirstTimeSync() {
mFailureList = "";
mDatabaseChanged = false;
mServerSyncTimeout = null;
mServerSyncRequired = false;
setFirstTimeSyncStarted(true);
mMode = Mode.FULL_SYNC_FIRST_TIME;
nextTaskFullSyncFirstTime();
}
/**
* Starts a full sync. If the native contacts haven't yet been fetched then
* a first time sync will be started, otherwise will start a normal full
* sync. Full syncs are always initiated from the UI (via UI request).
*/
public void startServerSync() {
mFailureList = "";
mDatabaseChanged = false;
mServerSyncTimeout = null;
mServerSyncRequired = false;
setFirstTimeSyncStarted(true);
if (mFirstTimeNativeSyncComplete) {
LogUtils.logI("ContactSyncEngine.startServerSync - server sync");
mMode = Mode.SERVER_SYNC;
nextTaskServerSync();
} else {
LogUtils.logI("ContactSyncEngine.startServerSync - first time full sync");
mMode = Mode.FULL_SYNC_FIRST_TIME;
nextTaskFullSyncFirstTime();
}
}
/**
* Starts a background thumbnail sync
*/
private void startThumbnailSync() {
mThumbnailSyncRequired = false;
mFailureList = "";
mDatabaseChanged = false;
mMode = Mode.THUMBNAIL_SYNC;
nextTaskThumbnailSync();
}
/**
* Starts a background fetch native contacts sync
*/
private void startFetchNativeSync() {
mNativeFetchSyncRequired = false;
mFailureList = "";
mDatabaseChanged = false;
mMode = Mode.FETCH_NATIVE_SYNC;
mFetchNativeSyncTimeout = null;
setTimeoutIfRequired();
nextTaskFetchNativeContacts();
}
/**
* Starts a background update native contacts sync
*/
private void startUpdateNativeSync() {
mNativeUpdateSyncRequired = false;
mFailureList = "";
mDatabaseChanged = false;
mMode = Mode.UPDATE_NATIVE_SYNC;
mUpdateNativeSyncTimeout = null;
setTimeoutIfRequired();
nextTaskUpdateNativeContacts();
}
/**
* Helper function to start the fetch native contacts processor
*
* @param isFirstTimeSync true if importing native contacts for the first time
* @return if this type of sync is enabled in the settings, false otherwise.
*/
private boolean startFetchNativeContacts(boolean isFirstTimeSync) {
if (mFetchNativeContacts || (Settings.ENABLE_FETCH_NATIVE_CONTACTS && isFirstTimeSync)) {
newState(State.FETCHING_NATIVE_CONTACTS);
startProcessor(mProcessorFactory.create(ProcessorFactory.FETCH_NATIVE_CONTACTS, this, mDb));
return true;
}
return false;
}
/**
* Helper function to start the update native contacts processor
*
* @return if this type of sync is enabled in the settings, false otherwise.
*/
private boolean startUpdateNativeContacts() {
if (mUpdateNativeContacts) {
newState(State.UPDATING_NATIVE_CONTACTS);
startProcessor(mProcessorFactory.create(ProcessorFactory.UPDATE_NATIVE_CONTACTS, this, mDb));
return true;
}
return false;
}
/**
* Helper function to start the download server contacts processor
*
* @return if this type of sync is enabled in the settings, false otherwise.
*/
private boolean startDownloadServerContacts() {
if (Settings.ENABLE_SERVER_CONTACT_SYNC) {
newState(State.FETCHING_SERVER_CONTACTS);
startProcessor(mProcessorFactory.create(ProcessorFactory.DOWNLOAD_SERVER_CONTACTS, this, mDb));
return true;
}
else
return false;
}
/**
* Helper function to start the upload server contacts processor
*
* @return if this type of sync is enabled in the settings, false otherwise.
*/
private boolean startUploadServerContacts() {
if (Settings.ENABLE_SERVER_CONTACT_SYNC) {
newState(State.UPDATING_SERVER_CONTACTS);
startProcessor(mProcessorFactory.create(ProcessorFactory.UPLOAD_SERVER_CONTACTS, this, mDb));
return true;
}
else
return false;
}
/**
* Helper function to start the download thumbnails processor
*
* @return if this type of sync is enabled in the settings, false otherwise.
*/
private boolean startDownloadServerThumbnails() {
if (Settings.ENABLE_THUMBNAIL_SYNC) {
ThumbnailHandler.getInstance().downloadContactThumbnails();
return true;
}
else
return false;
}
/**
* Called by a processor when it has completed. Will move to the next task.
* When the active contact sync has totally finished, will complete any
* pending UI request.
*
* @param status Status of the sync from the processor, any error codes will
* stop the sync.
* @param failureList Contains a list of sync failure information which can
* be used as a summary at the end. Otherwise should be an empty
* string.
* @param data Any processor specific data to pass back to the engine. Not
* currently used.
*/
@Override
public void onProcessorComplete(ServiceStatus status, String failureList, Object data) {
if (mState == State.IDLE) {
return;
}
if (mActiveProcessor != null) {
mActiveProcessor.onComplete();
}
mActiveProcessor = null;
mFailureList += failureList;
if (status != ServiceStatus.SUCCESS) {
LogUtils.logE("ContactSyncEngine.onProcessorComplete - Failed during " + mState
+ " with error " + status);
completeSync(status);
return;
}
if (mDbChangedByProcessor) {
switch (mState) {
case FETCHING_NATIVE_CONTACTS:
mServerSyncRequired = true;
break;
case FETCHING_SERVER_CONTACTS:
mThumbnailSyncRequired = true;
if (mUpdateNativeContacts) {
mNativeUpdateSyncRequired = true;
}
break;
default:
// Do nothing.
break;
}
}
switch (mMode) {
case FULL_SYNC_FIRST_TIME:
nextTaskFullSyncFirstTime();
break;
case SERVER_SYNC:
nextTaskServerSync();
break;
case FETCH_NATIVE_SYNC:
nextTaskFetchNativeContacts();
break;
case UPDATE_NATIVE_SYNC:
nextTaskUpdateNativeContacts();
break;
case THUMBNAIL_SYNC:
nextTaskThumbnailSync();
break;
default:
LogUtils.logE("ContactSyncEngine.onProcessorComplete - Unexpected mode: " + mMode);
completeSync(ServiceStatus.ERROR_SYNC_FAILED);
}
}
/**
* Moves to the next state for the full sync first time mode, and runs the
* appropriate processor. Completes the UI request when the sync is complete
* (if one is pending).
*/
private void nextTaskFullSyncFirstTime() {
switch (mState) {
case IDLE:
if (startFetchNativeContacts(true)) {
return;
}
// Fall through
case FETCHING_NATIVE_CONTACTS:
setFirstTimeNativeSyncComplete(true);
if (startUploadServerContacts()) {
return;
}
// Fall through
case UPDATING_SERVER_CONTACTS:
if (startDownloadServerContacts()) {
return;
}
// Fall through
case FETCHING_SERVER_CONTACTS:
mThumbnailSyncRequired = true;
mLastServerSyncTime = System.currentTimeMillis();
setFirstTimeSyncComplete(true);
completeSync(ServiceStatus.SUCCESS);
return;
default:
LogUtils.logE("ContactSyncEngine.nextTaskFullSyncFirstTime - Unexpected state: "
+ mState);
completeSync(ServiceStatus.ERROR_SYNC_FAILED);
}
}
/**
* Moves to the next state for the server sync mode, and runs the
* appropriate processor. Completes the UI request when the sync is complete
* (if one is pending).
*/
private void nextTaskServerSync() {
switch (mState) {
case IDLE:
if (startUploadServerContacts()) {
return;
}
// Fall through
case UPDATING_SERVER_CONTACTS:
if (startDownloadServerContacts()) {
return;
}
// Fall through
case FETCHING_SERVER_CONTACTS:
// force a thumbnail sync in case nothing in the database
// changed but we still have failing
// thumbnails that we should retry to download
mThumbnailSyncRequired = true;
mLastServerSyncTime = System.currentTimeMillis();
setFirstTimeSyncComplete(true);
completeSync(ServiceStatus.SUCCESS);
return;
default:
LogUtils.logE("ContactSyncEngine.nextTaskServerSync - Unexpected state: " + mState);
completeSync(ServiceStatus.ERROR_SYNC_FAILED);
}
}
/**
* Moves to the next state for the fetch native contacts mode, and runs the
* appropriate processor. Completes the UI request when the sync is complete
* (if one is pending).
*/
private void nextTaskFetchNativeContacts() {
switch (mState) {
case IDLE:
if (startFetchNativeContacts(false)) {
return;
}
// Fall through
case FETCHING_NATIVE_CONTACTS:
if (startUploadServerContacts()) {
return;
}
// Fall through
case UPDATING_SERVER_CONTACTS:
completeSync(ServiceStatus.SUCCESS);
return;
default:
LogUtils.logE("ContactSyncEngine.nextTaskFetchNativeContacts - Unexpected state: "
+ mState);
completeSync(ServiceStatus.ERROR_SYNC_FAILED);
}
}
/**
* Moves to the next state for the update native contacts mode, and runs the
* appropriate processor. Completes the UI request when the sync is complete
* (if one is pending).
*/
private void nextTaskUpdateNativeContacts() {
switch (mState) {
case IDLE:
if (startUpdateNativeContacts()) {
return;
}
// Fall through
case UPDATING_NATIVE_CONTACTS:
completeSync(ServiceStatus.SUCCESS);
return;
default:
LogUtils.logE("ContactSyncEngine.nextTaskUpdateNativeContacts - Unexpected state: "
+ mState);
completeSync(ServiceStatus.ERROR_SYNC_FAILED);
}
}
/**
* Moves to the next state for the thumbnail sync mode, and runs the
* appropriate processor. Completes the UI request when the sync is complete
* (if one is pending).
*/
private void nextTaskThumbnailSync() {
switch (mState) {
case IDLE:
if (startDownloadServerThumbnails()) {
return;
}
default:
LogUtils.logE("ContactSyncEngine.nextTaskThumbnailSync - Unexpected state: "
+ mState);
completeSync(ServiceStatus.ERROR_SYNC_FAILED);
}
}
/**
* Changes the state of the engine and informs the observers.
*
* @param newState The new state
*/
private void newState(State newState) {
mLastState = mState;
synchronized (mMutex) {
if (newState == mState) {
return;
}
mState = newState;
if (mState == State.IDLE) {
ApplicationCache.setSyncBusy(false);
} else {
ApplicationCache.setSyncBusy(true);
}
}
LogUtils.logI("ContactSyncEngine.newState: " + mLastState + " -> " + mState);
fireStateChangeEvent(mMode, mLastState, mState);
}
/**
* Called when the current mode has finished all the sync tasks. Completes
* the UI request if one is pending, sends an event to the observer and a
* database change event if necessary.
*
* @param status The overall status of the contact sync.
*/
private void completeSync(ServiceStatus status) {
// release wake lock acquired during full sync
releaseSyncLock();
if (mState == State.IDLE) {
return;
}
if (mDatabaseChanged) {
LogUtils.logD("ContactSyncEngine.completeSync - Firing Db changed event");
mDb.fireDatabaseChangedEvent(DatabaseChangeType.CONTACTS, true);
mDatabaseChanged = false;
}
mActiveProcessor = null;
newState(State.IDLE);
mMode = Mode.NONE;
completeUiRequest(status, mFailureList);
mCache.setSyncStatus(new SyncStatus(status));
mUiAgent.sendUnsolicitedUiEvent(ServiceUiRequest.UPDATE_SYNC_STATE, null);
if (mFirstTimeSyncComplete) {
fireSyncCompleteEvent(status);
}
if (ServiceStatus.SUCCESS == status) {
startSyncIfRequired();
} else {
setTimeout(SYNC_ERROR_WAIT_TIMEOUT);
}
mLastStatus = status;
setTimeoutIfRequired();
}
/**
* Sets the current timeout to the next pending timer and kicks the engine
* if necessary.
*/
private synchronized void setTimeoutIfRequired() {
Long initTimeout = mCurrentTimeout;
if (mCurrentTimeout == null
|| (mServerSyncTimeout != null && mServerSyncTimeout.compareTo(mCurrentTimeout) < 0)) {
mCurrentTimeout = mServerSyncTimeout;
}
if (mCurrentTimeout == null
|| (mFetchNativeSyncTimeout != null && mFetchNativeSyncTimeout
.compareTo(mCurrentTimeout) < 0)) {
mCurrentTimeout = mFetchNativeSyncTimeout;
}
if (mCurrentTimeout == null
|| (mUpdateNativeSyncTimeout != null && mUpdateNativeSyncTimeout
.compareTo(mCurrentTimeout) < 0)) {
mCurrentTimeout = mUpdateNativeSyncTimeout;
}
if (mCurrentTimeout != null && !mCurrentTimeout.equals(initTimeout)) {
mEventCallback.kickWorkerThread();
}
}
/**
* Called by the active processor to indicate that the NowPlus database has
* changed.
*/
@Override
public void onDatabaseChanged() {
mDatabaseChanged = true;
mDbChangedByProcessor = true;
final long currentTime = System.nanoTime();
if (mLastDbUpdateTime == null
|| mLastDbUpdateTime.longValue() + UI_REFRESH_WAIT_TIME_NANO < currentTime) {
LogUtils.logD("ContactSyncEngine.onDatabaseChanged - Updating UI...");
mDatabaseChanged = false;
mLastDbUpdateTime = currentTime;
mDb.fireDatabaseChangedEvent(DatabaseChangeType.CONTACTS, true);
}
}
/**
* Used by processors to fetch this engine. The BaseEngine reference is
* needed to send requests to the server.
*
* @return The BaseEngine reference of this engine.
*/
@Override
public BaseEngine getEngine() {
return this;
}
/**
* Used by active processor to set a timeout.
*
* @param timeout Timeout value based on current time in milliseconds
*/
@Override
public void setTimeout(long timeout) {
super.setTimeout(timeout);
}
/**
* Used by active processor to set the current progress.
*
* @param SyncStatus Status of the processor, must not be NULL.
* @throws InvalidParameterException when SyncStatus is NULL.
*/
@Override
public void setSyncStatus(final SyncStatus syncStatus) {
if (syncStatus == null) {
throw new InvalidParameterException(
"ContactSyncEngine.setSyncStatus() SyncStatus cannot be NULL");
}
/** Indicate that this is a first time sync in progress. **/
syncStatus.firstTimeSync(mMode == Mode.FULL_SYNC_FIRST_TIME);
mCache.setSyncStatus(syncStatus);
mUiAgent.sendUnsolicitedUiEvent(ServiceUiRequest.UPDATE_SYNC_STATE, null);
if (mState != State.IDLE && syncStatus.getProgress() != mCurrentProgressPercent) {
mCurrentProgressPercent = syncStatus.getProgress();
LogUtils.logI("ContactSyncEngine: Task " + mState + " is " + syncStatus.getProgress()
+ "% complete");
fireProgressEvent(mState, syncStatus.getProgress());
}
}
/**
* Called by active processor when issuing a request to store the request id
* in the base engine class.
*/
@Override
public void setActiveRequestId(int reqId) {
setReqId(reqId);
}
/**
* Helper function to update the database when the state of the
* {@link #mFirstTimeSyncStarted} flag changes.
*
* @param value New value to the flag. True indicates that first time sync
* has been started. The flag is never set to false again by the
* engine, it will be only set to false when a remove user data
* is done (and the database is deleted).
* @return SUCCESS or a suitable error code if the database could not be
* updated.
*/
private ServiceStatus setFirstTimeSyncStarted(boolean value) {
if (mFirstTimeSyncStarted == value) {
return ServiceStatus.SUCCESS;
}
PersistSettings setting = new PersistSettings();
setting.putFirstTimeSyncStarted(value);
ServiceStatus status = mDb.setOption(setting);
if (ServiceStatus.SUCCESS == status) {
synchronized (this) {
mFirstTimeSyncStarted = value;
}
}
return status;
}
/**
* Helper function to update the database when the state of the
* {@link #mFirstTimeSyncComplete} flag changes.
*
* @param value New value to the flag. True indicates that first time sync
* has been completed. The flag is never set to false again by
* the engine, it will be only set to false when a remove user
* data is done (and the database is deleted).
* @return SUCCESS or a suitable error code if the database could not be
* updated.
*/
private ServiceStatus setFirstTimeSyncComplete(boolean value) {
if (mFirstTimeSyncComplete == value) {
return ServiceStatus.SUCCESS;
}
PersistSettings setting = new PersistSettings();
setting.putFirstTimeSyncComplete(value);
ServiceStatus status = mDb.setOption(setting);
if (ServiceStatus.SUCCESS == status) {
synchronized (this) {
mFirstTimeSyncComplete = value;
}
}
return status;
}
/**
* Helper function to update the database when the state of the
* {@link #mFirstTimeNativeSyncComplete} flag changes.
*
* @param value New value to the flag. True indicates that the native fetch
* part of the first time sync has been completed. The flag is
* never set to false again by the engine, it will be only set to
* false when a remove user data is done (and the database is
* deleted).
* @return SUCCESS or a suitable error code if the database could not be
* updated.
*/
private ServiceStatus setFirstTimeNativeSyncComplete(boolean value) {
if (mFirstTimeSyncComplete == value) {
return ServiceStatus.SUCCESS;
}
PersistSettings setting = new PersistSettings();
setting.putFirstTimeNativeSyncComplete(value);
ServiceStatus status = mDb.setOption(setting);
if (ServiceStatus.SUCCESS == status) {
mFirstTimeNativeSyncComplete = value;
}
return status;
}
/**
* Called when a database change event is received from the DatabaseHelper.
* Only internal database change events are processed, external change
* events are generated by the contact sync engine.
*
* @param msg The message indicating the type of event
*/
private void processDbMessage(Message message) {
final ServiceUiRequest event = ServiceUiRequest.getUiEvent(message.what);
switch (event) {
case DATABASE_CHANGED_EVENT:
if (message.arg1 == DatabaseHelper.DatabaseChangeType.CONTACTS.ordinal()
&& message.arg2 == 0) {
LogUtils.logV("ContactSyncEngine.processDbMessage - Contacts have changed");
// startMeProfileSyncTimer();
startServerContactSyncTimer(SERVER_CONTACT_SYNC_TIMEOUT_MS);
startUpdateNativeContactSyncTimer();
}
break;
default:
// Do nothing.
break;
}
}
/**
* Notifies observers when a state or mode change occurs.
*
* @param mode Current mode
* @param previousState State before the change
* @param newState State after the change.
*/
private void fireStateChangeEvent(Mode mode, State previousState, State newState) {
ArrayList<IContactSyncObserver> tempList = new ArrayList<IContactSyncObserver>();
synchronized (this) {
tempList.addAll(mEventCallbackList);
}
for (IContactSyncObserver observer : tempList) {
observer.onContactSyncStateChange(mode, previousState, newState);
}
}
/**
* Notifies observers when a contact sync complete event occurs.
*
* @param status SUCCESS or a suitable error code
*/
private void fireSyncCompleteEvent(ServiceStatus status) {
ArrayList<IContactSyncObserver> tempList = new ArrayList<IContactSyncObserver>();
synchronized (this) {
tempList.addAll(mEventCallbackList);
}
for (IContactSyncObserver observer : tempList) {
observer.onSyncComplete(status);
}
}
/**
* Notifies observers when the progress value changes for the current sync.
*
* @param currentState Current sync task being processed
* @param percent Progress of task (between 0 and 100 percent)
*/
private void fireProgressEvent(State currentState, int percent) {
ArrayList<IContactSyncObserver> tempList = new ArrayList<IContactSyncObserver>();
synchronized (this) {
tempList.addAll(mEventCallbackList);
}
for (IContactSyncObserver observer : tempList) {
observer.onProgressEvent(currentState, percent);
}
}
/**
* Called by framework to warn the engine that a remove user data is about
* to start. Sets flags and kicks the engine.
*/
@Override
public void onReset() {
synchronized (this) {
mServerSyncRetryCount = 0;
mState = State.IDLE;
mMode = Mode.NONE;
mFailureList = null;
mDatabaseChanged = false;
mLastDbUpdateTime = 0L;
mActiveProcessor = null;
mServerSyncTimeout = null;
mFetchNativeSyncTimeout = null;
mUpdateNativeSyncTimeout = null;
mLastServerSyncTime = 0L;
mFirstTimeSyncComplete = false;
mFirstTimeSyncStarted = false;
mFirstTimeNativeSyncComplete = false;
mServerSyncRequired = false;
mNativeFetchSyncRequired = false;
mNativeUpdateSyncRequired = false;
mThumbnailSyncRequired = false;
mCurrentProgressPercent = 0;
mDbChangedByProcessor = false;
mActiveUiRequestBackup = null;
ApplicationCache.setSyncBusy(false);
}
super.onReset();
ThumbnailHandler.getInstance().reset();
}
/**
* @see NativeContactsApi.ContactsObserver#onChange()
*/
@Override
public void onChange() {
LogUtils.logD("ContactSyncEngine.onChange(): changes detected on native side.");
// changes detected on native side, start the timer for the
// FetchNativeContacts processor.
startFetchNativeContactSyncTimer();
}
/**
* Called before full contact sync is started to acquire partial wake lock.
* This will ensure that contact sync will continue even if device sleeps.
*/
public void acquireSyncLock() {
if(mWakeLock == null) {
final PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SyncWakeLock");
}
if (mWakeLock != null && !mWakeLock.isHeld()) {
mWakeLock.acquire();
}
}
/**
* Called after full sync is finished (either successfully or erroneously) to
* release partial wake lock.
*/
public void releaseSyncLock() {
if (mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
}
}
/**
* Signal contact sync engine to pause ongoing sync.
*
*/
public synchronized void pauseSync() {
// Pause sync if it is in progress.
if (ApplicationCache.isSyncBusy()) {
mIsSyncPaused = true;
}
}
/**
* Signal contact sync engine to resume sync.
*
*/
public synchronized void resumeSync() {
if (mIsSyncPaused) {
// Get last contact sync engine state from where contact sync can be resumed.
mState = mLastState;
mIsSyncPausedLast = mIsSyncPaused;
mIsSyncPaused = false;
// Remove any stale responses from response queue.
ResponseQueue.getInstance().clearResponseQueue();
// Set active processor to null.
mActiveProcessor = null;
}
}
}