16c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu/*
26c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Copyright (C) 2014 The Android Open Source Project
36c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
46c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Licensed under the Apache License, Version 2.0 (the "License");
56c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * you may not use this file except in compliance with the License.
66c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * You may obtain a copy of the License at
76c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
86c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *      http://www.apache.org/licenses/LICENSE-2.0
96c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
106c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Unless required by applicable law or agreed to in writing, software
116c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * distributed under the License is distributed on an "AS IS" BASIS,
126c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * See the License for the specific language governing permissions and
146c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * limitations under the License.
156c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu */
166c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
176c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hupackage com.android.exchange.service;
186c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
19451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdonimport android.app.AlarmManager;
20451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdonimport android.app.PendingIntent;
216c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Huimport android.app.Service;
22451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdonimport android.content.ContentResolver;
23c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Leeimport android.content.Context;
246c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Huimport android.content.Intent;
25451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdonimport android.os.Bundle;
26451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdonimport android.os.SystemClock;
276c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Huimport android.support.v4.util.LongSparseArray;
28451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdonimport android.text.format.DateUtils;
296c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
30c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Leeimport com.android.emailcommon.provider.Account;
31451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdonimport com.android.emailcommon.provider.EmailContent;
32451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdonimport com.android.emailcommon.provider.Mailbox;
336c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Huimport com.android.exchange.Eas;
34c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Leeimport com.android.exchange.eas.EasPing;
356c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Huimport com.android.mail.utils.LogUtils;
366c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
376c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Huimport java.util.concurrent.locks.Condition;
386c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Huimport java.util.concurrent.locks.Lock;
396c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Huimport java.util.concurrent.locks.ReentrantLock;
406c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
416c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu/**
426c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Bookkeeping for handling synchronization between pings and other sync related operations.
436c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * "Ping" refers to a hanging POST or GET that is used to receive push notifications. Ping is
446c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * the term for the Exchange command, but this code should be generic enough to be extended to IMAP.
456c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
466c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Basic rules of how these interact (note that all rules are per account):
476c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - Only one operation (ping or other active sync operation) may run at a time.
486c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - For shorthand, this class uses "sync" to mean "non-ping operation"; most such operations are
496c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *   sync ops, but some may not be (e.g. EAS Settings).
506c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - Syncs can come from many sources concurrently; this class must serialize them.
516c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
526c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * WHEN A SYNC STARTS:
536c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If nothing is running, proceed.
546c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If something is already running: wait until it's done.
556c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If the running thing is a ping task: interrupt it.
566c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
576c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * WHEN A SYNC ENDS:
586c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If there are waiting syncs: signal one to proceed.
596c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If there are no waiting syncs and this account is configured for push: start a ping.
606c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - Otherwise: This account is now idle.
616c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
626c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * WHEN A PING TASK ENDS:
636c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - A ping task loops until either it's interrupted by a sync (in which case, there will be one or
646c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *   more waiting syncs when the ping terminates), or encounters an error.
656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If there are waiting syncs, and we were interrupted: signal one to proceed.
666c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If there are waiting syncs, but the ping terminated with an error: TODO: How to handle?
676c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If there are no waiting syncs and this account is configured for push: This means the ping task
686c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *   was terminated due to an error. Handle this by sending a sync request through the SyncManager
696c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *   that doesn't actually do any syncing, and whose only effect is to restart the ping.
706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - Otherwise: This account is now idle.
716c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
726c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * WHEN AN ACCOUNT WANTS TO START OR CHANGE ITS PUSH BEHAVIOR:
736c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If nothing is running, start a new ping task.
746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If a ping task is currently running, restart it with the new settings.
756c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If a sync is currently running, do nothing.
766c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
776c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * WHEN AN ACCOUNT WANTS TO STOP GETTING PUSH:
786c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If nothing is running, do nothing.
796c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * - If a ping task is currently running, interrupt it.
806c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu */
816c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hupublic class PingSyncSynchronizer {
826c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
836c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private static final String TAG = Eas.LOG_TAG;
846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
85451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    private static final long SYNC_ERROR_BACKOFF_MILLIS =  DateUtils.MINUTE_IN_MILLIS;
86451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon
87451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    // Enable this to make pings get automatically renewed every hour. This
88451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    // should not be needed, but if there is a software error that results in
89451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    // the ping being lost, this is a fallback to make sure that messages are
90451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    // not delayed more than an hour.
9129df1a2f769cefedf8ec84403b5dc1384d3cb551Martin Hibdon    private static final boolean SCHEDULE_KICK = true;
92451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    private static final long KICK_SYNC_INTERVAL_SECONDS =
93451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            DateUtils.HOUR_IN_MILLIS / DateUtils.SECOND_IN_MILLIS;
94451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon
956c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
966c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * This class handles bookkeeping for a single account.
976c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
981e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon    private class AccountSyncState {
996c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        /** The currently running {@link PingTask}, or null if we aren't in the middle of a Ping. */
1006c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        private PingTask mPingTask;
1016c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1021e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon        // Values for mPushEnabled.
1031e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon        public static final int PUSH_UNKNOWN = 0;
1041e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon        public static final int PUSH_ENABLED = 1;
1051e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon        public static final int PUSH_DISABLED = 2;
1061e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon
1076c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        /**
108468ff529b5c80605a62d6fc7ecf87a8db46ea366Yu Ping Hu         * Tracks whether this account wants to get push notifications, based on calls to
109468ff529b5c80605a62d6fc7ecf87a8db46ea366Yu Ping Hu         * {@link #pushModify} and {@link #pushStop} (i.e. it tracks the last requested push state).
110468ff529b5c80605a62d6fc7ecf87a8db46ea366Yu Ping Hu         */
1111e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon        private int mPushEnabled;
112468ff529b5c80605a62d6fc7ecf87a8db46ea366Yu Ping Hu
113468ff529b5c80605a62d6fc7ecf87a8db46ea366Yu Ping Hu        /**
1146c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * The number of syncs that are blocked waiting for the current operation to complete.
1156c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * Unlike Pings, sync operations do not start their own tasks and are assumed to run in
1166c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * whatever thread calls into this class.
1176c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         */
1186c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        private int mSyncCount;
1196c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1206c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        /** The condition on which to block syncs that need to wait. */
1216c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        private Condition mCondition;
1226c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
12347b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon        /** The accountId for this accountState, used for logging */
12447b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon        private long mAccountId;
12547b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon
12647b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon        public AccountSyncState(final Lock lock, final long accountId) {
1276c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mPingTask = null;
1281e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon            // We don't yet have enough information to know whether or not push should be enabled.
1291e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon            // We need to look up the account and it's folders, which won't yet exist for a newly
1301e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon            // created account.
1311e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon            mPushEnabled = PUSH_UNKNOWN;
1326c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mSyncCount = 0;
1336c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mCondition = lock.newCondition();
13447b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            mAccountId = accountId;
1356c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
1366c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1376c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        /**
1386c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * Update bookkeeping for a new sync:
1396c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * - Stop the Ping if there is one.
1406c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * - Wait until there's nothing running for this account before proceeding.
1416c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         */
1426c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        public void syncStart() {
1436c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            ++mSyncCount;
1446c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mPingTask != null) {
1456c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                // Syncs are higher priority than Ping -- terminate the Ping.
14647b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                LogUtils.i(TAG, "PSS Sync is pre-empting a ping acct:%d", mAccountId);
1476c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                mPingTask.stop();
1486c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
1496c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mPingTask != null || mSyncCount > 1) {
1506c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                // There’s something we need to wait for before we can proceed.
1516c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                try {
15247b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                    LogUtils.i(TAG, "PSS Sync needs to wait: Ping: %s, Pending tasks: %d acct: %d",
15347b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                            mPingTask != null ? "yes" : "no", mSyncCount, mAccountId);
1546c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    mCondition.await();
1556c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                } catch (final InterruptedException e) {
1566c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    // TODO: Handle this properly. Not catching it might be the right answer.
15747b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                    LogUtils.i(TAG, "PSS InterruptedException acct:%d", mAccountId);
1586c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                }
1596c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
1606c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
1616c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1626c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        /**
1636c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * Update bookkeeping when a sync completes. This includes signaling pending ops to
1646c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * go ahead, or starting the ping if appropriate and there are no waiting ops.
1656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * @return Whether this account is now idle.
1666c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         */
167451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon        public boolean syncEnd(final boolean lastSyncHadError, final Account account,
168451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                               final PingSyncSynchronizer synchronizer) {
1696c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            --mSyncCount;
1706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mSyncCount > 0) {
17147b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                LogUtils.i(TAG, "PSS Signalling a pending sync to proceed acct:%d.",
17247b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                        account.getId());
1736c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                mCondition.signal();
1746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                return false;
1756c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            } else {
1761e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                if (mPushEnabled == PUSH_UNKNOWN) {
1771e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                    LogUtils.i(TAG, "PSS push enabled is unknown");
1781e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                    mPushEnabled = (EasService.pingNeededForAccount(mService, account) ?
1791e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                            PUSH_ENABLED : PUSH_DISABLED);
1801e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                }
1811e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                if (mPushEnabled == PUSH_ENABLED) {
182451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                    if (lastSyncHadError) {
18347b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                        LogUtils.i(TAG, "PSS last sync had error, scheduling delayed ping acct:%d.",
18447b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                                account.getId());
18553d15f1321d19432c88de34e57218a5f51eaf3dcMartin Hibdon                        scheduleDelayedPing(synchronizer.getContext(), account);
186451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                        return true;
187451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                    } else {
18847b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                        LogUtils.i(TAG, "PSS last sync succeeded, starting new ping acct:%d.",
18947b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                                account.getId());
190451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                        final android.accounts.Account amAccount =
191451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                                new android.accounts.Account(account.mEmailAddress,
192451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                                        Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
193451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                        mPingTask = new PingTask(synchronizer.getContext(), account, amAccount,
194451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                                synchronizer);
195451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                        mPingTask.start();
196451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                        return false;
197451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                    }
1986c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                }
1996c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
20047b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS no push enabled acct:%d.", account.getId());
2016c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            return true;
2026c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
2036c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
2046c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        /**
2056c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * Update bookkeeping when the ping task terminates, including signaling any waiting ops.
2066c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * @return Whether this account is now idle.
2076c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         */
208451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon        private boolean pingEnd(final android.accounts.Account amAccount) {
2096c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mPingTask = null;
2106c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mSyncCount > 0) {
21147b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                LogUtils.i(TAG, "PSS pingEnd, syncs still in progress acct:%d.", mAccountId);
2126c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                mCondition.signal();
2136c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                return false;
2146c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            } else {
2151e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                if (mPushEnabled == PUSH_ENABLED || mPushEnabled == PUSH_UNKNOWN) {
2161e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                    if (mPushEnabled == PUSH_UNKNOWN) {
2171e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                        // This should not occur. If mPushEnabled is unknown, we should not
2181e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                        // have started a ping. Still, we'd rather err on the side of restarting
2191e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                        // the ping, so log an error and request a new ping. Eventually we should
2201e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                        // do a sync, and then we'll correctly initialize mPushEnabled in
2211e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                        // syncEnd().
2221e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                        LogUtils.e(TAG, "PSS pingEnd, with mPushEnabled UNKNOWN?");
2231e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon                    }
22447b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                    LogUtils.i(TAG, "PSS pingEnd, starting new ping acct:%d.", mAccountId);
225c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                    /**
226c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                     * This situation only arises if we encountered some sort of error that
227c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                     * stopped our ping but not due to a sync interruption. In this scenario
228c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                     * we'll leverage the SyncManager to request a push only sync that will
229c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                     * restart the ping when the time is right. */
230c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                    EasPing.requestPing(amAccount);
2316c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    return false;
2326c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                }
2336c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
23447b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS pingEnd, no longer need ping acct:%d.", mAccountId);
2356c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            return true;
2366c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
2376c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
238451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon        private void scheduleDelayedPing(final Context context,
23953d15f1321d19432c88de34e57218a5f51eaf3dcMartin Hibdon                                         final Account account) {
24047b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS Scheduling a delayed ping acct:%d.", account.getId());
24153d15f1321d19432c88de34e57218a5f51eaf3dcMartin Hibdon            final Intent intent = new Intent(context, EasService.class);
242451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            intent.setAction(Eas.EXCHANGE_SERVICE_INTENT_ACTION);
24353d15f1321d19432c88de34e57218a5f51eaf3dcMartin Hibdon            intent.putExtra(EasService.EXTRA_START_PING, true);
24453d15f1321d19432c88de34e57218a5f51eaf3dcMartin Hibdon            intent.putExtra(EasService.EXTRA_PING_ACCOUNT, account);
24553d15f1321d19432c88de34e57218a5f51eaf3dcMartin Hibdon
246451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            final PendingIntent pi = PendingIntent.getService(context, 0, intent,
247451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                    PendingIntent.FLAG_ONE_SHOT);
248451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            final AlarmManager am = (AlarmManager)context.getSystemService(
249451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                    Context.ALARM_SERVICE);
250451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            final long atTime = SystemClock.elapsedRealtime() + SYNC_ERROR_BACKOFF_MILLIS;
251451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, atTime, pi);
252451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon        }
253451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon
2546c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        /**
2556c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * Modifies or starts a ping for this account if no syncs are running.
2566c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         */
257c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee        public void pushModify(final Account account, final PingSyncSynchronizer synchronizer) {
25847b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(LogUtils.TAG, "PSS pushModify acct:%d", account.getId());
2591e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon            mPushEnabled = PUSH_ENABLED;
260451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            final android.accounts.Account amAccount =
261451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                    new android.accounts.Account(account.mEmailAddress,
262451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                            Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
2636c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mSyncCount == 0) {
2646c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                if (mPingTask == null) {
2656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    // No ping, no running syncs -- start a new ping.
26647b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                    LogUtils.i(LogUtils.TAG, "PSS starting ping task acct:%d", account.getId());
267c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                    mPingTask = new PingTask(synchronizer.getContext(), account, amAccount,
268c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                            synchronizer);
269c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee                    mPingTask.start();
2706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                } else {
2716c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    // Ping is already running, so tell it to restart to pick up any new params.
27247b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                    LogUtils.i(LogUtils.TAG, "PSS restarting ping task acct:%d", account.getId());
2736c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    mPingTask.restart();
2746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                }
27547b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            } else {
27647b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon                LogUtils.i(LogUtils.TAG, "PSS syncs still in progress acct:%d", account.getId());
2776c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
278451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            if (SCHEDULE_KICK) {
279451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                final Bundle extras = new Bundle(1);
280451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                extras.putBoolean(Mailbox.SYNC_EXTRA_PUSH_ONLY, true);
281451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                ContentResolver.addPeriodicSync(amAccount, EmailContent.AUTHORITY, extras,
282451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon                        KICK_SYNC_INTERVAL_SECONDS);
283451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            }
2846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
2856c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
2866c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        /**
2876c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         * Stop the currently running ping.
2886c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu         */
2896c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        public void pushStop() {
29047b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(LogUtils.TAG, "PSS pushStop acct:%d", mAccountId);
2911e2a913eb7bd5b30ad4098582c31ba9c6b40efb5Martin Hibdon            mPushEnabled = PUSH_DISABLED;
2926c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mPingTask != null) {
2936c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                mPingTask.stop();
2946c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
2956c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
2966c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
2976c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
2986c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
2996c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Lock for access to {@link #mAccountStateMap}, also used to create the {@link Condition}s for
3006c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * each Account.
3016c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
3026c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private final ReentrantLock mLock;
3036c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
3046c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
3056c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Map from account ID -> {@link AccountSyncState} for accounts with a running operation.
3066c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * An account is in this map only when this account is active, i.e. has a ping or sync running
3076c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * or pending. If an account is not in the middle of a sync and is not configured for push,
3086c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * it will not be here. This allows to use emptiness of this map to know whether the service
3096c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * needs to be running, and is also handy when debugging.
3106c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
3116c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private final LongSparseArray<AccountSyncState> mAccountStateMap;
3126c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
3136c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The {@link Service} that this object is managing. */
3146c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private final Service mService;
3156c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
3166c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    public PingSyncSynchronizer(final Service service) {
3176c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mLock = new ReentrantLock();
3186c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccountStateMap = new LongSparseArray<AccountSyncState>();
3196c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mService = service;
3206c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
3216c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
322c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    public Context getContext() {
323c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee        return mService;
324c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    }
325c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee
3266c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
3276c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Gets the {@link AccountSyncState} for an account.
3286c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * The caller must hold {@link #mLock}.
3296c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param accountId The id for the account we're interested in.
3306c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param createIfNeeded If true, create the account state if it's not already there.
3316c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @return The {@link AccountSyncState} for that account, or null if the account is idle and
3326c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     *         createIfNeeded is false.
3336c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
3346c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private AccountSyncState getAccountState(final long accountId, final boolean createIfNeeded) {
3356c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        assert mLock.isHeldByCurrentThread();
3366c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        AccountSyncState state = mAccountStateMap.get(accountId);
3376c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        if (state == null && createIfNeeded) {
33847b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS adding account state for acct:%d", accountId);
33947b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            state = new AccountSyncState(mLock, accountId);
3406c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mAccountStateMap.put(accountId, state);
3416c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            // TODO: Is this too late to startService?
3426c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mAccountStateMap.size() == 1) {
3436c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                LogUtils.i(TAG, "PSS added first account, starting service");
3446c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                mService.startService(new Intent(mService, mService.getClass()));
3456c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
3466c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
3476c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        return state;
3486c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
3496c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
3506c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
3516c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Remove an account from the map. If this was the last account, then also stop this service.
3526c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * The caller must hold {@link #mLock}.
3536c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param accountId The id for the account we're removing.
3546c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
3556c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private void removeAccount(final long accountId) {
3566c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        assert mLock.isHeldByCurrentThread();
35747b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon        LogUtils.i(TAG, "PSS removing account state for acct:%d", accountId);
3586c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccountStateMap.delete(accountId);
3596c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        if (mAccountStateMap.size() == 0) {
3606c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            LogUtils.i(TAG, "PSS removed last account; stopping service.");
3616c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mService.stopSelf();
3626c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
3636c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
3646c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
3656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    public void syncStart(final long accountId) {
3666c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mLock.lock();
3676c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        try {
36847b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS syncStart for account acct:%d", accountId);
3696c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            final AccountSyncState accountState = getAccountState(accountId, true);
3706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            accountState.syncStart();
3716c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        } finally {
3726c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mLock.unlock();
3736c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
3746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
3756c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
376451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    public void syncEnd(final boolean lastSyncHadError, final Account account) {
3776c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mLock.lock();
3786c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        try {
379c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee            final long accountId = account.getId();
38047b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS syncEnd for account acct:%d", account.getId());
3816c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            final AccountSyncState accountState = getAccountState(accountId, false);
3826c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (accountState == null) {
3836c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                LogUtils.w(TAG, "PSS syncEnd for account %d but no state found", accountId);
3846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                return;
3856c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
386451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon            if (accountState.syncEnd(lastSyncHadError, account, this)) {
3876c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                removeAccount(accountId);
3886c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
3896c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        } finally {
3906c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mLock.unlock();
3916c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
3926c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
3936c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
394c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    public void pingEnd(final long accountId, final android.accounts.Account amAccount) {
3956c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mLock.lock();
3966c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        try {
39747b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS pingEnd for account %d", accountId);
3986c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            final AccountSyncState accountState = getAccountState(accountId, false);
3996c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (accountState == null) {
4006c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                LogUtils.w(TAG, "PSS pingEnd for account %d but no state found", accountId);
4016c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                return;
4026c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
403c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee            if (accountState.pingEnd(amAccount)) {
4046c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                removeAccount(accountId);
4056c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
4066c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        } finally {
4076c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mLock.unlock();
4086c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
4096c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
4106c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
411c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    public void pushModify(final Account account) {
4126c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mLock.lock();
4136c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        try {
414c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee            final long accountId = account.getId();
41547b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS pushModify acct:%d", accountId);
4166c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            final AccountSyncState accountState = getAccountState(accountId, true);
417c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee            accountState.pushModify(account, this);
4186c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        } finally {
4196c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mLock.unlock();
4206c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
4216c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
4226c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
4236c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    public void pushStop(final long accountId) {
4246c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mLock.lock();
4256c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        try {
42647b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS pushStop acct:%d", accountId);
4276c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            final AccountSyncState accountState = getAccountState(accountId, false);
4286c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (accountState != null) {
4296c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                accountState.pushStop();
4306c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
4316c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        } finally {
4326c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mLock.unlock();
4336c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
4346c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
435dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu
436dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu    /**
437dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu     * Stops our service if our map contains no active accounts.
438dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu     */
439dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu    public void stopServiceIfIdle() {
440dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu        mLock.lock();
441dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu        try {
44247b5e0eb42afd02cb933ee3ed0d6c917056b744dMartin Hibdon            LogUtils.i(TAG, "PSS stopIfIdle");
443dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu            if (mAccountStateMap.size() == 0) {
444dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu                LogUtils.i(TAG, "PSS has no active accounts; stopping service.");
445dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu                mService.stopSelf();
446dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu            }
447dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu        } finally {
448dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu            mLock.unlock();
449dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu        }
450dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu    }
451dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu
452dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu    /**
453dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu     * Tells all running ping tasks to stop.
454dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu     */
455dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu    public void stopAllPings() {
456dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu        mLock.lock();
457dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu        try {
458dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu            for (int i = 0; i < mAccountStateMap.size(); ++i) {
459dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu                mAccountStateMap.valueAt(i).pushStop();
460dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu            }
461dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu        } finally {
462dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu            mLock.unlock();
463dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu        }
464dd316c9ade8257b2489c78fa3eb2763c9042b5ddYu Ping Hu    }
4656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu}
466