1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.exchange.service;
18
19import android.content.AbstractThreadedSyncAdapter;
20import android.content.ContentProviderClient;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.ServiceConnection;
24import android.content.SyncResult;
25import android.database.Cursor;
26import android.os.Bundle;
27import android.os.RemoteException;
28import android.provider.CalendarContract.Events;
29import android.util.Log;
30
31import com.android.emailcommon.provider.Account;
32import com.android.emailcommon.provider.EmailContent.MailboxColumns;
33import com.android.emailcommon.provider.Mailbox;
34import com.android.emailcommon.service.EmailServiceStatus;
35import com.android.exchange.Eas;
36import com.android.mail.utils.LogUtils;
37
38public class CalendarSyncAdapterService extends AbstractSyncAdapterService {
39    private static final String TAG = LogUtils.TAG;
40    private static final String ACCOUNT_AND_TYPE_CALENDAR =
41        MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CALENDAR;
42    private static final String DIRTY_IN_ACCOUNT =
43        Events.DIRTY + "=1 AND " + Events.ACCOUNT_NAME + "=?";
44
45    private static final Object sSyncAdapterLock = new Object();
46    private static AbstractThreadedSyncAdapter sSyncAdapter = null;
47
48    public CalendarSyncAdapterService() {
49        super();
50    }
51
52    @Override
53    protected AbstractThreadedSyncAdapter getSyncAdapter() {
54        synchronized (sSyncAdapterLock) {
55            if (sSyncAdapter == null) {
56                sSyncAdapter = new SyncAdapterImpl(this);
57            }
58            return sSyncAdapter;
59        }
60    }
61
62    private class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
63        public SyncAdapterImpl(Context context) {
64            super(context, true /* autoInitialize */);
65        }
66
67        @Override
68        public void onPerformSync(android.accounts.Account acct, Bundle extras,
69                String authority, ContentProviderClient provider, SyncResult syncResult) {
70            if (LogUtils.isLoggable(TAG, Log.DEBUG)) {
71                LogUtils.d(TAG, "onPerformSync calendar: %s, %s",
72                        acct.toString(), extras.toString());
73            } else {
74                LogUtils.i(TAG, "onPerformSync calendar: %s", extras.toString());
75            }
76
77            if (!waitForService()) {
78                // The service didn't connect, nothing we can do.
79                return;
80            }
81            final Account emailAccount = Account.restoreAccountWithAddress(
82                    CalendarSyncAdapterService.this, acct.name);
83            if (emailAccount == null) {
84                // There could be a timing issue with onPerformSync() being called and
85                // the account being removed from our database.
86                LogUtils.w(TAG,
87                        "onPerformSync() - Could not find an Account, skipping calendar sync.");
88                return;
89            }
90
91            // TODO: is this still needed?
92            if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
93                final Cursor c = getContentResolver().query(Events.CONTENT_URI,
94                        new String[] {Events._ID}, DIRTY_IN_ACCOUNT,
95                        new String[] {acct.name}, null);
96                if (c == null) {
97                    LogUtils.e(TAG, "Null changes cursor in CalendarSyncAdapterService");
98                    return;
99                }
100                try {
101                    if (!c.moveToFirst()) {
102                        if (Eas.USER_LOG) {
103                            LogUtils.d(TAG, "No changes for " + acct.name);
104                        }
105                        return;
106                    }
107                } finally {
108                    c.close();
109                }
110            }
111
112            // TODO: move this logic to some common place.
113            // Push only means this sync request should only refresh the ping (either because
114            // settings changed, or we need to restart it for some reason).
115            final boolean pushOnly = Mailbox.isPushOnlyExtras(extras);
116
117            if (pushOnly) {
118                LogUtils.d(TAG, "onPerformSync calendar: mailbox push only");
119                if (mEasService != null) {
120                    try {
121                        mEasService.pushModify(emailAccount.mId);
122                        return;
123                    } catch (final RemoteException re) {
124                        LogUtils.e(TAG, re, "While trying to pushModify within onPerformSync");
125                        // TODO: how to handle this?
126                    }
127                }
128                return;
129            } else {
130                try {
131                    final int result = mEasService.sync(emailAccount.mId, extras);
132                    writeResultToSyncResult(result, syncResult);
133                    if (syncResult.stats.numAuthExceptions > 0 &&
134                            result != EmailServiceStatus.PROVISIONING_ERROR) {
135                        showAuthNotification(emailAccount.mId, emailAccount.mEmailAddress);
136                    }
137                } catch (RemoteException e) {
138                    LogUtils.e(TAG, e, "While trying to pushModify within onPerformSync");
139                }
140            }
141
142            LogUtils.d(TAG, "onPerformSync calendar: finished");
143        }
144    }
145}
146