summaryrefslogtreecommitdiffstats
path: root/provider_src/com/android/email/mail/store/ImapConnection.java
diff options
context:
space:
mode:
Diffstat (limited to 'provider_src/com/android/email/mail/store/ImapConnection.java')
-rw-r--r--provider_src/com/android/email/mail/store/ImapConnection.java85
1 files changed, 77 insertions, 8 deletions
diff --git a/provider_src/com/android/email/mail/store/ImapConnection.java b/provider_src/com/android/email/mail/store/ImapConnection.java
index bf4bb2a4c..bef5f346e 100644
--- a/provider_src/com/android/email/mail/store/ImapConnection.java
+++ b/provider_src/com/android/email/mail/store/ImapConnection.java
@@ -36,6 +36,7 @@ import com.android.emailcommon.mail.MessagingException;
import com.android.mail.utils.LogUtils;
import java.io.IOException;
+import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -50,6 +51,15 @@ class ImapConnection {
// Always check in FALSE
private static final boolean DEBUG_FORCE_SEND_ID = false;
+ // RFC 2177 defines that IDLE connections must be refreshed at least every 29 minutes
+ public static final int PING_IDLE_TIMEOUT = 29 * 60 * 1000;
+
+ // Special timeout for DONE operations
+ public static final int DONE_TIMEOUT = 5 * 1000;
+
+ // Time to wait between the first idle message and triggering the changes
+ private static final int IDLE_OP_READ_TIMEOUT = 500;
+
/** ID capability per RFC 2971*/
public static final int CAPABILITY_ID = 1 << 0;
/** NAMESPACE capability per RFC 2342 */
@@ -58,6 +68,8 @@ class ImapConnection {
public static final int CAPABILITY_STARTTLS = 1 << 2;
/** UIDPLUS capability per RFC 4315 */
public static final int CAPABILITY_UIDPLUS = 1 << 3;
+ /** IDLE capability per RFC 2177 */
+ public static final int CAPABILITY_IDLE = 1 << 4;
/** The capabilities supported; a set of CAPABILITY_* values. */
private int mCapabilities;
@@ -69,6 +81,8 @@ class ImapConnection {
private String mAccessToken;
private String mIdPhrase = null;
+ private boolean mIdling = false;
+
/** # of command/response lines to log upon crash. */
private static final int DISCOURSE_LOGGER_SIZE = 64;
private final DiscourseLogger mDiscourse = new DiscourseLogger(DISCOURSE_LOGGER_SIZE);
@@ -210,10 +224,23 @@ class ImapConnection {
mImapStore = null;
}
+ int getReadTimeout() throws IOException {
+ if (mTransport == null) {
+ return MailTransport.SOCKET_READ_TIMEOUT;
+ }
+ return mTransport.getReadTimeout();
+ }
+
+ void setReadTimeout(int timeout) throws IOException {
+ if (mTransport != null) {
+ mTransport.setReadTimeout(timeout);
+ }
+ }
+
/**
* Returns whether or not the specified capability is supported by the server.
*/
- private boolean isCapable(int capability) {
+ public boolean isCapable(int capability) {
return (mCapabilities & capability) != 0;
}
@@ -235,6 +262,9 @@ class ImapConnection {
if (capabilities.contains(ImapConstants.STARTTLS)) {
mCapabilities |= CAPABILITY_STARTTLS;
}
+ if (capabilities.contains(ImapConstants.IDLE)) {
+ mCapabilities |= CAPABILITY_IDLE;
+ }
}
/**
@@ -273,6 +303,12 @@ class ImapConnection {
*/
String sendCommand(String command, boolean sensitive)
throws MessagingException, IOException {
+ // Don't allow any command other than DONE when idling
+ if (mIdling && !command.equals(ImapConstants.DONE)) {
+ return null;
+ }
+ mIdling = command.equals(ImapConstants.IDLE);
+
LogUtils.d(Logging.LOG_TAG, "sendCommand %s", (sensitive ? IMAP_REDACTED_LOG : command));
open();
return sendCommandInternal(command, sensitive);
@@ -284,7 +320,13 @@ class ImapConnection {
throw new IOException("Null transport");
}
String tag = Integer.toString(mNextCommandTag.incrementAndGet());
- String commandToSend = tag + " " + command;
+ final String commandToSend;
+ if (command.equals(ImapConstants.DONE)) {
+ // Do not send a tag for DONE command
+ commandToSend = command;
+ } else {
+ commandToSend = tag + " " + command;
+ }
mTransport.writeLine(commandToSend, sensitive ? IMAP_REDACTED_LOG : null);
mDiscourse.addSentCommand(sensitive ? IMAP_REDACTED_LOG : commandToSend);
return tag;
@@ -327,6 +369,11 @@ class ImapConnection {
return executeSimpleCommand(command, false);
}
+ List<ImapResponse> executeIdleCommand() throws IOException, MessagingException {
+ mParser.expectIdlingResponse();
+ return executeSimpleCommand(ImapConstants.IDLE, false);
+ }
+
/**
* Read and return all of the responses from the most recent command sent to the server
*
@@ -336,13 +383,35 @@ class ImapConnection {
*/
List<ImapResponse> getCommandResponses() throws IOException, MessagingException {
final List<ImapResponse> responses = new ArrayList<ImapResponse>();
- ImapResponse response;
- do {
- response = mParser.readResponse();
- responses.add(response);
- } while (!response.isTagged());
+ ImapResponse response = null;
+ boolean idling = false;
+ boolean throwSocketTimeoutEx = true;
+ int lastSocketTimeout = getReadTimeout();
+ try {
+ do {
+ response = mParser.readResponse();
+ if (idling) {
+ setReadTimeout(IDLE_OP_READ_TIMEOUT);
+ throwSocketTimeoutEx = false;
+ }
+ responses.add(response);
+ if (response.isIdling()) {
+ idling = true;
+ }
+ } while (idling || !response.isTagged());
+ } catch (SocketTimeoutException ex) {
+ if (throwSocketTimeoutEx) {
+ throw ex;
+ }
+ } finally {
+ mParser.resetIdlingStatus();
+ if (lastSocketTimeout != getReadTimeout()) {
+ setReadTimeout(lastSocketTimeout);
+ }
+ }
- if (!response.isOk()) {
+ // When idling, any response is valid; otherwise it must be OK
+ if (!response.isOk() && !idling) {
final String toString = response.toString();
final String status = response.getStatusOrEmpty().getString();
final String alert = response.getAlertTextOrEmpty().getString();