Example usage for android.content ContentResolver getSyncAutomatically

List of usage examples for android.content ContentResolver getSyncAutomatically

Introduction

In this page you can find the example usage for android.content ContentResolver getSyncAutomatically.

Prototype

public static boolean getSyncAutomatically(Account account, String authority) 

Source Link

Document

Check if the provider should be synced when a network tickle is received

This method requires the caller to hold the permission android.Manifest.permission#READ_SYNC_SETTINGS .

Usage

From source file:org.andstatus.app.account.AccountData.java

public static AccountData fromAndroidAccount(MyContext myContext, Account androidAccount) {
    if (androidAccount == null) {
        throw new IllegalArgumentException(TAG + " account is null");
    }/* w w w.  j a va 2s. c o  m*/
    android.accounts.AccountManager am = AccountManager.get(myContext.context());
    AccountData accountData = fromJsonString(am.getUserData(androidAccount, KEY_ACCOUNT), true);
    accountData.setDataBoolean(MyAccount.KEY_IS_SYNCABLE,
            ContentResolver.getIsSyncable(androidAccount, MyProvider.AUTHORITY) != 0);
    accountData.setDataBoolean(MyAccount.KEY_SYNC_AUTOMATICALLY,
            ContentResolver.getSyncAutomatically(androidAccount, MyProvider.AUTHORITY));
    accountData.setDataLong(MyPreferences.KEY_SYNC_FREQUENCY_SECONDS, getSyncFrequencySeconds(androidAccount));
    return accountData;
}

From source file:com.ntsync.android.sync.activities.AccountStatisticListLoader.java

@Override
public List<AccountStatistic> loadInBackground() {
    Account[] accounts = accountManager.getAccountsByType(Constants.ACCOUNT_TYPE);

    List<AccountStatistic> statList = new ArrayList<AccountStatistic>();
    for (Account account : accounts) {

        String username = account.name;
        int contactCount = -1;
        int contactGroupCount = -1;
        Context ctx = getContext();

        AccountSyncResult syncResult = SyncUtils.getSyncResult(accountManager, account);
        Date lastSync = syncResult != null ? syncResult.getLastSyncTime() : null;
        Date nextSync = null;/*from  ww  w .  j a  v a2s  .  c  om*/

        boolean autoSyncEnabled = ContentResolver.getMasterSyncAutomatically()
                && ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY);
        if (autoSyncEnabled) {
            List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(account, ContactsContract.AUTHORITY);
            long nextSyncRef = lastSync != null ? lastSync.getTime() : System.currentTimeMillis();
            for (PeriodicSync sync : syncs) {
                long nextTime = nextSyncRef + sync.period * 1000;
                if (nextSync == null || nextTime < nextSync.getTime()) {
                    nextSync = new Date(nextTime);
                }
            }
        }

        boolean autoSync = ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY);

        // Get Restrictions
        Restrictions restr = SyncUtils.getRestrictions(account, accountManager);
        if (restr == null) {
            try {
                String authtoken = NetworkUtilities.blockingGetAuthToken(accountManager, account, null);
                restr = NetworkUtilities.getRestrictions(getContext(), account, authtoken, accountManager);
            } catch (OperationCanceledException e) {
                Log.i(TAG, "Restriction loading canceled from user", e);
            } catch (AuthenticatorException e) {
                Log.w(TAG, "Authenticator failed", e);
            } catch (AuthenticationException e) {
                Log.i(TAG, "Authentification failed", e);
            } catch (NetworkErrorException e) {
                Log.i(TAG, "Loading Restrictions failed", e);
            } catch (ServerException e) {
                Log.i(TAG, "Loading Restrictions failed", e);
            }
        }

        contactCount = ContactManager.getContactCount(ctx, account);
        contactGroupCount = ContactManager.getContactGroupCount(ctx, account);

        statList.add(new AccountStatistic(username, contactCount, contactGroupCount, restr, syncResult,
                nextSync, autoSync));

    }
    return statList;
}

From source file:dev.drsoran.moloko.sync.util.SyncUtils.java

public final static boolean isReadyToSync(Context context) {
    // Check if we are connected.
    boolean sync = ConnectionUtil.isConnected(context);

    if (sync) {/*from  w w w . ja v  a 2 s  . c  om*/
        final Account account = AccountUtils.getRtmAccount(context);

        // Check if we have an account and the sync has not been disabled
        // in between.
        sync = account != null && ContentResolver.getSyncAutomatically(account, Rtm.AUTHORITY);
    }

    return sync;
}

From source file:com.xandy.calendar.selectcalendars.SelectCalendarsSyncFragment.java

@Override
public void onResume() {
    super.onResume();
    if (!ContentResolver.getMasterSyncAutomatically()
            || !ContentResolver.getSyncAutomatically(mAccount, CalendarContract.AUTHORITY)) {
        Resources res = getActivity().getResources();
        mSyncStatus.setText(res.getString(R.string.acct_not_synced));
        mSyncStatus.setVisibility(View.VISIBLE);
        mAccountsButton.setText(res.getString(R.string.accounts));
        mAccountsButton.setVisibility(View.VISIBLE);
    } else {/*from w w  w. ja v  a  2 s .c o  m*/
        mSyncStatus.setVisibility(View.GONE);
        mAccountsButton.setVisibility(View.GONE);

        // Start a background sync to get the list of calendars from the server.
        Utils.startCalendarMetafeedSync(mAccount);
        getActivity().getContentResolver().registerContentObserver(Calendars.CONTENT_URI, true,
                mCalendarsObserver);
    }
}

From source file:org.andstatus.app.account.AccountData.java

/**
 * @param result //w  w  w.jav a2s. com
 * @return true if Android account changed
 */
void saveDataToAccount(MyContext myContext, Account androidAccount, SaveResult result) {
    AccountData oldData = fromAndroidAccount(myContext, androidAccount);
    result.changed = !this.equals(oldData);
    if (result.changed) {
        long syncFrequencySeconds = getDataLong(MyPreferences.KEY_SYNC_FREQUENCY_SECONDS, 0);
        if (syncFrequencySeconds > 0 && syncFrequencySeconds != getSyncFrequencySeconds(androidAccount)) {
            result.changed = true;
            setSyncFrequencySeconds(androidAccount, syncFrequencySeconds);
        }
        boolean isSyncable = getDataBoolean(MyAccount.KEY_IS_SYNCABLE, true);
        if (isSyncable != (ContentResolver.getIsSyncable(androidAccount, MyProvider.AUTHORITY) != 0)) {
            ContentResolver.setIsSyncable(androidAccount, MyProvider.AUTHORITY, isSyncable ? 1 : 0);
        }
        boolean syncAutomatically = getDataBoolean(MyAccount.KEY_SYNC_AUTOMATICALLY, true);
        if (syncAutomatically != ContentResolver.getSyncAutomatically(androidAccount, MyProvider.AUTHORITY)) {
            // We need to preserve sync on/off during backup/restore.
            // don't know about "network tickles"... See:
            // http://stackoverflow.com/questions/5013254/what-is-a-network-tickle-and-how-to-i-go-about-sending-one
            ContentResolver.setSyncAutomatically(androidAccount, MyProvider.AUTHORITY, syncAutomatically);
        }
        android.accounts.AccountManager am = AccountManager.get(myContext.context());
        am.setUserData(androidAccount, KEY_ACCOUNT, toJsonString());
        result.savedToAccountManager = true;
    }
    result.success = true;
}

From source file:de.spiritcroc.syncsettings.Util.java

public static void autoSyncOn(Account account, String authority) {
    if (DEBUG) {/*www.  j  a v a2s.c om*/
        Log.d(LOG_TAG, "sync on " + account + ", " + authority);
        Log.d(LOG_TAG, "previous " + ContentResolver.getSyncAutomatically(account, authority));
    }
    ContentResolver.setSyncAutomatically(account, authority, true);
    if (DEBUG) {
        Log.d("syncsettings", "now " + ContentResolver.getSyncAutomatically(account, authority));
    }
}

From source file:de.spiritcroc.syncsettings.Util.java

public static void autoSyncOff(Account account, String authority) {
    if (DEBUG) {/*  w w  w .j ava 2s.c  om*/
        Log.d(LOG_TAG, "sync off " + account + ", " + authority);
        Log.d(LOG_TAG, "previous " + ContentResolver.getSyncAutomatically(account, authority));
    }
    ContentResolver.setSyncAutomatically(account, authority, false);
    if (DEBUG) {
        Log.d("syncsettings", "now " + ContentResolver.getSyncAutomatically(account, authority));
    }
}

From source file:org.sufficientlysecure.keychain.service.ContactSyncAdapterService.java

public static void requestContactsSync() {
    // if user has disabled automatic sync, do nothing
    boolean isSyncEnabled = ContentResolver.getSyncAutomatically(
            new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), ContactsContract.AUTHORITY);

    if (!isSyncEnabled) {
        return;//from w  w w.j  a v a 2s .co  m
    }

    Bundle extras = new Bundle();
    // no need to wait, do it immediately
    extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
    extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    ContentResolver.requestSync(new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
            ContactsContract.AUTHORITY, extras);
}

From source file:com.example.jumpnote.android.SyncAdapter.java

@Override
public void onPerformSync(final Account account, Bundle extras, String authority,
        final ContentProviderClient provider, final SyncResult syncResult) {
    TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    String clientDeviceId = tm.getDeviceId();

    final long newSyncTime = System.currentTimeMillis();

    final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
    final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
    final boolean initialize = extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);

    C2DMReceiver.refreshAppC2DMRegistrationState(mContext);

    Log.i(TAG, "Beginning " + (uploadOnly ? "upload-only" : "full") + " sync for account " + account.name);

    // Read this account's sync metadata
    final SharedPreferences syncMeta = mContext.getSharedPreferences("sync:" + account.name, 0);
    long lastSyncTime = syncMeta.getLong(LAST_SYNC, 0);
    long lastServerSyncTime = syncMeta.getLong(SERVER_LAST_SYNC, 0);

    // Check for changes in either app-wide auto sync registration information, or changes in
    // the user's preferences for auto sync on this account; if either changes, piggy back the
    // new registration information in this sync.
    long lastRegistrationChangeTime = C2DMessaging.getLastRegistrationChange(mContext);

    boolean autoSyncDesired = ContentResolver.getMasterSyncAutomatically()
            && ContentResolver.getSyncAutomatically(account, JumpNoteContract.AUTHORITY);
    boolean autoSyncEnabled = syncMeta.getBoolean(DM_REGISTERED, false);

    // Will be 0 for no change, -1 for unregister, 1 for register.
    final int deviceRegChange;
    JsonRpcClient.Call deviceRegCall = null;
    if (autoSyncDesired != autoSyncEnabled || lastRegistrationChangeTime > lastSyncTime || initialize
            || manualSync) {/*from  w  w w. j a v  a  2s  .c  o m*/

        String registrationId = C2DMessaging.getRegistrationId(mContext);
        deviceRegChange = (autoSyncDesired && registrationId != null) ? 1 : -1;

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG,
                    "Auto sync selection or registration information has changed, "
                            + (deviceRegChange == 1 ? "registering" : "unregistering")
                            + " messaging for this device, for account " + account.name);
        }

        try {
            if (deviceRegChange == 1) {
                // Register device for auto sync on this account.
                deviceRegCall = new JsonRpcClient.Call(JumpNoteProtocol.DevicesRegister.METHOD);
                JSONObject params = new JSONObject();

                DeviceRegistration device = new DeviceRegistration(clientDeviceId, DEVICE_TYPE, registrationId);
                params.put(JumpNoteProtocol.DevicesRegister.ARG_DEVICE, device.toJSON());
                deviceRegCall.setParams(params);
            } else {
                // Unregister device for auto sync on this account.
                deviceRegCall = new JsonRpcClient.Call(JumpNoteProtocol.DevicesUnregister.METHOD);
                JSONObject params = new JSONObject();
                params.put(JumpNoteProtocol.DevicesUnregister.ARG_DEVICE_ID, clientDeviceId);
                deviceRegCall.setParams(params);
            }
        } catch (JSONException e) {
            logErrorMessage("Error generating device registration remote RPC parameters.", manualSync);
            e.printStackTrace();
            return;
        }
    } else {
        deviceRegChange = 0;
    }

    // Get the list of locally changed notes. If this is an upload-only sync and there were
    // no local changes, cancel the sync.
    List<ModelJava.Note> locallyChangedNotes = null;
    try {
        locallyChangedNotes = getLocallyChangedNotes(provider, account, new Date(lastSyncTime));
    } catch (RemoteException e) {
        logErrorMessage("Remote exception accessing content provider: " + e.getMessage(), manualSync);
        e.printStackTrace();
        syncResult.stats.numIoExceptions++;
        return;
    }

    if (uploadOnly && locallyChangedNotes.isEmpty() && deviceRegCall == null) {
        Log.i(TAG, "No local changes; upload-only sync canceled.");
        return;
    }

    // Set up the RPC sync calls
    final AuthenticatedJsonRpcJavaClient jsonRpcClient = new AuthenticatedJsonRpcJavaClient(mContext,
            Config.SERVER_AUTH_URL_TEMPLATE, Config.SERVER_RPC_URL);
    try {
        jsonRpcClient.blockingAuthenticateAccount(account,
                manualSync ? AuthenticatedJsonRpcJavaClient.NEED_AUTH_INTENT
                        : AuthenticatedJsonRpcJavaClient.NEED_AUTH_NOTIFICATION,
                false);
    } catch (AuthenticationException e) {
        logErrorMessage("Authentication exception when attempting to sync.", manualSync);
        e.printStackTrace();
        syncResult.stats.numAuthExceptions++;
        return;
    } catch (OperationCanceledException e) {
        Log.i(TAG, "Sync for account " + account.name + " manually canceled.");
        return;
    } catch (RequestedUserAuthenticationException e) {
        syncResult.stats.numAuthExceptions++;
        return;
    } catch (InvalidAuthTokenException e) {
        logErrorMessage("Invalid auth token provided by AccountManager when attempting to " + "sync.",
                manualSync);
        e.printStackTrace();
        syncResult.stats.numAuthExceptions++;
        return;
    }

    // Set up the notes sync call.
    JsonRpcClient.Call notesSyncCall = new JsonRpcClient.Call(JumpNoteProtocol.NotesSync.METHOD);
    try {
        JSONObject params = new JSONObject();
        params.put(JumpNoteProtocol.ARG_CLIENT_DEVICE_ID, clientDeviceId);
        params.put(JumpNoteProtocol.NotesSync.ARG_SINCE_DATE,
                Util.formatDateISO8601(new Date(lastServerSyncTime)));

        JSONArray locallyChangedNotesJson = new JSONArray();
        for (ModelJava.Note locallyChangedNote : locallyChangedNotes) {
            locallyChangedNotesJson.put(locallyChangedNote.toJSON());
        }

        params.put(JumpNoteProtocol.NotesSync.ARG_LOCAL_NOTES, locallyChangedNotesJson);
        notesSyncCall.setParams(params);
    } catch (JSONException e) {
        logErrorMessage("Error generating sync remote RPC parameters.", manualSync);
        e.printStackTrace();
        syncResult.stats.numParseExceptions++;
        return;
    }

    List<JsonRpcClient.Call> jsonRpcCalls = new ArrayList<JsonRpcClient.Call>();
    jsonRpcCalls.add(notesSyncCall);
    if (deviceRegChange != 0)
        jsonRpcCalls.add(deviceRegCall);

    jsonRpcClient.callBatch(jsonRpcCalls, new JsonRpcClient.BatchCallback() {
        public void onData(Object[] data) {
            if (data[0] != null) {
                // Read notes sync data.
                JSONObject dataJson = (JSONObject) data[0];
                try {
                    List<ModelJava.Note> changedNotes = new ArrayList<ModelJava.Note>();
                    JSONArray notesJson = dataJson.getJSONArray(JumpNoteProtocol.NotesSync.RET_NOTES);
                    for (int i = 0; i < notesJson.length(); i++) {
                        changedNotes.add(new ModelJava.Note(notesJson.getJSONObject(i)));
                    }

                    reconcileSyncedNotes(provider, account, changedNotes, syncResult.stats);

                    // If sync is successful (no exceptions thrown), update sync metadata
                    long newServerSyncTime = Util
                            .parseDateISO8601(dataJson.getString(JumpNoteProtocol.NotesSync.RET_NEW_SINCE_DATE))
                            .getTime();
                    syncMeta.edit().putLong(LAST_SYNC, newSyncTime).commit();
                    syncMeta.edit().putLong(SERVER_LAST_SYNC, newServerSyncTime).commit();
                    Log.i(TAG, "Sync complete, setting last sync time to " + Long.toString(newSyncTime));
                } catch (JSONException e) {
                    logErrorMessage("Error parsing note sync RPC response", manualSync);
                    e.printStackTrace();
                    syncResult.stats.numParseExceptions++;
                    return;
                } catch (ParseException e) {
                    logErrorMessage("Error parsing note sync RPC response", manualSync);
                    e.printStackTrace();
                    syncResult.stats.numParseExceptions++;
                    return;
                } catch (RemoteException e) {
                    logErrorMessage("RemoteException in reconcileSyncedNotes: " + e.getMessage(), manualSync);
                    e.printStackTrace();
                    return;
                } catch (OperationApplicationException e) {
                    logErrorMessage("Could not apply batch operations to content provider: " + e.getMessage(),
                            manualSync);
                    e.printStackTrace();
                    return;
                } finally {
                    provider.release();
                }
            }

            // Read device reg data.
            if (deviceRegChange != 0) {
                // data[1] will be null in case of an error (successful unregisters
                // will have an empty JSONObject, not null).
                boolean registered = (data[1] != null && deviceRegChange == 1);
                syncMeta.edit().putBoolean(DM_REGISTERED, registered).commit();
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "Stored account auto sync registration state: " + Boolean.toString(registered));
                }
            }
        }

        public void onError(int callIndex, JsonRpcException e) {
            if (e.getHttpCode() == 403) {
                Log.w(TAG, "Got a 403 response, invalidating App Engine ACSID token");
                jsonRpcClient.invalidateAccountAcsidToken(account);
            }

            provider.release();
            logErrorMessage("Error calling remote note sync RPC", manualSync);
            e.printStackTrace();
        }
    });
}

From source file:com.samsung.android.remindme.SyncAdapter.java

@Override
public void onPerformSync(final Account account, Bundle extras, String authority,
        final ContentProviderClient provider, final SyncResult syncResult) {
    Log.i(TAG, "onPerformSync called!");
    TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    String clientDeviceId = tm.getDeviceId();

    final long newSyncTime = System.currentTimeMillis();

    final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
    final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
    final boolean initialize = extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);

    C2DMReceiver.refreshAppC2DMRegistrationState(mContext);

    Log.i(TAG, "Beginning " + (uploadOnly ? "upload-only" : "full") + " sync for account " + account.name);

    // Read this account's sync metadata
    final SharedPreferences syncMeta = mContext.getSharedPreferences("sync:" + account.name, 0);
    long lastSyncTime = syncMeta.getLong(LAST_SYNC, 0);
    long lastServerSyncTime = syncMeta.getLong(SERVER_LAST_SYNC, 0);

    // Check for changes in either app-wide auto sync registration information, or changes in
    // the user's preferences for auto sync on this account; if either changes, piggy back the
    // new registration information in this sync.
    long lastRegistrationChangeTime = C2DMessaging.getLastRegistrationChange(mContext);

    boolean autoSyncDesired = ContentResolver.getMasterSyncAutomatically()
            && ContentResolver.getSyncAutomatically(account, RemindMeContract.AUTHORITY);
    boolean autoSyncEnabled = syncMeta.getBoolean(DM_REGISTERED, false);

    // Will be 0 for no change, -1 for unregister, 1 for register.
    final int deviceRegChange;
    JsonRpcClient.Call deviceRegCall = null;
    if (autoSyncDesired != autoSyncEnabled || lastRegistrationChangeTime > lastSyncTime || initialize
            || manualSync) {/* ww  w .ja  v a2 s .co m*/

        String registrationId = C2DMessaging.getRegistrationId(mContext);
        deviceRegChange = (autoSyncDesired && registrationId != null) ? 1 : -1;

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG,
                    "Auto sync selection or registration information has changed, "
                            + (deviceRegChange == 1 ? "registering" : "unregistering")
                            + " messaging for this device, for account " + account.name);
        }

        try {
            if (deviceRegChange == 1) {
                // Register device for auto sync on this account.
                deviceRegCall = new JsonRpcClient.Call(RemindMeProtocol.DevicesRegister.METHOD);
                JSONObject params = new JSONObject();

                DeviceRegistration device = new DeviceRegistration(clientDeviceId, DEVICE_TYPE, registrationId);
                params.put(RemindMeProtocol.DevicesRegister.ARG_DEVICE, device.toJSON());
                deviceRegCall.setParams(params);
            } else {
                // Unregister device for auto sync on this account.
                deviceRegCall = new JsonRpcClient.Call(RemindMeProtocol.DevicesUnregister.METHOD);
                JSONObject params = new JSONObject();
                params.put(RemindMeProtocol.DevicesUnregister.ARG_DEVICE_ID, clientDeviceId);
                deviceRegCall.setParams(params);
            }
        } catch (JSONException e) {
            logErrorMessage("Error generating device registration remote RPC parameters.", manualSync);
            e.printStackTrace();
            return;
        }
    } else {
        deviceRegChange = 0;
    }

    // Get the list of locally changed alerts. If this is an upload-only sync and there were
    // no local changes, cancel the sync.
    List<ModelJava.Alert> locallyChangedAlerts = null;
    try {
        locallyChangedAlerts = getLocallyChangedAlerts(provider, account, new Date(lastSyncTime));
    } catch (RemoteException e) {
        logErrorMessage("Remote exception accessing content provider: " + e.getMessage(), manualSync);
        e.printStackTrace();
        syncResult.stats.numIoExceptions++;
        return;
    }

    if (uploadOnly && locallyChangedAlerts.isEmpty() && deviceRegCall == null) {
        Log.i(TAG, "No local changes; upload-only sync canceled.");
        return;
    }

    // Set up the RPC sync calls
    final AuthenticatedJsonRpcJavaClient jsonRpcClient = new AuthenticatedJsonRpcJavaClient(mContext,
            Config.SERVER_AUTH_URL_TEMPLATE, Config.SERVER_RPC_URL);
    try {
        jsonRpcClient.blockingAuthenticateAccount(account,
                manualSync ? AuthenticatedJsonRpcJavaClient.NEED_AUTH_INTENT
                        : AuthenticatedJsonRpcJavaClient.NEED_AUTH_NOTIFICATION,
                false);
    } catch (AuthenticationException e) {
        logErrorMessage("Authentication exception when attempting to sync. root cause: " + e.getMessage(),
                manualSync);
        e.printStackTrace();

        syncResult.stats.numAuthExceptions++;
        return;
    } catch (OperationCanceledException e) {
        Log.i(TAG, "Sync for account " + account.name + " manually canceled.");
        return;
    } catch (RequestedUserAuthenticationException e) {
        syncResult.stats.numAuthExceptions++;
        return;
    } catch (InvalidAuthTokenException e) {
        logErrorMessage("Invalid auth token provided by AccountManager when attempting to " + "sync.",
                manualSync);
        e.printStackTrace();
        syncResult.stats.numAuthExceptions++;
        return;
    }

    // Set up the alerts sync call.
    JsonRpcClient.Call alertsSyncCall = new JsonRpcClient.Call(RemindMeProtocol.AlertsSync.METHOD);
    try {
        JSONObject params = new JSONObject();
        params.put(RemindMeProtocol.ARG_CLIENT_DEVICE_ID, clientDeviceId);
        params.put(RemindMeProtocol.AlertsSync.ARG_SINCE_DATE,
                Util.formatDateISO8601(new Date(lastServerSyncTime)));

        JSONArray locallyChangedAlertsJson = new JSONArray();
        for (ModelJava.Alert locallyChangedAlert : locallyChangedAlerts) {
            locallyChangedAlertsJson.put(locallyChangedAlert.toJSON());
        }

        params.put(RemindMeProtocol.AlertsSync.ARG_LOCAL_NOTES, locallyChangedAlertsJson);
        alertsSyncCall.setParams(params);
    } catch (JSONException e) {
        logErrorMessage("Error generating sync remote RPC parameters.", manualSync);
        e.printStackTrace();
        syncResult.stats.numParseExceptions++;
        return;
    }

    List<JsonRpcClient.Call> jsonRpcCalls = new ArrayList<JsonRpcClient.Call>();
    jsonRpcCalls.add(alertsSyncCall);
    if (deviceRegChange != 0)
        jsonRpcCalls.add(deviceRegCall);

    jsonRpcClient.callBatch(jsonRpcCalls, new JsonRpcClient.BatchCallback() {
        public void onData(Object[] data) {
            if (data[0] != null) {
                // Read alerts sync data.
                JSONObject dataJson = (JSONObject) data[0];
                try {
                    List<ModelJava.Alert> changedAlerts = new ArrayList<ModelJava.Alert>();
                    JSONArray alertsJson = dataJson.getJSONArray(RemindMeProtocol.AlertsSync.RET_NOTES);
                    for (int i = 0; i < alertsJson.length(); i++) {
                        changedAlerts.add(new ModelJava.Alert(alertsJson.getJSONObject(i)));
                    }

                    reconcileSyncedAlerts(provider, account, changedAlerts, syncResult.stats);

                    // If sync is successful (no exceptions thrown), update sync metadata
                    long newServerSyncTime = Util
                            .parseDateISO8601(
                                    dataJson.getString(RemindMeProtocol.AlertsSync.RET_NEW_SINCE_DATE))
                            .getTime();
                    syncMeta.edit().putLong(LAST_SYNC, newSyncTime).commit();
                    syncMeta.edit().putLong(SERVER_LAST_SYNC, newServerSyncTime).commit();
                    Log.i(TAG, "Sync complete, setting last sync time to " + Long.toString(newSyncTime));
                } catch (JSONException e) {
                    logErrorMessage("Error parsing alert sync RPC response", manualSync);
                    e.printStackTrace();
                    syncResult.stats.numParseExceptions++;
                    return;
                } catch (ParseException e) {
                    logErrorMessage("Error parsing alert sync RPC response", manualSync);
                    e.printStackTrace();
                    syncResult.stats.numParseExceptions++;
                    return;
                } catch (RemoteException e) {
                    logErrorMessage("RemoteException in reconcileSyncedAlerts: " + e.getMessage(), manualSync);
                    e.printStackTrace();
                    return;
                } catch (OperationApplicationException e) {
                    logErrorMessage("Could not apply batch operations to content provider: " + e.getMessage(),
                            manualSync);
                    e.printStackTrace();
                    return;
                } finally {
                    provider.release();
                }
            }

            // Read device reg data.
            if (deviceRegChange != 0) {
                // data[1] will be null in case of an error (successful unregisters
                // will have an empty JSONObject, not null).
                boolean registered = (data[1] != null && deviceRegChange == 1);
                syncMeta.edit().putBoolean(DM_REGISTERED, registered).commit();
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "Stored account auto sync registration state: " + Boolean.toString(registered));
                }
            }
        }

        public void onError(int callIndex, JsonRpcException e) {
            if (e.getHttpCode() == 403) {
                Log.w(TAG, "Got a 403 response, invalidating App Engine ACSID token");
                jsonRpcClient.invalidateAccountAcsidToken(account);
            }

            provider.release();
            logErrorMessage("Error calling remote alert sync RPC", manualSync);
            e.printStackTrace();
        }
    });
}