diff --git a/Android.mk b/Android.mk
index 3d257446c..c1b354e5e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -43,7 +43,7 @@ LOCAL_ASSET_DIR := $(LOCAL_PATH)/$(unified_email_dir)/assets
LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips:com.android.mail:com.android.email:com.android.emailcommon:com.android.ex.photo:android.support.v7.gridlayout:com.android.datetimepicker
-LOCAL_STATIC_JAVA_LIBRARIES := android-common com.android.emailcommon com.android.emailsync guava libchips libphotoviewer
+LOCAL_STATIC_JAVA_LIBRARIES := android-common com.android.emailcommon guava libchips libphotoviewer
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-gridlayout
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
diff --git a/emailsync/Android.mk b/emailsync/Android.mk
deleted file mode 100644
index 5f18d175d..000000000
--- a/emailsync/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright 2012, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-# Build the com.android.emailcommon static library. At the moment, this includes
-# the emailcommon files themselves plus everything under src/org (apache code). All of our
-# AIDL files are also compiled into the static library
-
-include $(CLEAR_VARS)
-
-
-LOCAL_MODULE := com.android.emailsync
-LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/emailsync)
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.emailcommon
-
-LOCAL_SDK_VERSION := 14
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/emailsync/AndroidManifest.xml b/emailsync/AndroidManifest.xml
deleted file mode 100644
index 4659bbbba..000000000
--- a/emailsync/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/emailsync/src/com/android/emailsync/AbstractSyncService.java b/emailsync/src/com/android/emailsync/AbstractSyncService.java
deleted file mode 100644
index 6c8f2e594..000000000
--- a/emailsync/src/com/android/emailsync/AbstractSyncService.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2008-2009 Marc Blank
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.emailsync;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.text.format.DateUtils;
-
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.provider.Mailbox;
-import com.android.mail.utils.LogUtils;
-
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Base class for all protocol services SyncManager (extends Service, implements
- * Runnable) instantiates subclasses to run a sync (either timed, or push, or
- * mail placed in outbox, etc.) EasSyncService is currently implemented; my goal
- * would be to move IMAP to this structure when it comes time to introduce push
- * functionality.
- */
-public abstract class AbstractSyncService implements Runnable {
-
- public String TAG = "AbstractSyncService";
-
- public static final int EXIT_DONE = 0;
- public static final int EXIT_IO_ERROR = 1;
- public static final int EXIT_LOGIN_FAILURE = 2;
- public static final int EXIT_EXCEPTION = 3;
- public static final int EXIT_SECURITY_FAILURE = 4;
- public static final int EXIT_ACCESS_DENIED = 5;
-
- public Mailbox mMailbox;
- protected long mMailboxId;
- protected int mExitStatus = EXIT_EXCEPTION;
- protected String mExitReason;
- protected String mMailboxName;
- public Account mAccount;
- public Context mContext;
- public int mChangeCount = 0;
- public volatile int mSyncReason = 0;
- protected volatile boolean mStop = false;
- public volatile Thread mThread;
- protected final Object mSynchronizer = new Object();
- // Whether or not the sync service is valid (usable)
- public boolean mIsValid = true;
-
- public boolean mUserLog = false;
- public boolean mFileLog = false;
-
- protected volatile long mRequestTime = 0;
- protected LinkedBlockingQueue mRequestQueue = new LinkedBlockingQueue();
-
- /**
- * Sent by SyncManager to request that the service stop itself cleanly
- */
- public abstract void stop();
-
- /**
- * Sent by SyncManager to indicate that an alarm has fired for this service, and that its
- * pending (network) operation has timed out. The service is NOT automatically stopped,
- * although the behavior is service dependent.
- *
- * @return true if the operation was stopped normally; false if the thread needed to be
- * interrupted.
- */
- public abstract boolean alarm();
-
- /**
- * Sent by SyncManager to request that the service reset itself cleanly; the meaning of this
- * operation is service dependent.
- */
- public abstract void reset();
-
- /**
- * Called to validate an account; abstract to allow each protocol to do what
- * is necessary. For consistency with the Email app's original
- * functionality, success is indicated by a failure to throw an Exception
- * (ugh). Parameters are self-explanatory
- *
- * @param hostAuth
- * @return a Bundle containing a result code and, depending on the result, a PolicySet or an
- * error message
- */
- public abstract Bundle validateAccount(HostAuth hostAuth, Context context);
-
- /**
- * Called to clear the syncKey for the calendar associated with this service; this is necessary
- * because changes to calendar sync state cause a reset of data.
- */
- public abstract void resetCalendarSyncKey();
-
- public AbstractSyncService(Context _context, Mailbox _mailbox) {
- mContext = _context;
- mMailbox = _mailbox;
- mMailboxId = _mailbox.mId;
- mMailboxName = _mailbox.mServerId;
- mAccount = Account.restoreAccountWithId(_context, _mailbox.mAccountKey);
- }
-
- // Will be required when subclasses are instantiated by name
- public AbstractSyncService(String prefix) {
- }
-
- /**
- * The UI can call this static method to perform account validation. This method wraps each
- * protocol's validateAccount method. Arguments are self-explanatory, except where noted.
- *
- * @param klass the protocol class (EasSyncService.class for example)
- * @param hostAuth
- * @param context
- * @return a Bundle containing a result code and, depending on the result, a PolicySet or an
- * error message
- */
- public static Bundle validate(Class extends AbstractSyncService> klass,
- HostAuth hostAuth, Context context) {
- AbstractSyncService svc;
- try {
- svc = klass.newInstance();
- return svc.validateAccount(hostAuth, context);
- } catch (IllegalAccessException e) {
- } catch (InstantiationException e) {
- }
- return null;
- }
-
- public static class ValidationResult {
- static final int NO_FAILURE = 0;
- static final int CONNECTION_FAILURE = 1;
- static final int VALIDATION_FAILURE = 2;
- static final int EXCEPTION = 3;
-
- static final ValidationResult succeeded = new ValidationResult(true, NO_FAILURE, null);
- boolean success;
- int failure = NO_FAILURE;
- String reason = null;
- Exception exception = null;
-
- ValidationResult(boolean _success, int _failure, String _reason) {
- success = _success;
- failure = _failure;
- reason = _reason;
- }
-
- ValidationResult(boolean _success) {
- success = _success;
- }
-
- ValidationResult(Exception e) {
- success = false;
- failure = EXCEPTION;
- exception = e;
- }
-
- public boolean isSuccess() {
- return success;
- }
-
- public String getReason() {
- return reason;
- }
- }
-
- public boolean isStopped() {
- return mStop;
- }
-
- public Object getSynchronizer() {
- return mSynchronizer;
- }
-
- /**
- * Convenience methods to do user logging (i.e. connection activity). Saves a bunch of
- * repetitive code.
- */
- public void userLog(String string, int code, String string2) {
- if (mUserLog) {
- userLog(string + code + string2);
- }
- }
-
- public void userLog(String string, int code) {
- if (mUserLog) {
- userLog(string + code);
- }
- }
-
- public void userLog(String str, Exception e) {
- if (mUserLog) {
- LogUtils.e(TAG, str, e);
- } else {
- LogUtils.e(TAG, str + e);
- }
- if (mFileLog) {
- FileLogger.log(e);
- }
- }
-
- /**
- * Standard logging for EAS.
- * If user logging is active, we concatenate any arguments and log them using LogUtils.d
- * We also check for file logging, and log appropriately
- * @param strings strings to concatenate and log
- */
- public void userLog(String ...strings) {
- if (mUserLog) {
- String logText;
- if (strings.length == 1) {
- logText = strings[0];
- } else {
- StringBuilder sb = new StringBuilder(64);
- for (String string: strings) {
- sb.append(string);
- }
- logText = sb.toString();
- }
- LogUtils.d(TAG, logText);
- if (mFileLog) {
- FileLogger.log(TAG, logText);
- }
- }
- }
-
- /**
- * Error log is used for serious issues that should always be logged
- * @param str the string to log
- */
- public void errorLog(String str) {
- LogUtils.e(TAG, str);
- if (mFileLog) {
- FileLogger.log(TAG, str);
- }
- }
-
- /**
- * Waits for up to 10 seconds for network connectivity; returns whether or not there is
- * network connectivity.
- *
- * @return whether there is network connectivity
- */
- public boolean hasConnectivity() {
- ConnectivityManager cm =
- (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- int tries = 0;
- while (tries++ < 1) {
- // Use the same test as in ExchangeService#waitForConnectivity
- // TODO: Create common code for this test in emailcommon
- NetworkInfo info = cm.getActiveNetworkInfo();
- if (info != null) {
- return true;
- }
- try {
- Thread.sleep(10 * DateUtils.SECOND_IN_MILLIS);
- } catch (InterruptedException e) {
- }
- }
- return false;
- }
-
- /**
- * Request handling (common functionality)
- * Can be overridden if desired
- */
-
- public void addRequest(Request req) {
- if (!mRequestQueue.contains(req)) {
- mRequestQueue.offer(req);
- }
- }
-
- public void removeRequest(Request req) {
- mRequestQueue.remove(req);
- }
-
- public boolean hasPendingRequests() {
- return !mRequestQueue.isEmpty();
- }
-
- public void clearRequests() {
- mRequestQueue.clear();
- }
-}
diff --git a/emailsync/src/com/android/emailsync/EmailSyncAlarmReceiver.java b/emailsync/src/com/android/emailsync/EmailSyncAlarmReceiver.java
deleted file mode 100644
index a52c7e53a..000000000
--- a/emailsync/src/com/android/emailsync/EmailSyncAlarmReceiver.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2008-2009 Marc Blank
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.emailsync;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-
-import com.android.emailcommon.provider.EmailContent.Message;
-import com.android.emailcommon.provider.EmailContent.MessageColumns;
-import com.android.emailcommon.provider.ProviderUnavailableException;
-import com.android.mail.utils.LogUtils;
-
-import java.util.ArrayList;
-
-/**
- * EmailSyncAlarmReceiver (USAR) is used by the SyncManager to start up-syncs of user-modified data
- * back to the Exchange server.
- *
- * Here's how this works for Email, for example:
- *
- * 1) User modifies or deletes an email from the UI.
- * 2) SyncManager, which has a ContentObserver watching the Message class, is alerted to a change
- * 3) SyncManager sets an alarm (to be received by USAR) for a few seconds in the
- * future (currently 15), the delay preventing excess syncing (think of it as a debounce mechanism).
- * 4) ESAR Receiver's onReceive method is called
- * 5) ESAR goes through all change and deletion records and compiles a list of mailboxes which have
- * changes to be uploaded.
- * 6) ESAR calls SyncManager to start syncs of those mailboxes
- *
- * If EmailProvider isn't available, the upsyncs will happen the next time ExchangeService starts
- *
- */
-public class EmailSyncAlarmReceiver extends BroadcastReceiver {
- final String[] MAILBOX_DATA_PROJECTION = {MessageColumns.MAILBOX_KEY};
-
- @Override
- public void onReceive(final Context context, Intent intent) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- handleReceive(context);
- }
- }).start();
- }
-
- private void handleReceive(Context context) {
- ArrayList mailboxesToNotify = new ArrayList();
- ContentResolver cr = context.getContentResolver();
-
- // Get a selector for EAS accounts (we don't want to sync on changes to POP/IMAP messages)
- String selector = SyncManager.getAccountSelector();
-
- try {
- // Find all of the deletions
- Cursor c = cr.query(Message.DELETED_CONTENT_URI, MAILBOX_DATA_PROJECTION, selector,
- null, null);
- if (c == null) throw new ProviderUnavailableException();
- try {
- // Keep track of which mailboxes to notify; we'll only notify each one once
- while (c.moveToNext()) {
- long mailboxId = c.getLong(0);
- if (!mailboxesToNotify.contains(mailboxId)) {
- mailboxesToNotify.add(mailboxId);
- }
- }
- } finally {
- c.close();
- }
-
- // Now, find changed messages
- c = cr.query(Message.UPDATED_CONTENT_URI, MAILBOX_DATA_PROJECTION, selector,
- null, null);
- if (c == null) throw new ProviderUnavailableException();
- try {
- // Keep track of which mailboxes to notify; we'll only notify each one once
- while (c.moveToNext()) {
- long mailboxId = c.getLong(0);
- if (!mailboxesToNotify.contains(mailboxId)) {
- mailboxesToNotify.add(mailboxId);
- }
- }
- } finally {
- c.close();
- }
-
- // Request service from the mailbox
- for (Long mailboxId: mailboxesToNotify) {
- SyncManager.serviceRequest(mailboxId, SyncManager.SYNC_UPSYNC);
- }
- } catch (ProviderUnavailableException e) {
- LogUtils.e("EmailSyncAlarmReceiver",
- "EmailProvider unavailable; aborting alarm receiver");
- }
- }
-}
diff --git a/emailsync/src/com/android/emailsync/FileLogger.java b/emailsync/src/com/android/emailsync/FileLogger.java
deleted file mode 100644
index db8b62605..000000000
--- a/emailsync/src/com/android/emailsync/FileLogger.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.emailsync;
-
-import android.content.Context;
-import android.os.Environment;
-
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Date;
-
-public class FileLogger {
- private static FileLogger LOGGER = null;
- private static FileWriter sLogWriter = null;
- public static String LOG_FILE_NAME =
- Environment.getExternalStorageDirectory() + "/emaillog.txt";
-
- public synchronized static FileLogger getLogger (Context c) {
- LOGGER = new FileLogger();
- return LOGGER;
- }
-
- private FileLogger() {
- try {
- sLogWriter = new FileWriter(LOG_FILE_NAME, true);
- } catch (IOException e) {
- // Doesn't matter
- }
- }
-
- static public synchronized void close() {
- if (sLogWriter != null) {
- try {
- sLogWriter.close();
- } catch (IOException e) {
- // Doesn't matter
- }
- sLogWriter = null;
- }
- }
-
- static public synchronized void log(Exception e) {
- if (sLogWriter != null) {
- log("Exception", "Stack trace follows...");
- PrintWriter pw = new PrintWriter(sLogWriter);
- e.printStackTrace(pw);
- pw.flush();
- }
- }
-
- @SuppressWarnings("deprecation")
- static public synchronized void log(String prefix, String str) {
- if (LOGGER == null) {
- LOGGER = new FileLogger();
- log("Logger", "\r\n\r\n --- New Log ---");
- }
- Date d = new Date();
- int hr = d.getHours();
- int min = d.getMinutes();
- int sec = d.getSeconds();
-
- // I don't use DateFormat here because (in my experience), it's much slower
- StringBuffer sb = new StringBuffer(256);
- sb.append('[');
- sb.append(hr);
- sb.append(':');
- if (min < 10)
- sb.append('0');
- sb.append(min);
- sb.append(':');
- if (sec < 10) {
- sb.append('0');
- }
- sb.append(sec);
- sb.append("] ");
- if (prefix != null) {
- sb.append(prefix);
- sb.append("| ");
- }
- sb.append(str);
- sb.append("\r\n");
- String s = sb.toString();
-
- if (sLogWriter != null) {
- try {
- sLogWriter.write(s);
- sLogWriter.flush();
- } catch (IOException e) {
- // Something might have happened to the sdcard
- if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
- // If the card is mounted and we can create the writer, retry
- LOGGER = new FileLogger();
- if (sLogWriter != null) {
- try {
- log("FileLogger", "Exception writing log; recreating...");
- log(prefix, str);
- } catch (Exception e1) {
- // Nothing to do at this point
- }
- }
- }
- }
- }
- }
-}
diff --git a/emailsync/src/com/android/emailsync/MailboxAlarmReceiver.java b/emailsync/src/com/android/emailsync/MailboxAlarmReceiver.java
deleted file mode 100644
index 810579924..000000000
--- a/emailsync/src/com/android/emailsync/MailboxAlarmReceiver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2008-2009 Marc Blank
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.emailsync;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-
-/**
- * MailboxAlarmReceiver is used to "wake up" the ExchangeService at the appropriate time(s). It may
- * also be used for individual sync adapters, but this isn't implemented at the present time.
- *
- */
-public class MailboxAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- long mailboxId = intent.getLongExtra("mailbox", SyncManager.EXTRA_MAILBOX_ID);
- // EXCHANGE_SERVICE_MAILBOX_ID tells us that the service is asking to be started
- if (mailboxId == SyncManager.SYNC_SERVICE_MAILBOX_ID) {
- context.startService(new Intent(context, SyncManager.class));
- } else {
- SyncManager.alert(context, mailboxId);
- }
- }
-}
-
diff --git a/emailsync/src/com/android/emailsync/MessageMoveRequest.java b/emailsync/src/com/android/emailsync/MessageMoveRequest.java
deleted file mode 100644
index a741a982b..000000000
--- a/emailsync/src/com/android/emailsync/MessageMoveRequest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.emailsync;
-
-import com.android.emailsync.Request;
-
-/**
- * MessageMoveRequest is the EAS wrapper for requesting a "move to folder"
- */
-public class MessageMoveRequest extends Request {
- public final long mMailboxId;
-
- public MessageMoveRequest(long messageId, long mailboxId) {
- super(messageId);
- mMailboxId = mailboxId;
- }
-
- // MessageMoveRequests are unique by their message id (i.e. it's meaningless to have two
- // separate message moves queued at the same time)
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof MessageMoveRequest)) return false;
- return ((MessageMoveRequest)o).mMessageId == mMessageId;
- }
-
- @Override
- public int hashCode() {
- return (int)mMessageId;
- }
-}
diff --git a/emailsync/src/com/android/emailsync/Request.java b/emailsync/src/com/android/emailsync/Request.java
deleted file mode 100644
index 655b301f7..000000000
--- a/emailsync/src/com/android/emailsync/Request.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.emailsync;
-
-/**
- * Requests for mailbox actions are handled by subclasses of this abstract class.
- * Three subclasses are now defined: PartRequest (attachment load), MeetingResponseRequest
- * (respond to a meeting invitation), and MessageMoveRequest (move a message to another folder)
- */
-public abstract class Request {
- public final long mTimeStamp = System.currentTimeMillis();
- public final long mMessageId;
-
- public Request(long messageId) {
- mMessageId = messageId;
- }
-
- // Subclasses of Request may have different semantics regarding equality; therefore,
- // we force them to implement the equals method
- @Override
- public abstract boolean equals(Object o);
- @Override
- public abstract int hashCode();
-}
diff --git a/emailsync/src/com/android/emailsync/SyncManager.java b/emailsync/src/com/android/emailsync/SyncManager.java
deleted file mode 100644
index 195a0ec6b..000000000
--- a/emailsync/src/com/android/emailsync/SyncManager.java
+++ /dev/null
@@ -1,2288 +0,0 @@
-/*
- * Copyright (C) 2008-2009 Marc Blank
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.emailsync;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.State;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.Process;
-import android.provider.CalendarContract;
-import android.provider.CalendarContract.Calendars;
-import android.provider.CalendarContract.Events;
-import android.provider.ContactsContract;
-
-import com.android.emailcommon.TempDirectory;
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.EmailContent;
-import com.android.emailcommon.provider.EmailContent.Body;
-import com.android.emailcommon.provider.EmailContent.BodyColumns;
-import com.android.emailcommon.provider.EmailContent.MailboxColumns;
-import com.android.emailcommon.provider.EmailContent.Message;
-import com.android.emailcommon.provider.EmailContent.MessageColumns;
-import com.android.emailcommon.provider.EmailContent.SyncColumns;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.provider.Mailbox;
-import com.android.emailcommon.provider.Policy;
-import com.android.emailcommon.provider.ProviderUnavailableException;
-import com.android.emailcommon.service.AccountServiceProxy;
-import com.android.emailcommon.service.EmailServiceProxy;
-import com.android.emailcommon.service.IEmailServiceCallback.Stub;
-import com.android.emailcommon.service.PolicyServiceProxy;
-import com.android.emailcommon.utility.EmailClientConnectionManager;
-import com.android.emailcommon.utility.Utility;
-import com.android.mail.utils.LogUtils;
-
-import org.apache.http.conn.params.ConnManagerPNames;
-import org.apache.http.conn.params.ConnPerRoute;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpParams;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * The SyncServiceManager handles the lifecycle of various sync adapters used by services that
- * cannot rely on the system SyncManager
- *
- * SyncServiceManager uses ContentObservers to detect changes to accounts, mailboxes, & messages in
- * order to maintain proper 2-way syncing of data. (More documentation to follow)
- *
- */
-public abstract class SyncManager extends Service implements Runnable {
-
- private static String TAG = "SyncManager";
-
- // The SyncServiceManager's mailbox "id"
- public static final int EXTRA_MAILBOX_ID = -1;
- public static final int SYNC_SERVICE_MAILBOX_ID = 0;
-
- private static final int SECONDS = 1000;
- private static final int MINUTES = 60*SECONDS;
- private static final int ONE_DAY_MINUTES = 1440;
-
- private static final int SYNC_SERVICE_HEARTBEAT_TIME = 15*MINUTES;
- private static final int CONNECTIVITY_WAIT_TIME = 10*MINUTES;
-
- // Sync hold constants for services with transient errors
- private static final int HOLD_DELAY_MAXIMUM = 4*MINUTES;
-
- // Reason codes when SyncServiceManager.kick is called (mainly for debugging)
- // UI has changed data, requiring an upsync of changes
- public static final int SYNC_UPSYNC = 0;
- // A scheduled sync (when not using push)
- public static final int SYNC_SCHEDULED = 1;
- // Mailbox was marked push
- public static final int SYNC_PUSH = 2;
- // A ping (EAS push signal) was received
- public static final int SYNC_PING = 3;
- // Misc.
- public static final int SYNC_KICK = 4;
- // A part request (attachment load, for now) was sent to SyncServiceManager
- public static final int SYNC_SERVICE_PART_REQUEST = 5;
-
- // Requests >= SYNC_CALLBACK_START generate callbacks to the UI
- public static final int SYNC_CALLBACK_START = 6;
- // startSync was requested of SyncServiceManager (other than due to user request)
- public static final int SYNC_SERVICE_START_SYNC = SYNC_CALLBACK_START + 0;
- // startSync was requested of SyncServiceManager (due to user request)
- public static final int SYNC_UI_REQUEST = SYNC_CALLBACK_START + 1;
-
- protected static final String WHERE_IN_ACCOUNT_AND_PUSHABLE =
- MailboxColumns.ACCOUNT_KEY + "=? and type in (" + Mailbox.TYPE_INBOX + ','
- + Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + ',' + Mailbox.TYPE_CONTACTS + ','
- + Mailbox.TYPE_CALENDAR + ')';
- protected static final String WHERE_IN_ACCOUNT_AND_TYPE_INBOX =
- MailboxColumns.ACCOUNT_KEY + "=? and type = " + Mailbox.TYPE_INBOX ;
- private static final String WHERE_MAILBOX_KEY = MessageColumns.MAILBOX_KEY + "=?";
- private static final String WHERE_NOT_INTERVAL_NEVER_AND_ACCOUNT_KEY_IN =
- "(" + MailboxColumns.TYPE + '=' + Mailbox.TYPE_OUTBOX
- + " or " + MailboxColumns.SYNC_INTERVAL + "<" + Mailbox.CHECK_INTERVAL_NEVER + ')'
- + " and " + MailboxColumns.ACCOUNT_KEY + " in (";
-
- public static final int SEND_FAILED = 1;
- public static final String MAILBOX_KEY_AND_NOT_SEND_FAILED =
- MessageColumns.MAILBOX_KEY + "=? and (" + SyncColumns.SERVER_ID + " is null or " +
- SyncColumns.SERVER_ID + "!=" + SEND_FAILED + ')';
-
- public static final String CALENDAR_SELECTION =
- Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?";
- private static final String WHERE_CALENDAR_ID = Events.CALENDAR_ID + "=?";
-
- // Offsets into the syncStatus data for EAS that indicate type, exit status, and change count
- // The format is S::
- public static final int STATUS_TYPE_CHAR = 1;
- public static final int STATUS_EXIT_CHAR = 3;
- public static final int STATUS_CHANGE_COUNT_OFFSET = 5;
-
- // Ready for ping
- public static final int PING_STATUS_OK = 0;
- // Service already running (can't ping)
- public static final int PING_STATUS_RUNNING = 1;
- // Service waiting after I/O error (can't ping)
- public static final int PING_STATUS_WAITING = 2;
- // Service had a fatal error; can't run
- public static final int PING_STATUS_UNABLE = 3;
- // Service is disabled by user (checkbox)
- public static final int PING_STATUS_DISABLED = 4;
-
- private static final int MAX_CLIENT_CONNECTION_MANAGER_SHUTDOWNS = 1;
-
- // We synchronize on this for all actions affecting the service and error maps
- private static final Object sSyncLock = new Object();
- // All threads can use this lock to wait for connectivity
- public static final Object sConnectivityLock = new Object();
- public static boolean sConnectivityHold = false;
-
- // Keeps track of running services (by mailbox id)
- public final HashMap mServiceMap =
- new HashMap();
- // Keeps track of services whose last sync ended with an error (by mailbox id)
- /*package*/ public ConcurrentHashMap mSyncErrorMap =
- new ConcurrentHashMap();
- // Keeps track of which services require a wake lock (by mailbox id)
- private final HashMap mWakeLocks = new HashMap();
- // Keeps track of which services have held a wake lock (by mailbox id)
- private final HashMap mWakeLocksHistory = new HashMap();
- // Keeps track of PendingIntents for mailbox alarms (by mailbox id)
- private final HashMap mPendingIntents = new HashMap();
- // The actual WakeLock obtained by SyncServiceManager
- private WakeLock mWakeLock = null;
- // Keep our cached list of active Accounts here
- public final AccountList mAccountList = new AccountList();
- // Keep track of when we started up
- private long mServiceStartTime;
-
- // Observers that we use to look for changed mail-related data
- private final Handler mHandler = new Handler();
- private AccountObserver mAccountObserver;
- private MailboxObserver mMailboxObserver;
- private SyncedMessageObserver mSyncedMessageObserver;
-
- // Concurrent because CalendarSyncAdapter can modify the map during a wipe
- private final ConcurrentHashMap mCalendarObservers =
- new ConcurrentHashMap();
-
- public ContentResolver mResolver;
-
- // The singleton SyncServiceManager object, with its thread and stop flag
- protected static SyncManager INSTANCE;
- protected static Thread sServiceThread = null;
- // Cached unique device id
- protected static String sDeviceId = null;
- // HashMap of ConnectionManagers that all EAS threads can use (by HostAuth id)
- private static HashMap sClientConnectionManagers =
- new HashMap();
- // Count of ClientConnectionManager shutdowns
- private static volatile int sClientConnectionManagerShutdownCount = 0;
-
- private static volatile boolean sStartingUp = false;
- private static volatile boolean sStop = false;
-
- // The reason for SyncServiceManager's next wakeup call
- private String mNextWaitReason;
- // Whether we have an unsatisfied "kick" pending
- private boolean mKicked = false;
-
- // Receiver of connectivity broadcasts
- private ConnectivityReceiver mConnectivityReceiver = null;
- // The most current NetworkInfo (from ConnectivityManager)
- private NetworkInfo mNetworkInfo;
-
- // For sync logging
- protected static boolean sUserLog = false;
- protected static boolean sFileLog = false;
-
- /**
- * Return an AccountObserver for this manager; the subclass must implement the newAccount()
- * method, which is called whenever the observer discovers that a new account has been created.
- * The subclass should do any housekeeping necessary
- * @param handler a Handler
- * @return the AccountObserver
- */
- public abstract AccountObserver getAccountObserver(Handler handler);
-
- /**
- * Perform any housekeeping necessary upon startup of the manager
- */
- public abstract void onStartup();
-
- /**
- * Returns a String that can be used as a WHERE clause in SQLite that selects accounts whose
- * syncs are managed by this manager
- * @return the account selector String
- */
- public abstract String getAccountsSelector();
-
- /**
- * Returns an appropriate sync service for the passed in mailbox
- * @param context the caller's context
- * @param mailbox the Mailbox to be synced
- * @return a service that will sync the Mailbox
- */
- public abstract AbstractSyncService getServiceForMailbox(Context context, Mailbox mailbox);
-
- /**
- * Return a list of all Accounts in EmailProvider. Because the result of this call may be used
- * in account reconciliation, an exception is thrown if the result cannot be guaranteed accurate
- * @param context the caller's context
- * @param accounts a list that Accounts will be added into
- * @return the list of Accounts
- * @throws ProviderUnavailableException if the list of Accounts cannot be guaranteed valid
- */
- public abstract AccountList collectAccounts(Context context, AccountList accounts);
-
- /**
- * Returns the AccountManager type (e.g. com.android.exchange) for this sync service
- */
- public abstract String getAccountManagerType();
-
- /**
- * Returns the intent used for this sync service
- */
- public abstract Intent getServiceIntent();
-
- /**
- * Returns the callback proxy used for communicating back with the Email app
- */
- public abstract Stub getCallbackProxy();
-
- /**
- * Called when a sync service has started (in case any action is needed). This method must
- * not perform any long-lived actions (db access, network access, etc)
- */
- public abstract void onStartService(Mailbox mailbox);
-
- public class AccountList extends ArrayList {
- private static final long serialVersionUID = 1L;
-
- private final WeakHashMap mAmMap =
- new WeakHashMap();
-
- @Override
- public boolean add(Account account) {
- // Cache the account manager account
- mAmMap.put(account, account.getAccountManagerAccount(getAccountManagerType()));
- super.add(account);
- return true;
- }
-
- public android.accounts.Account getAmAccount(Account account) {
- return mAmMap.get(account);
- }
-
- public boolean contains(long id) {
- for (Account account : this) {
- if (account.mId == id) {
- return true;
- }
- }
- return false;
- }
-
- public Account getById(long id) {
- for (Account account : this) {
- if (account.mId == id) {
- return account;
- }
- }
- return null;
- }
-
- public Account getByName(String accountName) {
- for (Account account : this) {
- if (account.mEmailAddress.equalsIgnoreCase(accountName)) {
- return account;
- }
- }
- return null;
- }
- }
-
- public static void setUserDebug(int state) {
- sUserLog = (state & EmailServiceProxy.DEBUG_BIT) != 0;
- sFileLog = (state & EmailServiceProxy.DEBUG_FILE_BIT) != 0;
- if (sFileLog) {
- sUserLog = true;
- }
- LogUtils.d("Sync Debug", "Logging: " + (sUserLog ? "User " : "")
- + (sFileLog ? "File" : ""));
- }
-
- private static boolean onSecurityHold(Account account) {
- return (account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0;
- }
-
- public static String getAccountSelector() {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return "";
- return ssm.getAccountsSelector();
- }
-
- public abstract class AccountObserver extends ContentObserver {
- String mSyncableMailboxSelector = null;
- String mAccountSelector = null;
-
- // Runs when SyncServiceManager first starts
- @SuppressWarnings("deprecation")
- public AccountObserver(Handler handler) {
- super(handler);
- // At startup, we want to see what EAS accounts exist and cache them
- // TODO: Move database work out of UI thread
- Context context = getContext();
- synchronized (mAccountList) {
- try {
- collectAccounts(context, mAccountList);
- } catch (ProviderUnavailableException e) {
- // Just leave if EmailProvider is unavailable
- return;
- }
- // Create an account mailbox for any account without one
- for (Account account : mAccountList) {
- int cnt = Mailbox.count(context, Mailbox.CONTENT_URI, "accountKey="
- + account.mId, null);
- if (cnt == 0) {
- // This case handles a newly created account
- newAccount(account.mId);
- }
- }
- }
- // Run through accounts and update account hold information
- Utility.runAsync(new Runnable() {
- @Override
- public void run() {
- synchronized (mAccountList) {
- for (Account account : mAccountList) {
- if (onSecurityHold(account)) {
- // If we're in a security hold, and our policies are active, release
- // the hold
- if (PolicyServiceProxy.isActive(SyncManager.this, null)) {
- PolicyServiceProxy.setAccountHoldFlag(SyncManager.this,
- account, false);
- log("isActive true; release hold for " + account.mDisplayName);
- }
- }
- }
- }
- }});
- }
-
- /**
- * Returns a String suitable for appending to a where clause that selects for all syncable
- * mailboxes in all eas accounts
- * @return a complex selection string that is not to be cached
- */
- public String getSyncableMailboxWhere() {
- if (mSyncableMailboxSelector == null) {
- StringBuilder sb = new StringBuilder(WHERE_NOT_INTERVAL_NEVER_AND_ACCOUNT_KEY_IN);
- boolean first = true;
- synchronized (mAccountList) {
- for (Account account : mAccountList) {
- if (!first) {
- sb.append(',');
- } else {
- first = false;
- }
- sb.append(account.mId);
- }
- }
- sb.append(')');
- mSyncableMailboxSelector = sb.toString();
- }
- return mSyncableMailboxSelector;
- }
-
- private void onAccountChanged() {
- try {
- maybeStartSyncServiceManagerThread();
- Context context = getContext();
-
- // A change to the list requires us to scan for deletions (stop running syncs)
- // At startup, we want to see what accounts exist and cache them
- AccountList currentAccounts = new AccountList();
- try {
- collectAccounts(context, currentAccounts);
- } catch (ProviderUnavailableException e) {
- // Just leave if EmailProvider is unavailable
- return;
- }
- synchronized (mAccountList) {
- for (Account account : mAccountList) {
- boolean accountIncomplete =
- (account.mFlags & Account.FLAGS_INCOMPLETE) != 0;
- // If the current list doesn't include this account and the account wasn't
- // incomplete, then this is a deletion
- if (!currentAccounts.contains(account.mId) && !accountIncomplete) {
- // The implication is that the account has been deleted; let's find out
- alwaysLog("Observer found deleted account: " + account.mDisplayName);
- // Run the reconciler (the reconciliation itself runs in the Email app)
- runAccountReconcilerSync(SyncManager.this);
- // See if the account is still around
- Account deletedAccount =
- Account.restoreAccountWithId(context, account.mId);
- if (deletedAccount != null) {
- // It is; add it to our account list
- alwaysLog("Account still in provider: " + account.mDisplayName);
- currentAccounts.add(account);
- } else {
- // It isn't; stop syncs and clear our selectors
- alwaysLog("Account deletion confirmed: " + account.mDisplayName);
- stopAccountSyncs(account.mId, true);
- mSyncableMailboxSelector = null;
- mAccountSelector = null;
- }
- } else {
- // Get the newest version of this account
- Account updatedAccount =
- Account.restoreAccountWithId(context, account.mId);
- if (updatedAccount == null) continue;
- if (account.mSyncInterval != updatedAccount.mSyncInterval
- || account.mSyncLookback != updatedAccount.mSyncLookback) {
- // Set the inbox interval to the interval of the Account
- // This setting should NOT affect other boxes
- ContentValues cv = new ContentValues();
- cv.put(MailboxColumns.SYNC_INTERVAL, updatedAccount.mSyncInterval);
- getContentResolver().update(Mailbox.CONTENT_URI, cv,
- WHERE_IN_ACCOUNT_AND_TYPE_INBOX, new String[] {
- Long.toString(account.mId)
- });
- // Stop all current syncs; the appropriate ones will restart
- log("Account " + account.mDisplayName + " changed; stop syncs");
- stopAccountSyncs(account.mId, true);
- }
-
- // See if this account is no longer on security hold
- if (onSecurityHold(account) && !onSecurityHold(updatedAccount)) {
- releaseSyncHolds(SyncManager.this,
- AbstractSyncService.EXIT_SECURITY_FAILURE, account);
- }
-
- // Put current values into our cached account
- account.mSyncInterval = updatedAccount.mSyncInterval;
- account.mSyncLookback = updatedAccount.mSyncLookback;
- account.mFlags = updatedAccount.mFlags;
- }
- }
- // Look for new accounts
- for (Account account : currentAccounts) {
- if (!mAccountList.contains(account.mId)) {
- // Don't forget to cache the HostAuth
- HostAuth ha = HostAuth.restoreHostAuthWithId(getContext(),
- account.mHostAuthKeyRecv);
- if (ha == null) continue;
- account.mHostAuthRecv = ha;
- // This is an addition; create our magic hidden mailbox...
- log("Account observer found new account: " + account.mDisplayName);
- newAccount(account.mId);
- mAccountList.add(account);
- mSyncableMailboxSelector = null;
- mAccountSelector = null;
- }
- }
- // Finally, make sure our account list is up to date
- mAccountList.clear();
- mAccountList.addAll(currentAccounts);
- }
-
- // See if there's anything to do...
- kick("account changed");
- } catch (ProviderUnavailableException e) {
- alwaysLog("Observer failed; provider unavailable");
- }
- }
-
- @Override
- public void onChange(boolean selfChange) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- onAccountChanged();
- }}, "Account Observer").start();
- }
-
- public abstract void newAccount(long acctId);
- }
-
- /**
- * Register a specific Calendar's data observer; we need to recognize when the SYNC_EVENTS
- * column has changed (when sync has turned off or on)
- * @param account the Account whose Calendar we're observing
- */
- private void registerCalendarObserver(Account account) {
- // Get a new observer
- CalendarObserver observer = new CalendarObserver(mHandler, account);
- if (observer.mCalendarId != 0) {
- // If we find the Calendar (and we'd better) register it and store it in the map
- mCalendarObservers.put(account.mId, observer);
- mResolver.registerContentObserver(
- ContentUris.withAppendedId(Calendars.CONTENT_URI, observer.mCalendarId), false,
- observer);
- }
- }
-
- /**
- * Unregister all CalendarObserver's
- */
- static public void unregisterCalendarObservers() {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- ContentResolver resolver = ssm.mResolver;
- for (CalendarObserver observer: ssm.mCalendarObservers.values()) {
- resolver.unregisterContentObserver(observer);
- }
- ssm.mCalendarObservers.clear();
- }
-
- public static Uri asSyncAdapter(Uri uri, String account, String accountType) {
- return uri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
- .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
- .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
- }
-
- /**
- * Return the syncable state of an account's calendar, as determined by the sync_events column
- * of our Calendar (from CalendarProvider2)
- * Note that the current state of sync_events is cached in our CalendarObserver
- * @param accountId the id of the account whose calendar we are checking
- * @return whether or not syncing of events is enabled
- */
- private boolean isCalendarEnabled(long accountId) {
- CalendarObserver observer = mCalendarObservers.get(accountId);
- if (observer != null) {
- return (observer.mSyncEvents == 1);
- }
- // If there's no observer, there's no Calendar in CalendarProvider2, so we return true
- // to allow Calendar creation
- return true;
- }
-
- private class CalendarObserver extends ContentObserver {
- final long mAccountId;
- final String mAccountName;
- long mCalendarId;
- long mSyncEvents;
-
- public CalendarObserver(Handler handler, Account account) {
- super(handler);
- mAccountId = account.mId;
- mAccountName = account.mEmailAddress;
- // Find the Calendar for this account
- Cursor c = mResolver.query(Calendars.CONTENT_URI,
- new String[] {Calendars._ID, Calendars.SYNC_EVENTS},
- CALENDAR_SELECTION,
- new String[] {account.mEmailAddress, getAccountManagerType()},
- null);
- if (c != null) {
- // Save its id and its sync events status
- try {
- if (c.moveToFirst()) {
- mCalendarId = c.getLong(0);
- mSyncEvents = c.getLong(1);
- }
- } finally {
- c.close();
- }
- }
- }
-
- @Override
- public synchronized void onChange(boolean selfChange) {
- // See if the user has changed syncing of our calendar
- if (!selfChange) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- Cursor c = mResolver.query(Calendars.CONTENT_URI,
- new String[] {Calendars.SYNC_EVENTS}, Calendars._ID + "=?",
- new String[] {Long.toString(mCalendarId)}, null);
- if (c == null) return;
- // Get its sync events; if it's changed, we've got work to do
- try {
- if (c.moveToFirst()) {
- long newSyncEvents = c.getLong(0);
- if (newSyncEvents != mSyncEvents) {
- log("_sync_events changed for calendar in " + mAccountName);
- Mailbox mailbox = Mailbox.restoreMailboxOfType(INSTANCE,
- mAccountId, Mailbox.TYPE_CALENDAR);
- // Sanity check for mailbox deletion
- if (mailbox == null) return;
- ContentValues cv = new ContentValues();
- if (newSyncEvents == 0) {
- // When sync is disabled, we're supposed to delete
- // all events in the calendar
- log("Deleting events and setting syncKey to 0 for " +
- mAccountName);
- // First, stop any sync that's ongoing
- stopManualSync(mailbox.mId);
- // Set the syncKey to 0 (reset)
- AbstractSyncService service = getServiceForMailbox(
- INSTANCE, mailbox);
- service.resetCalendarSyncKey();
- // Reset the sync key locally and stop syncing
- cv.put(Mailbox.SYNC_KEY, "0");
- cv.put(Mailbox.SYNC_INTERVAL,
- Mailbox.CHECK_INTERVAL_NEVER);
- mResolver.update(ContentUris.withAppendedId(
- Mailbox.CONTENT_URI, mailbox.mId), cv, null,
- null);
- // Delete all events using the sync adapter
- // parameter so that the deletion is only local
- Uri eventsAsSyncAdapter =
- asSyncAdapter(
- Events.CONTENT_URI,
- mAccountName,
- getAccountManagerType());
- mResolver.delete(eventsAsSyncAdapter, WHERE_CALENDAR_ID,
- new String[] {Long.toString(mCalendarId)});
- } else {
- // Make this a push mailbox and kick; this will start
- // a resync of the Calendar; the account mailbox will
- // ping on this during the next cycle of the ping loop
- cv.put(Mailbox.SYNC_INTERVAL,
- Mailbox.CHECK_INTERVAL_PUSH);
- mResolver.update(ContentUris.withAppendedId(
- Mailbox.CONTENT_URI, mailbox.mId), cv, null,
- null);
- kick("calendar sync changed");
- }
-
- // Save away the new value
- mSyncEvents = newSyncEvents;
- }
- }
- } finally {
- c.close();
- }
- } catch (ProviderUnavailableException e) {
- LogUtils.w(TAG, "Observer failed; provider unavailable");
- }
- }}, "Calendar Observer").start();
- }
- }
- }
-
- private class MailboxObserver extends ContentObserver {
- public MailboxObserver(Handler handler) {
- super(handler);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- // See if there's anything to do...
- if (!selfChange) {
- kick("mailbox changed");
- }
- }
- }
-
- private class SyncedMessageObserver extends ContentObserver {
- Intent syncAlarmIntent = new Intent(INSTANCE, EmailSyncAlarmReceiver.class);
- PendingIntent syncAlarmPendingIntent =
- PendingIntent.getBroadcast(INSTANCE, 0, syncAlarmIntent, 0);
- AlarmManager alarmManager = (AlarmManager)INSTANCE.getSystemService(Context.ALARM_SERVICE);
-
- public SyncedMessageObserver(Handler handler) {
- super(handler);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- alarmManager.set(AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + 10*SECONDS, syncAlarmPendingIntent);
- }
- }
-
- static public Account getAccountById(long accountId) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- AccountList accountList = ssm.mAccountList;
- synchronized (accountList) {
- return accountList.getById(accountId);
- }
- }
- return null;
- }
-
- static public Account getAccountByName(String accountName) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- AccountList accountList = ssm.mAccountList;
- synchronized (accountList) {
- return accountList.getByName(accountName);
- }
- }
- return null;
- }
-
- public class SyncStatus {
- static public final int NOT_RUNNING = 0;
- static public final int DIED = 1;
- static public final int SYNC = 2;
- static public final int IDLE = 3;
- }
-
- /*package*/ public class SyncError {
- int reason;
- public boolean fatal = false;
- long holdDelay = 15*SECONDS;
- public long holdEndTime = System.currentTimeMillis() + holdDelay;
-
- public SyncError(int _reason, boolean _fatal) {
- reason = _reason;
- fatal = _fatal;
- }
-
- /**
- * We double the holdDelay from 15 seconds through 8 mins
- */
- void escalate() {
- if (holdDelay <= HOLD_DELAY_MAXIMUM) {
- holdDelay *= 2;
- }
- holdEndTime = System.currentTimeMillis() + holdDelay;
- }
- }
-
- private void logSyncHolds() {
- if (sUserLog) {
- log("Sync holds:");
- long time = System.currentTimeMillis();
- for (long mailboxId : mSyncErrorMap.keySet()) {
- Mailbox m = Mailbox.restoreMailboxWithId(this, mailboxId);
- if (m == null) {
- log("Mailbox " + mailboxId + " no longer exists");
- } else {
- SyncError error = mSyncErrorMap.get(mailboxId);
- if (error != null) {
- log("Mailbox " + m.mDisplayName + ", error = " + error.reason
- + ", fatal = " + error.fatal);
- if (error.holdEndTime > 0) {
- log("Hold ends in " + ((error.holdEndTime - time) / 1000) + "s");
- }
- }
- }
- }
- }
- }
-
- /**
- * Release security holds for the specified account
- * @param account the account whose Mailboxes should be released from security hold
- */
- static public void releaseSecurityHold(Account account) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.releaseSyncHolds(INSTANCE, AbstractSyncService.EXIT_SECURITY_FAILURE,
- account);
- }
- }
-
- /**
- * Release a specific type of hold (the reason) for the specified Account; if the account
- * is null, mailboxes from all accounts with the specified hold will be released
- * @param reason the reason for the SyncError (AbstractSyncService.EXIT_XXX)
- * @param account an Account whose mailboxes should be released (or all if null)
- * @return whether or not any mailboxes were released
- */
- public /*package*/ boolean releaseSyncHolds(Context context, int reason, Account account) {
- boolean holdWasReleased = releaseSyncHoldsImpl(context, reason, account);
- kick("security release");
- return holdWasReleased;
- }
-
- private boolean releaseSyncHoldsImpl(Context context, int reason, Account account) {
- boolean holdWasReleased = false;
- for (long mailboxId: mSyncErrorMap.keySet()) {
- if (account != null) {
- Mailbox m = Mailbox.restoreMailboxWithId(context, mailboxId);
- if (m == null) {
- mSyncErrorMap.remove(mailboxId);
- } else if (m.mAccountKey != account.mId) {
- continue;
- }
- }
- SyncError error = mSyncErrorMap.get(mailboxId);
- if (error != null && error.reason == reason) {
- mSyncErrorMap.remove(mailboxId);
- holdWasReleased = true;
- }
- }
- return holdWasReleased;
- }
-
- public static void log(String str) {
- log(TAG, str);
- }
-
- public static void log(String tag, String str) {
- if (sUserLog) {
- LogUtils.d(tag, str);
- if (sFileLog) {
- FileLogger.log(tag, str);
- }
- }
- }
-
- public static void alwaysLog(String str) {
- if (!sUserLog) {
- LogUtils.d(TAG, str);
- } else {
- log(str);
- }
- }
-
- /**
- * EAS requires a unique device id, so that sync is possible from a variety of different
- * devices (e.g. the syncKey is specific to a device) If we're on an emulator or some other
- * device that doesn't provide one, we can create it as "device".
- * This would work on a real device as well, but it would be better to use the "real" id if
- * it's available
- */
- static public String getDeviceId(Context context) {
- if (sDeviceId == null) {
- sDeviceId = new AccountServiceProxy(context).getDeviceId();
- alwaysLog("Received deviceId from Email app: " + sDeviceId);
- }
- return sDeviceId;
- }
-
- static public ConnPerRoute sConnPerRoute = new ConnPerRoute() {
- @Override
- public int getMaxForRoute(HttpRoute route) {
- return 8;
- }
- };
-
- static public synchronized EmailClientConnectionManager getClientConnectionManager(
- Context context, HostAuth hostAuth) {
- // We'll use a different connection manager for each HostAuth
- EmailClientConnectionManager mgr = null;
- // We don't save managers for validation/autodiscover
- if (hostAuth.mId != HostAuth.NOT_SAVED) {
- mgr = sClientConnectionManagers.get(hostAuth.mId);
- }
- if (mgr == null) {
- // After two tries, kill the process. Most likely, this will happen in the background
- // The service will restart itself after about 5 seconds
- if (sClientConnectionManagerShutdownCount > MAX_CLIENT_CONNECTION_MANAGER_SHUTDOWNS) {
- alwaysLog("Shutting down process to unblock threads");
- Process.killProcess(Process.myPid());
- }
- HttpParams params = new BasicHttpParams();
- params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25);
- params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute);
- boolean ssl = hostAuth.shouldUseSsl();
- int port = hostAuth.mPort;
- mgr = EmailClientConnectionManager.newInstance(context, params, hostAuth);
- log("Creating connection manager for port " + port + ", ssl: " + ssl);
- sClientConnectionManagers.put(hostAuth.mId, mgr);
- }
- // Null is a valid return result if we get an exception
- return mgr;
- }
-
- static private synchronized void shutdownConnectionManager() {
- log("Shutting down ClientConnectionManagers");
- for (EmailClientConnectionManager mgr: sClientConnectionManagers.values()) {
- mgr.shutdown();
- }
- sClientConnectionManagers.clear();
- }
-
- public static void stopAccountSyncs(long acctId) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.stopAccountSyncs(acctId, true);
- }
- }
-
- public void stopAccountSyncs(long acctId, boolean includeAccountMailbox) {
- synchronized (sSyncLock) {
- List deletedBoxes = new ArrayList();
- for (Long mid : mServiceMap.keySet()) {
- Mailbox box = Mailbox.restoreMailboxWithId(this, mid);
- if (box != null) {
- if (box.mAccountKey == acctId) {
- if (!includeAccountMailbox &&
- box.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
- AbstractSyncService svc = mServiceMap.get(mid);
- if (svc != null) {
- svc.stop();
- }
- continue;
- }
- AbstractSyncService svc = mServiceMap.get(mid);
- if (svc != null) {
- svc.stop();
- Thread t = svc.mThread;
- if (t != null) {
- t.interrupt();
- }
- }
- deletedBoxes.add(mid);
- }
- }
- }
- for (Long mid : deletedBoxes) {
- releaseMailbox(mid);
- }
- }
- }
-
- /**
- * Informs SyncServiceManager that an account has a new folder list; as a result, any existing
- * folder might have become invalid. Therefore, we act as if the account has been deleted, and
- * then we reinitialize it.
- *
- * @param acctId
- */
- static public void stopNonAccountMailboxSyncsForAccount(long acctId) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.stopAccountSyncs(acctId, false);
- kick("reload folder list");
- }
- }
-
- private boolean hasWakeLock(long id) {
- synchronized (mWakeLocks) {
- return mWakeLocks.get(id) != null;
- }
- }
-
- private void acquireWakeLock(long id) {
- synchronized (mWakeLocks) {
- Long lock = mWakeLocks.get(id);
- if (lock == null) {
- if (mWakeLock == null) {
- PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MAIL_SERVICE");
- mWakeLock.acquire();
- }
- mWakeLocks.put(id, System.currentTimeMillis());
- }
- }
- }
-
- private void releaseWakeLock(long id) {
- synchronized (mWakeLocks) {
- Long lock = mWakeLocks.get(id);
- if (lock != null) {
- Long startTime = mWakeLocks.remove(id);
- Long historicalTime = mWakeLocksHistory.get(id);
- if (historicalTime == null) {
- historicalTime = 0L;
- }
- mWakeLocksHistory.put(id,
- historicalTime + (System.currentTimeMillis() - startTime));
- if (mWakeLocks.isEmpty()) {
- if (mWakeLock != null) {
- mWakeLock.release();
- }
- mWakeLock = null;
- } else {
- log("Release request for lock not held: " + id);
- }
- }
- }
- }
-
- static public String alarmOwner(long id) {
- if (id == EXTRA_MAILBOX_ID) {
- return TAG;
- } else {
- String name = Long.toString(id);
- if (sUserLog && INSTANCE != null) {
- Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, id);
- if (m != null) {
- name = m.mDisplayName + '(' + m.mAccountKey + ')';
- }
- }
- return "Mailbox " + name;
- }
- }
-
- private void clearAlarm(long id) {
- synchronized (mPendingIntents) {
- PendingIntent pi = mPendingIntents.get(id);
- if (pi != null) {
- AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
- alarmManager.cancel(pi);
- //log("+Alarm cleared for " + alarmOwner(id));
- mPendingIntents.remove(id);
- }
- }
- }
-
- private void setAlarm(long id, long millis) {
- synchronized (mPendingIntents) {
- PendingIntent pi = mPendingIntents.get(id);
- if (pi == null) {
- Intent i = new Intent(this, MailboxAlarmReceiver.class);
- i.putExtra("mailbox", id);
- i.setData(Uri.parse("Box" + id));
- pi = PendingIntent.getBroadcast(this, 0, i, 0);
- mPendingIntents.put(id, pi);
-
- AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
- alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millis, pi);
- //log("+Alarm set for " + alarmOwner(id) + ", " + millis/1000 + "s");
- }
- }
- }
-
- private void clearAlarms() {
- AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
- synchronized (mPendingIntents) {
- for (PendingIntent pi : mPendingIntents.values()) {
- alarmManager.cancel(pi);
- }
- mPendingIntents.clear();
- }
- }
-
- static public boolean isHoldingWakeLock(long id) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- return ssm.hasWakeLock(id);
- }
- return false;
- }
-
- static public void runAwake(long id) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.acquireWakeLock(id);
- ssm.clearAlarm(id);
- }
- }
-
- static public void runAsleep(long id, long millis) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.setAlarm(id, millis);
- ssm.releaseWakeLock(id);
- }
- }
-
- static public void clearWatchdogAlarm(long id) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.clearAlarm(id);
- }
- }
-
- static public void setWatchdogAlarm(long id, long millis) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.setAlarm(id, millis);
- }
- }
-
- static public void alert(Context context, final long id) {
- final SyncManager ssm = INSTANCE;
- checkSyncManagerRunning();
- if (id < 0) {
- log("SyncServiceManager alert");
- kick("ping SyncServiceManager");
- } else if (ssm == null) {
- context.startService(new Intent(context, SyncManager.class));
- } else {
- final AbstractSyncService service = ssm.getRunningService(id);
- if (service != null) {
- // Handle alerts in a background thread, as we are typically called from a
- // broadcast receiver, and are therefore running in the UI thread
- String threadName = "SyncServiceManager Alert: ";
- if (service.mMailbox != null) {
- threadName += service.mMailbox.mDisplayName;
- }
- new Thread(new Runnable() {
- @Override
- public void run() {
- Mailbox m = Mailbox.restoreMailboxWithId(ssm, id);
- if (m != null) {
- // We ignore drafts completely (doesn't sync). Changes in Outbox are
- // handled in the checkMailboxes loop, so we can ignore these pings.
- if (sUserLog) {
- LogUtils.d(TAG, "Alert for mailbox " + id + " ("
- + m.mDisplayName + ")");
- }
- if (m.mType == Mailbox.TYPE_DRAFTS || m.mType == Mailbox.TYPE_OUTBOX) {
- String[] args = new String[] {Long.toString(m.mId)};
- ContentResolver resolver = INSTANCE.mResolver;
- resolver.delete(Message.DELETED_CONTENT_URI, WHERE_MAILBOX_KEY,
- args);
- resolver.delete(Message.UPDATED_CONTENT_URI, WHERE_MAILBOX_KEY,
- args);
- return;
- }
- service.mAccount = Account.restoreAccountWithId(INSTANCE, m.mAccountKey);
- service.mMailbox = m;
- // Send the alarm to the sync service
- if (!service.alarm()) {
- // A false return means that we were forced to interrupt the thread
- // In this case, we release the mailbox so that we can start another
- // thread to do the work
- log("Alarm failed; releasing mailbox");
- synchronized(sSyncLock) {
- ssm.releaseMailbox(id);
- }
- // Shutdown the connection manager; this should close all of our
- // sockets and generate IOExceptions all around.
- SyncManager.shutdownConnectionManager();
- }
- }
- }}, threadName).start();
- }
- }
- }
-
- public class ConnectivityReceiver extends BroadcastReceiver {
- @SuppressWarnings("deprecation")
- @Override
- public void onReceive(Context context, Intent intent) {
- Bundle b = intent.getExtras();
- if (b != null) {
- NetworkInfo a = (NetworkInfo)b.get(ConnectivityManager.EXTRA_NETWORK_INFO);
- String info = "Connectivity alert for " + a.getTypeName();
- State state = a.getState();
- if (state == State.CONNECTED) {
- info += " CONNECTED";
- log(info);
- synchronized (sConnectivityLock) {
- sConnectivityLock.notifyAll();
- }
- kick("connected");
- } else if (state == State.DISCONNECTED) {
- info += " DISCONNECTED";
- log(info);
- kick("disconnected");
- }
- }
- }
- }
-
- /**
- * Starts a service thread and enters it into the service map
- * This is the point of instantiation of all sync threads
- * @param service the service to start
- */
- private void startServiceThread(AbstractSyncService service) {
- final Mailbox mailbox = service.mMailbox;
- synchronized (sSyncLock) {
- String mailboxName = mailbox.mDisplayName;
- String accountName = service.mAccount.mDisplayName;
- Thread thread = new Thread(service, mailboxName + "[" + accountName + "]");
- log("Starting thread for " + mailboxName + " in account " + accountName);
- thread.start();
- mServiceMap.put(mailbox.mId, service);
- runAwake(mailbox.mId);
- }
- onStartService(mailbox);
- }
-
- private void requestSync(Mailbox m, int reason, Request req) {
- int syncStatus = EmailContent.SYNC_STATUS_BACKGROUND;
- // Don't sync if there's no connectivity
- if (sConnectivityHold || (m == null) || sStop) {
- return;
- }
- synchronized (sSyncLock) {
- Account acct = Account.restoreAccountWithId(this, m.mAccountKey);
- if (acct != null) {
- // Always make sure there's not a running instance of this service
- AbstractSyncService service = mServiceMap.get(m.mId);
- if (service == null) {
- service = getServiceForMailbox(this, m);
- if (!service.mIsValid) return;
- service.mSyncReason = reason;
- if (req != null) {
- service.addRequest(req);
- }
- startServiceThread(service);
- if (reason >= SYNC_CALLBACK_START) {
- syncStatus = EmailContent.SYNC_STATUS_USER;
- }
- setMailboxSyncStatus(m.mId, syncStatus);
- }
- }
- }
- }
-
- public void setMailboxSyncStatus(long id, int status) {
- ContentValues values = new ContentValues();
- values.put(Mailbox.UI_SYNC_STATUS, status);
- mResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id), values, null, null);
- }
-
- public void setMailboxLastSyncResult(long id, int result) {
- if (result != EmailContent.LAST_SYNC_RESULT_SUCCESS) {
- LogUtils.w(TAG, new Throwable(), "setMailboxLastSyncResult %d", result);
- }
- ContentValues values = new ContentValues();
- values.put(Mailbox.UI_LAST_SYNC_RESULT, result);
- mResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id), values, null, null);
- }
-
- private void stopServiceThreads() {
- synchronized (sSyncLock) {
- ArrayList toStop = new ArrayList();
-
- // Keep track of which services to stop
- for (Long mailboxId : mServiceMap.keySet()) {
- toStop.add(mailboxId);
- }
-
- // Shut down all of those running services
- for (Long mailboxId : toStop) {
- AbstractSyncService svc = mServiceMap.get(mailboxId);
- if (svc != null) {
- log("Stopping " + svc.mAccount.mDisplayName + '/' + svc.mMailbox.mDisplayName);
- svc.stop();
- if (svc.mThread != null) {
- svc.mThread.interrupt();
- }
- }
- releaseWakeLock(mailboxId);
- }
- }
- }
-
- private void waitForConnectivity() {
- boolean waiting = false;
- ConnectivityManager cm =
- (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
- while (!sStop) {
- NetworkInfo info = cm.getActiveNetworkInfo();
- if (info != null) {
- mNetworkInfo = info;
- // We're done if there's an active network
- if (waiting) {
- // If we've been waiting, release any I/O error holds
- releaseSyncHolds(this, AbstractSyncService.EXIT_IO_ERROR, null);
- // And log what's still being held
- logSyncHolds();
- }
- return;
- } else {
- // If this is our first time through the loop, shut down running service threads
- if (!waiting) {
- waiting = true;
- stopServiceThreads();
- }
- // Wait until a network is connected (or 10 mins), but let the device sleep
- // We'll set an alarm just in case we don't get notified (bugs happen)
- synchronized (sConnectivityLock) {
- runAsleep(EXTRA_MAILBOX_ID, CONNECTIVITY_WAIT_TIME+5*SECONDS);
- try {
- log("Connectivity lock...");
- sConnectivityHold = true;
- sConnectivityLock.wait(CONNECTIVITY_WAIT_TIME);
- log("Connectivity lock released...");
- } catch (InterruptedException e) {
- // This is fine; we just go around the loop again
- } finally {
- sConnectivityHold = false;
- }
- runAwake(EXTRA_MAILBOX_ID);
- }
- }
- }
- }
-
- /**
- * Note that there are two ways the EAS SyncServiceManager service can be created:
- *
- * 1) as a background service instantiated via startService (which happens on boot, when the
- * first EAS account is created, etc), in which case the service thread is spun up, mailboxes
- * sync, etc. and
- * 2) to execute an RPC call from the UI, in which case the background service will already be
- * running most of the time (unless we're creating a first EAS account)
- *
- * If the running background service detects that there are no EAS accounts (on boot, if none
- * were created, or afterward if the last remaining EAS account is deleted), it will call
- * stopSelf() to terminate operation.
- *
- * The goal is to ensure that the background service is running at all times when there is at
- * least one EAS account in existence
- *
- * Because there are edge cases in which our process can crash (typically, this has been seen
- * in UI crashes, ANR's, etc.), it's possible for the UI to start up again without the
- * background service having been started. We explicitly try to start the service in Welcome
- * (to handle the case of the app having been reloaded). We also start the service on any
- * startSync call (if it isn't already running)
- */
- @SuppressWarnings("deprecation")
- @Override
- public void onCreate() {
- TAG = getClass().getSimpleName();
- EmailContent.init(this);
- Utility.runAsync(new Runnable() {
- @Override
- public void run() {
- // Quick checks first, before getting the lock
- if (sStartingUp) return;
- synchronized (sSyncLock) {
- alwaysLog("!!! onCreate");
- // Try to start up properly; we might be coming back from a crash that the Email
- // application isn't aware of.
- startService(getServiceIntent());
- if (sStop) {
- return;
- }
- }
- }});
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- alwaysLog("!!! onStartCommand, startingUp = " + sStartingUp + ", running = " +
- (INSTANCE != null));
- if (!sStartingUp && INSTANCE == null) {
- sStartingUp = true;
- Utility.runAsync(new Runnable() {
- @Override
- public void run() {
- try {
- synchronized (sSyncLock) {
- // SyncServiceManager cannot start unless we connect to AccountService
- if (!new AccountServiceProxy(SyncManager.this).test()) {
- alwaysLog("!!! Email application not found; stopping self");
- stopSelf();
- }
- String deviceId = getDeviceId(SyncManager.this);
- if (deviceId == null) {
- alwaysLog("!!! deviceId unknown; stopping self and retrying");
- stopSelf();
- // Try to restart ourselves in a few seconds
- Utility.runAsync(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- }
- startService(getServiceIntent());
- }});
- return;
- }
- // Run the reconciler and clean up mismatched accounts - if we weren't
- // running when accounts were deleted, it won't have been called.
- runAccountReconcilerSync(SyncManager.this);
- // Update other services depending on final account configuration
- maybeStartSyncServiceManagerThread();
- if (sServiceThread == null) {
- log("!!! EAS SyncServiceManager, stopping self");
- stopSelf();
- } else if (sStop) {
- // If we were trying to stop, attempt a restart in 5 secs
- setAlarm(SYNC_SERVICE_MAILBOX_ID, 5*SECONDS);
- } else {
- mServiceStartTime = System.currentTimeMillis();
- }
- }
- } finally {
- sStartingUp = false;
- }
- }});
- }
- return Service.START_STICKY;
- }
-
- public static void reconcileAccounts(Context context) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.runAccountReconcilerSync(context);
- }
- }
-
- protected abstract void runAccountReconcilerSync(Context context);
-
- @SuppressWarnings("deprecation")
- @Override
- public void onDestroy() {
- log("!!! onDestroy");
- // Handle shutting down off the UI thread
- Utility.runAsync(new Runnable() {
- @Override
- public void run() {
- // Quick checks first, before getting the lock
- if (INSTANCE == null || sServiceThread == null) return;
- synchronized(sSyncLock) {
- // Stop the sync manager thread and return
- if (sServiceThread != null) {
- sStop = true;
- sServiceThread.interrupt();
- }
- }
- }});
- }
-
- void maybeStartSyncServiceManagerThread() {
- // Start our thread...
- // See if there are any EAS accounts; otherwise, just go away
- if (sServiceThread == null || !sServiceThread.isAlive()) {
- AccountList currentAccounts = new AccountList();
- try {
- collectAccounts(this, currentAccounts);
- } catch (ProviderUnavailableException e) {
- // Just leave if EmailProvider is unavailable
- return;
- }
- if (!currentAccounts.isEmpty()) {
- log(sServiceThread == null ? "Starting thread..." : "Restarting thread...");
- sServiceThread = new Thread(this, TAG);
- INSTANCE = this;
- sServiceThread.start();
- }
- }
- }
-
- /**
- * Start up the SyncManager service if it's not already running
- * This is a stopgap for cases in which SyncServiceManager died (due to a crash somewhere in
- * com.android.email) and hasn't been restarted. See the comment for onCreate for details
- */
- static void checkSyncManagerRunning() {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- if (sServiceThread == null) {
- log("!!! checkSyncServiceManagerServiceRunning; starting service...");
- ssm.startService(new Intent(ssm, SyncManager.class));
- }
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public void run() {
- sStop = false;
- alwaysLog("Service thread running");
-
- TempDirectory.setTempDirectory(this);
-
- // Synchronize here to prevent a shutdown from happening while we initialize our observers
- // and receivers
- synchronized (sSyncLock) {
- if (INSTANCE != null) {
- mResolver = getContentResolver();
-
- // Set up our observers; we need them to know when to start/stop various syncs based
- // on the insert/delete/update of mailboxes and accounts
- // We also observe synced messages to trigger upsyncs at the appropriate time
- mAccountObserver = getAccountObserver(mHandler);
- mResolver.registerContentObserver(Account.NOTIFIER_URI, true, mAccountObserver);
- mMailboxObserver = new MailboxObserver(mHandler);
- mResolver.registerContentObserver(Mailbox.CONTENT_URI, false, mMailboxObserver);
- mSyncedMessageObserver = new SyncedMessageObserver(mHandler);
- mResolver.registerContentObserver(Message.SYNCED_CONTENT_URI, true,
- mSyncedMessageObserver);
-
- mConnectivityReceiver = new ConnectivityReceiver();
- registerReceiver(mConnectivityReceiver, new IntentFilter(
- ConnectivityManager.CONNECTIVITY_ACTION));
-
- onStartup();
- }
- }
-
- try {
- // Loop indefinitely until we're shut down
- while (!sStop) {
- runAwake(EXTRA_MAILBOX_ID);
- waitForConnectivity();
- mNextWaitReason = null;
- long nextWait = checkMailboxes();
- try {
- synchronized (this) {
- if (!mKicked) {
- if (nextWait < 0) {
- log("Negative wait? Setting to 1s");
- nextWait = 1*SECONDS;
- }
- if (nextWait > 10*SECONDS) {
- if (mNextWaitReason != null) {
- log("Next awake " + nextWait / 1000 + "s: " + mNextWaitReason);
- }
- runAsleep(EXTRA_MAILBOX_ID, nextWait + (3*SECONDS));
- }
- wait(nextWait);
- }
- }
- } catch (InterruptedException e) {
- // Needs to be caught, but causes no problem
- log("SyncServiceManager interrupted");
- } finally {
- synchronized (this) {
- if (mKicked) {
- //log("Wait deferred due to kick");
- mKicked = false;
- }
- }
- }
- }
- log("Shutdown requested");
- } catch (ProviderUnavailableException pue) {
- // Shutdown cleanly in this case
- // NOTE: Sync adapters will also crash with this error, but that is already handled
- // in the adapters themselves, i.e. they return cleanly via done(). When the Email
- // process starts running again, remote processes will be started again in due course
- LogUtils.e(TAG, "EmailProvider unavailable; shutting down");
- // Ask for our service to be restarted; this should kick-start the Email process as well
- startService(new Intent(this, SyncManager.class));
- } catch (RuntimeException e) {
- // Crash; this is a completely unexpected runtime error
- LogUtils.e(TAG, "RuntimeException", e);
- throw e;
- } finally {
- shutdown();
- }
- }
-
- private void shutdown() {
- synchronized (sSyncLock) {
- // If INSTANCE is null, we've already been shut down
- if (INSTANCE != null) {
- log("Shutting down...");
-
- // Stop our running syncs
- stopServiceThreads();
-
- // Stop receivers
- if (mConnectivityReceiver != null) {
- unregisterReceiver(mConnectivityReceiver);
- }
-
- // Unregister observers
- ContentResolver resolver = getContentResolver();
- if (mSyncedMessageObserver != null) {
- resolver.unregisterContentObserver(mSyncedMessageObserver);
- mSyncedMessageObserver = null;
- }
- if (mAccountObserver != null) {
- resolver.unregisterContentObserver(mAccountObserver);
- mAccountObserver = null;
- }
- if (mMailboxObserver != null) {
- resolver.unregisterContentObserver(mMailboxObserver);
- mMailboxObserver = null;
- }
- unregisterCalendarObservers();
-
- // Clear pending alarms and associated Intents
- clearAlarms();
-
- // Release our wake lock, if we have one
- synchronized (mWakeLocks) {
- if (mWakeLock != null) {
- mWakeLock.release();
- mWakeLock = null;
- }
- }
-
- INSTANCE = null;
- sServiceThread = null;
- sStop = false;
- log("Goodbye");
- }
- }
- }
-
- /**
- * Release a mailbox from the service map and release its wake lock.
- * NOTE: This method MUST be called while holding sSyncLock!
- *
- * @param mailboxId the id of the mailbox to be released
- */
- public void releaseMailbox(long mailboxId) {
- mServiceMap.remove(mailboxId);
- releaseWakeLock(mailboxId);
- }
-
- /**
- * Retrieve a running sync service for the passed-in mailbox id in a threadsafe manner
- *
- * @param mailboxId the id of the mailbox whose service is to be found
- * @return the running service (a subclass of AbstractSyncService) or null if none
- */
- public AbstractSyncService getRunningService(long mailboxId) {
- synchronized(sSyncLock) {
- return mServiceMap.get(mailboxId);
- }
- }
-
- /**
- * Check whether an Outbox (referenced by a Cursor) has any messages that can be sent
- * @param outboxCursor the cursor to an Outbox
- * @return true if there is mail to be sent
- */
- private boolean hasSendableMessages(Cursor outboxCursor) {
- Cursor c = mResolver.query(Message.CONTENT_URI, Message.ID_COLUMN_PROJECTION,
- MAILBOX_KEY_AND_NOT_SEND_FAILED,
- new String[] {Long.toString(outboxCursor.getLong(Mailbox.CONTENT_ID_COLUMN))},
- null);
- try {
- while (c.moveToNext()) {
- if (!Utility.hasUnloadedAttachments(this, c.getLong(Message.CONTENT_ID_COLUMN))) {
- return true;
- }
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return false;
- }
-
- /**
- * Taken from ConnectivityManager using public constants
- */
- public static boolean isNetworkTypeMobile(int networkType) {
- switch (networkType) {
- case ConnectivityManager.TYPE_MOBILE:
- case ConnectivityManager.TYPE_MOBILE_MMS:
- case ConnectivityManager.TYPE_MOBILE_SUPL:
- case ConnectivityManager.TYPE_MOBILE_DUN:
- case ConnectivityManager.TYPE_MOBILE_HIPRI:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Determine whether the account is allowed to sync automatically, as opposed to manually, based
- * on whether the "require manual sync when roaming" policy is in force and applicable
- * @param account the account
- * @return whether or not the account can sync automatically
- */
- /*package*/ public static boolean canAutoSync(Account account) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) {
- return false;
- }
- NetworkInfo networkInfo = ssm.mNetworkInfo;
-
- // Enforce manual sync only while roaming here
- long policyKey = account.mPolicyKey;
- // Quick exit from this check
- if ((policyKey != 0) && (networkInfo != null) &&
- isNetworkTypeMobile(networkInfo.getType())) {
- // We'll cache the Policy data here
- Policy policy = account.mPolicy;
- if (policy == null) {
- policy = Policy.restorePolicyWithId(INSTANCE, policyKey);
- account.mPolicy = policy;
- if (!PolicyServiceProxy.isActive(ssm, policy)) {
- PolicyServiceProxy.setAccountHoldFlag(ssm, account, true);
- log("canAutoSync; policies not active, set hold flag");
- return false;
- }
- }
- if (policy != null && policy.mRequireManualSyncWhenRoaming && networkInfo.isRoaming()) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Convenience method to determine whether Email sync is enabled for a given account
- * @param account the Account in question
- * @return whether Email sync is enabled
- */
- private static boolean canSyncEmail(android.accounts.Account account) {
- return ContentResolver.getSyncAutomatically(account, EmailContent.AUTHORITY);
- }
-
- /**
- * Determine whether a mailbox of a given type in a given account can be synced automatically
- * by SyncServiceManager. This is an increasingly complex determination, taking into account
- * security policies and user settings (both within the Email application and in the Settings
- * application)
- *
- * @param account the Account that the mailbox is in
- * @param type the type of the Mailbox
- * @return whether or not to start a sync
- */
- private boolean isMailboxSyncable(Account account, int type) {
- // This 'if' statement performs checks to see whether or not a mailbox is a
- // candidate for syncing based on policies, user settings, & other restrictions
- if (type == Mailbox.TYPE_OUTBOX) {
- // Outbox is always syncable
- return true;
- } else if (type == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
- // Always sync EAS mailbox unless master sync is off
- return ContentResolver.getMasterSyncAutomatically();
- } else if (type == Mailbox.TYPE_CONTACTS || type == Mailbox.TYPE_CALENDAR) {
- // Contacts/Calendar obey this setting from ContentResolver
- if (!ContentResolver.getMasterSyncAutomatically()) {
- return false;
- }
- // Get the right authority for the mailbox
- String authority;
- if (type == Mailbox.TYPE_CONTACTS) {
- authority = ContactsContract.AUTHORITY;
- } else {
- authority = CalendarContract.AUTHORITY;
- if (!mCalendarObservers.containsKey(account.mId)){
- // Make sure we have an observer for this Calendar, as
- // we need to be able to detect sync state changes, sigh
- registerCalendarObserver(account);
- }
- }
- // See if "sync automatically" is set; if not, punt
- if (!ContentResolver.getSyncAutomatically(mAccountList.getAmAccount(account),
- authority)) {
- return false;
- // See if the calendar is enabled from the Calendar app UI; if not, punt
- } else if ((type == Mailbox.TYPE_CALENDAR) && !isCalendarEnabled(account.mId)) {
- return false;
- }
- // Never automatically sync trash
- } else if (type == Mailbox.TYPE_TRASH) {
- return false;
- // For non-outbox, non-account mail, we do two checks:
- // 1) are we restricted by policy (i.e. manual sync only),
- // 2) has the user checked the "Sync Email" box in Account Settings, and
- } else if (!canAutoSync(account) || !canSyncEmail(mAccountList.getAmAccount(account))) {
- return false;
- }
- return true;
- }
-
- private long checkMailboxes () {
- // First, see if any running mailboxes have been deleted
- ArrayList deletedMailboxes = new ArrayList();
- synchronized (sSyncLock) {
- for (long mailboxId: mServiceMap.keySet()) {
- Mailbox m = Mailbox.restoreMailboxWithId(this, mailboxId);
- if (m == null) {
- deletedMailboxes.add(mailboxId);
- }
- }
- // If so, stop them or remove them from the map
- for (Long mailboxId: deletedMailboxes) {
- AbstractSyncService svc = mServiceMap.get(mailboxId);
- if (svc == null || svc.mThread == null) {
- releaseMailbox(mailboxId);
- continue;
- } else {
- boolean alive = svc.mThread.isAlive();
- log("Deleted mailbox: " + svc.mMailboxName);
- if (alive) {
- stopManualSync(mailboxId);
- } else {
- log("Removing from serviceMap");
- releaseMailbox(mailboxId);
- }
- }
- }
- }
-
- long nextWait = SYNC_SERVICE_HEARTBEAT_TIME;
- long now = System.currentTimeMillis();
-
- // Start up threads that need it; use a query which finds eas mailboxes where the
- // the sync interval is not "never". This is the set of mailboxes that we control
- if (mAccountObserver == null) {
- log("mAccountObserver null; service died??");
- return nextWait;
- }
-
- Cursor c = getContentResolver().query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
- mAccountObserver.getSyncableMailboxWhere(), null, null);
- if (c == null) throw new ProviderUnavailableException();
- try {
- while (c.moveToNext()) {
- long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
- AbstractSyncService service = getRunningService(mailboxId);
- if (service == null) {
- // Get the cached account
- Account account = getAccountById(c.getInt(Mailbox.CONTENT_ACCOUNT_KEY_COLUMN));
- if (account == null) continue;
-
- // We handle a few types of mailboxes specially
- int mailboxType = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
- if (!isMailboxSyncable(account, mailboxType)) {
- continue;
- }
-
- // Check whether we're in a hold (temporary or permanent)
- SyncError syncError = mSyncErrorMap.get(mailboxId);
- if (syncError != null) {
- // Nothing we can do about fatal errors
- if (syncError.fatal) continue;
- if (now < syncError.holdEndTime) {
- // If release time is earlier than next wait time,
- // move next wait time up to the release time
- if (syncError.holdEndTime < now + nextWait) {
- nextWait = syncError.holdEndTime - now;
- mNextWaitReason = "Release hold";
- }
- continue;
- } else {
- // Keep the error around, but clear the end time
- syncError.holdEndTime = 0;
- }
- }
-
- // Otherwise, we use the sync interval
- long syncInterval = c.getInt(Mailbox.CONTENT_SYNC_INTERVAL_COLUMN);
- if (syncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
- Mailbox m = EmailContent.getContent(this, c, Mailbox.class);
- requestSync(m, SYNC_PUSH, null);
- } else if (mailboxType == Mailbox.TYPE_OUTBOX) {
- if (hasSendableMessages(c)) {
- Mailbox m = EmailContent.getContent(this, c, Mailbox.class);
- startServiceThread(getServiceForMailbox(this, m));
- }
- } else if (syncInterval > 0 && syncInterval <= ONE_DAY_MINUTES) {
- // TODO: Migrating to use system SyncManager, so this should be dead code.
- long lastSync = c.getLong(Mailbox.CONTENT_SYNC_TIME_COLUMN);
- long sinceLastSync = now - lastSync;
- long toNextSync = syncInterval*MINUTES - sinceLastSync;
- String name = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
- if (toNextSync <= 0) {
- Mailbox m = EmailContent.getContent(this, c, Mailbox.class);
- requestSync(m, SYNC_SCHEDULED, null);
- } else if (toNextSync < nextWait) {
- nextWait = toNextSync;
- if (sUserLog) {
- log("Next sync for " + name + " in " + nextWait/1000 + "s");
- }
- mNextWaitReason = "Scheduled sync, " + name;
- } else if (sUserLog) {
- log("Next sync for " + name + " in " + toNextSync/1000 + "s");
- }
- }
- } else {
- Thread thread = service.mThread;
- // Look for threads that have died and remove them from the map
- if (thread != null && !thread.isAlive()) {
- if (sUserLog) {
- log("Dead thread, mailbox released: " +
- c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN));
- }
- synchronized (sSyncLock) {
- releaseMailbox(mailboxId);
- }
- // Restart this if necessary
- if (nextWait > 3*SECONDS) {
- nextWait = 3*SECONDS;
- mNextWaitReason = "Clean up dead thread(s)";
- }
- } else {
- long requestTime = service.mRequestTime;
- if (requestTime > 0) {
- long timeToRequest = requestTime - now;
- if (timeToRequest <= 0) {
- service.mRequestTime = 0;
- service.alarm();
- } else if (requestTime > 0 && timeToRequest < nextWait) {
- if (timeToRequest < 11*MINUTES) {
- nextWait = timeToRequest < 250 ? 250 : timeToRequest;
- mNextWaitReason = "Sync data change";
- } else {
- log("Illegal timeToRequest: " + timeToRequest);
- }
- }
- }
- }
- }
- }
- } finally {
- c.close();
- }
- return nextWait;
- }
-
- static public void serviceRequest(long mailboxId, int reason) {
- serviceRequest(mailboxId, 5*SECONDS, reason);
- }
-
- /**
- * Return a boolean indicating whether the mailbox can be synced
- * @param m the mailbox
- * @return whether or not the mailbox can be synced
- */
- public static boolean isSyncable(Mailbox m) {
- return m.mType != Mailbox.TYPE_DRAFTS
- && m.mType != Mailbox.TYPE_OUTBOX
- && m.mType != Mailbox.TYPE_SEARCH
- && m.mType < Mailbox.TYPE_NOT_SYNCABLE;
- }
-
- static public void serviceRequest(long mailboxId, long ms, int reason) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- Mailbox m = Mailbox.restoreMailboxWithId(ssm, mailboxId);
- if (m == null || !isSyncable(m)) return;
- try {
- AbstractSyncService service = ssm.getRunningService(mailboxId);
- if (service != null) {
- service.mRequestTime = System.currentTimeMillis() + ms;
- kick("service request");
- } else {
- startManualSync(mailboxId, reason, null);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- static public void serviceRequestImmediate(long mailboxId) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- AbstractSyncService service = ssm.getRunningService(mailboxId);
- if (service != null) {
- service.mRequestTime = System.currentTimeMillis();
- Mailbox m = Mailbox.restoreMailboxWithId(ssm, mailboxId);
- if (m != null) {
- service.mAccount = Account.restoreAccountWithId(ssm, m.mAccountKey);
- service.mMailbox = m;
- kick("service request immediate");
- }
- }
- }
-
- static public void sendMessageRequest(Request req) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- Message msg = Message.restoreMessageWithId(ssm, req.mMessageId);
- if (msg == null) return;
- long mailboxId = msg.mMailboxKey;
- Mailbox mailbox = Mailbox.restoreMailboxWithId(ssm, mailboxId);
- if (mailbox == null) return;
-
- // If we're loading an attachment for Outbox, we want to look at the source message
- // to find the loading mailbox
- if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
- long sourceId = Utility.getFirstRowLong(ssm, Body.CONTENT_URI,
- new String[] {BodyColumns.SOURCE_MESSAGE_KEY},
- BodyColumns.MESSAGE_KEY + "=?",
- new String[] {Long.toString(msg.mId)}, null, 0, -1L);
- if (sourceId != -1L) {
- EmailContent.Message sourceMsg =
- EmailContent.Message.restoreMessageWithId(ssm, sourceId);
- if (sourceMsg != null) {
- mailboxId = sourceMsg.mMailboxKey;
- }
- }
- }
- sendRequest(mailboxId, req);
- }
-
- static public void sendRequest(long mailboxId, Request req) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- AbstractSyncService service = ssm.getRunningService(mailboxId);
- if (service == null) {
- startManualSync(mailboxId, SYNC_SERVICE_PART_REQUEST, req);
- kick("part request");
- } else {
- service.addRequest(req);
- }
- }
-
- /**
- * Determine whether a given Mailbox can be synced, i.e. is not already syncing and is not in
- * an error state
- *
- * @param mailboxId
- * @return whether or not the Mailbox is available for syncing (i.e. is a valid push target)
- */
- static public int pingStatus(long mailboxId) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return PING_STATUS_OK;
- // Already syncing...
- if (ssm.getRunningService(mailboxId) != null) {
- return PING_STATUS_RUNNING;
- }
- // No errors or a transient error, don't ping...
- SyncError error = ssm.mSyncErrorMap.get(mailboxId);
- if (error != null) {
- if (error.fatal) {
- return PING_STATUS_UNABLE;
- } else if (error.holdEndTime > 0) {
- return PING_STATUS_WAITING;
- }
- }
- return PING_STATUS_OK;
- }
-
- static public void startManualSync(long mailboxId, int reason, Request req) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- synchronized (sSyncLock) {
- AbstractSyncService svc = ssm.mServiceMap.get(mailboxId);
- if (svc == null) {
- if (ssm.mSyncErrorMap.containsKey(mailboxId) && reason == SyncManager.SYNC_UPSYNC) {
- return;
- } else if (reason != SyncManager.SYNC_UPSYNC) {
- ssm.mSyncErrorMap.remove(mailboxId);
- }
- Mailbox m = Mailbox.restoreMailboxWithId(ssm, mailboxId);
- if (m != null) {
- log("Starting sync for " + m.mDisplayName);
- ssm.requestSync(m, reason, req);
- }
- } else {
- // If this is a ui request, set the sync reason for the service
- if (reason >= SYNC_CALLBACK_START) {
- svc.mSyncReason = reason;
- }
- }
- }
- }
-
- // DO NOT CALL THIS IN A LOOP ON THE SERVICEMAP
- static public void stopManualSync(long mailboxId) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- synchronized (sSyncLock) {
- AbstractSyncService svc = ssm.mServiceMap.get(mailboxId);
- if (svc != null) {
- log("Stopping sync for " + svc.mMailboxName);
- svc.stop();
- svc.mThread.interrupt();
- ssm.releaseWakeLock(mailboxId);
- }
- }
- }
-
- /**
- * Wake up SyncServiceManager to check for mailboxes needing service
- */
- static public void kick(String reason) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- synchronized (ssm) {
- //INSTANCE.log("Kick: " + reason);
- ssm.mKicked = true;
- ssm.notify();
- }
- }
- if (sConnectivityLock != null) {
- synchronized (sConnectivityLock) {
- sConnectivityLock.notify();
- }
- }
- }
-
- /**
- * Tell SyncServiceManager to remove the mailbox from the map of mailboxes with sync errors
- * @param mailboxId the id of the mailbox
- */
- static public void removeFromSyncErrorMap(long mailboxId) {
- SyncManager ssm = INSTANCE;
- if (ssm != null) {
- ssm.mSyncErrorMap.remove(mailboxId);
- }
- }
-
- private boolean isRunningInServiceThread(long mailboxId) {
- AbstractSyncService syncService = getRunningService(mailboxId);
- Thread thisThread = Thread.currentThread();
- return syncService != null && syncService.mThread != null &&
- thisThread == syncService.mThread;
- }
-
- /**
- * Sent by services indicating that their thread is finished; action depends on the exitStatus
- * of the service.
- *
- * @param svc the service that is finished
- */
- static public void done(AbstractSyncService svc) {
- SyncManager ssm = INSTANCE;
- if (ssm == null) return;
- synchronized(sSyncLock) {
- long mailboxId = svc.mMailboxId;
- // If we're no longer the syncing thread for the mailbox, just return
- if (!ssm.isRunningInServiceThread(mailboxId)) {
- return;
- }
- ssm.releaseMailbox(mailboxId);
- ssm.setMailboxSyncStatus(mailboxId, EmailContent.SYNC_STATUS_NONE);
-
- ConcurrentHashMap errorMap = ssm.mSyncErrorMap;
- SyncError syncError = errorMap.get(mailboxId);
-
- int exitStatus = svc.mExitStatus;
- Mailbox m = Mailbox.restoreMailboxWithId(ssm, mailboxId);
- if (m == null) return;
-
- if (exitStatus != AbstractSyncService.EXIT_LOGIN_FAILURE) {
- long accountId = m.mAccountKey;
- Account account = Account.restoreAccountWithId(ssm, accountId);
- if (account == null) return;
- if (ssm.releaseSyncHolds(ssm,
- AbstractSyncService.EXIT_LOGIN_FAILURE, account)) {
- new AccountServiceProxy(ssm).notifyLoginSucceeded(accountId);
- }
- }
-
- int lastResult = EmailContent.LAST_SYNC_RESULT_SUCCESS;
- // For error states, whether the error is fatal (won't automatically be retried)
- boolean errorIsFatal = true;
- try {
- switch (exitStatus) {
- case AbstractSyncService.EXIT_DONE:
- if (svc.hasPendingRequests()) {
- // TODO Handle this case
- }
- errorMap.remove(mailboxId);
- // If we've had a successful sync, clear the shutdown count
- synchronized (SyncManager.class) {
- sClientConnectionManagerShutdownCount = 0;
- }
- // Leave now; other statuses are errors
- return;
- // I/O errors get retried at increasing intervals
- case AbstractSyncService.EXIT_IO_ERROR:
- if (syncError != null) {
- syncError.escalate();
- log(m.mDisplayName + " held for " + (syncError.holdDelay/ 1000) + "s");
- return;
- } else {
- log(m.mDisplayName + " added to syncErrorMap, hold for 15s");
- }
- lastResult = EmailContent.LAST_SYNC_RESULT_CONNECTION_ERROR;
- errorIsFatal = false;
- break;
- // These errors are not retried automatically
- case AbstractSyncService.EXIT_LOGIN_FAILURE:
- new AccountServiceProxy(ssm).notifyLoginFailed(m.mAccountKey, svc.mExitReason);
- lastResult = EmailContent.LAST_SYNC_RESULT_AUTH_ERROR;
- break;
- case AbstractSyncService.EXIT_SECURITY_FAILURE:
- case AbstractSyncService.EXIT_ACCESS_DENIED:
- lastResult = EmailContent.LAST_SYNC_RESULT_SECURITY_ERROR;
- break;
- case AbstractSyncService.EXIT_EXCEPTION:
- lastResult = EmailContent.LAST_SYNC_RESULT_INTERNAL_ERROR;
- break;
- }
- // Add this box to the error map
- errorMap.put(mailboxId, ssm.new SyncError(exitStatus, errorIsFatal));
- } finally {
- // Always set the last result
- ssm.setMailboxLastSyncResult(mailboxId, lastResult);
- kick("sync completed");
- }
- }
- }
-
- /**
- * Given the status string from a Mailbox, return the type code for the last sync
- * @param status the syncStatus column of a Mailbox
- * @return
- */
- static public int getStatusType(String status) {
- if (status == null) {
- return -1;
- } else {
- return status.charAt(STATUS_TYPE_CHAR) - '0';
- }
- }
-
- /**
- * Given the status string from a Mailbox, return the change count for the last sync
- * The change count is the number of adds + deletes + changes in the last sync
- * @param status the syncStatus column of a Mailbox
- * @return
- */
- static public int getStatusChangeCount(String status) {
- try {
- String s = status.substring(STATUS_CHANGE_COUNT_OFFSET);
- return Integer.parseInt(s);
- } catch (RuntimeException e) {
- return -1;
- }
- }
-
- static public Context getContext() {
- return INSTANCE;
- }
-
- private void writeWakeLockTimes(PrintWriter pw, HashMap map, boolean historical) {
- long now = System.currentTimeMillis();
- for (long mailboxId: map.keySet()) {
- Long time = map.get(mailboxId);
- if (time == null) {
- // Just in case...
- continue;
- }
- Mailbox mailbox = Mailbox.restoreMailboxWithId(this, mailboxId);
- StringBuilder sb = new StringBuilder();
- if (mailboxId == EXTRA_MAILBOX_ID) {
- sb.append(" SyncManager");
- } else if (mailbox == null) {
- sb.append(" Mailbox " + mailboxId + " (deleted?)");
- } else {
- String protocol = Account.getProtocol(this, mailbox.mAccountKey);
- sb.append(" Mailbox " + mailboxId + " (" + protocol + ", type " +
- mailbox.mType + ")");
- }
- long logTime = historical ? time : (now - time);
- sb.append(" held for " + (logTime / 1000) + "s");
- pw.println(sb.toString());
- }
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- long uptime = System.currentTimeMillis() - mServiceStartTime;
- pw.println("SyncManager: " + TAG + " up for " + (uptime / 1000 / 60) + " m");
- if (mWakeLock != null) {
- pw.println(" Holding WakeLock");
- writeWakeLockTimes(pw, mWakeLocks, false);
- } else {
- pw.println(" Not holding WakeLock");
- }
- if (!mWakeLocksHistory.isEmpty()) {
- pw.println(" Historical times");
- writeWakeLockTimes(pw, mWakeLocksHistory, true);
- }
- }
-}