Merge "Proper IMAP logout" into nyc-mr1-dev
diff --git a/src/com/android/phone/common/mail/store/ImapConnection.java b/src/com/android/phone/common/mail/store/ImapConnection.java
index 0360e3e..6945b94 100644
--- a/src/com/android/phone/common/mail/store/ImapConnection.java
+++ b/src/com/android/phone/common/mail/store/ImapConnection.java
@@ -30,6 +30,7 @@
 import com.android.phone.common.mail.store.imap.ImapUtility;
 import com.android.phone.common.mail.utils.LogUtils;
 import com.android.phone.vvm.omtp.OmtpEvents;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -110,7 +111,7 @@
             // The server should greet us with something like
             // * OK IMAP4rev1 Server
             // consume the response before doing anything else.
-            ImapResponse response = mParser.readResponse();
+            ImapResponse response = mParser.readResponse(false);
             if (!response.isOk()) {
                 mImapStore.getImapHelper()
                         .handleEvent(OmtpEvents.DATA_INVALID_INITIAL_SERVER_RESPONSE);
@@ -138,11 +139,26 @@
         }
     }
 
+    void logout() {
+        try {
+            sendCommand(ImapConstants.LOGOUT, false);
+            if (!mParser.readResponse(true).is(0, ImapConstants.BYE)) {
+                VvmLog.e(TAG, "Server did not respond LOGOUT with BYE");
+            }
+            if (!mParser.readResponse(false).isOk()) {
+                VvmLog.e(TAG, "Server did not respond OK after LOGOUT");
+            }
+        } catch (IOException | MessagingException e) {
+            VvmLog.e(TAG, "Error while logging out:" + e);
+        }
+    }
+
     /**
      * Closes the connection and releases all resources. This connection can not be used again
      * until {@link #setStore(ImapStore)} is called.
      */
     void close() {
+        logout();
         if (mTransport != null) {
             mTransport.close();
             mTransport = null;
@@ -323,7 +339,7 @@
     }
 
     public ImapResponse readResponse() throws IOException, MessagingException {
-        return mParser.readResponse();
+        return mParser.readResponse(false);
     }
 
     public List<ImapResponse> executeSimpleCommand(String command)
@@ -377,11 +393,12 @@
      * @throws IOException
      * @throws MessagingException
      */
-    List<ImapResponse> getCommandResponses() throws IOException, MessagingException {
+    List<ImapResponse> getCommandResponses()
+            throws IOException, MessagingException {
         final List<ImapResponse> responses = new ArrayList<ImapResponse>();
         ImapResponse response;
         do {
-            response = mParser.readResponse();
+            response = mParser.readResponse(false);
             responses.add(response);
         } while (!(response.isTagged() || response.isContinuationRequest()));
 
diff --git a/src/com/android/phone/common/mail/store/ImapFolder.java b/src/com/android/phone/common/mail/store/ImapFolder.java
index fe3df30..9b51f34 100644
--- a/src/com/android/phone/common/mail/store/ImapFolder.java
+++ b/src/com/android/phone/common/mail/store/ImapFolder.java
@@ -161,7 +161,6 @@
         }
         mMessageCount = -1;
         synchronized (this) {
-            mStore.closeConnection();
             mConnection = null;
         }
     }
diff --git a/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java b/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
index a6d2df6..6fc5abe 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
@@ -136,10 +136,12 @@
      * is stored in the internal storage.  When the {@link ImapResponse} is no longer used
      * {@link #destroyResponses} should be called to destroy all the responses in the array.
      *
+     * @param byeExpected is a untagged BYE response expected? If not proper cleanup will be done
+     * and {@link ByeException} will be thrown.
      * @return the parsed {@link ImapResponse} object.
-     * @exception ByeException when detects BYE.
+     * @exception ByeException when detects BYE and <code>byeExpected</code> is false.
      */
-    public ImapResponse readResponse() throws IOException, MessagingException {
+    public ImapResponse readResponse(boolean byeExpected) throws IOException, MessagingException {
         ImapResponse response = null;
         try {
             response = parseResponse();
@@ -154,7 +156,7 @@
         }
 
         // Handle this outside of try-catch.  We don't have to dump protocol log when getting BYE.
-        if (response.is(0, ImapConstants.BYE)) {
+        if (!byeExpected && response.is(0, ImapConstants.BYE)) {
             Log.w(TAG, ByeException.MESSAGE);
             response.destroy();
             throw new ByeException();
diff --git a/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java b/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
index e4230ff..3411228 100644
--- a/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
+++ b/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
@@ -175,8 +175,7 @@
         @Override
         public void onAvailable(Network network) {
             super.onAvailable(network);
-            ImapHelper helper = new ImapHelper(getContext(), mPhoneAccountHandle, network);
-            try {
+            try (ImapHelper helper = new ImapHelper(getContext(), mPhoneAccountHandle, network)) {
                 @ChangePinResult int result =
                         helper.changePin(mOldPin.getText().toString(),
                                 mNewPin.getText().toString());
diff --git a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
index fe3911c..d267e68 100644
--- a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
+++ b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
@@ -159,19 +159,21 @@
                 try {
                     while (mRetryCount > 0) {
                         VvmLog.i(TAG, "fetching voicemail, retry count=" + mRetryCount);
-                        ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
-                        if (!imapHelper.isSuccessfullyInitialized()) {
-                            VvmLog.w(TAG, "Can't retrieve Imap credentials.");
-                            return;
-                        }
+                        try (ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount,
+                                network)) {
+                            if (!imapHelper.isSuccessfullyInitialized()) {
+                                VvmLog.w(TAG, "Can't retrieve Imap credentials.");
+                                return;
+                            }
 
-                        boolean success = imapHelper.fetchVoicemailPayload(
-                                new VoicemailFetchedCallback(mContext, mUri), mUid);
-                        if (!success && mRetryCount > 0) {
-                            VvmLog.i(TAG, "fetch voicemail failed, retrying");
-                            mRetryCount--;
-                        } else {
-                            return;
+                            boolean success = imapHelper.fetchVoicemailPayload(
+                                    new VoicemailFetchedCallback(mContext, mUri), mUid);
+                            if (!success && mRetryCount > 0) {
+                                VvmLog.i(TAG, "fetch voicemail failed, retrying");
+                                mRetryCount--;
+                            } else {
+                                return;
+                            }
                         }
                     }
                 } finally {
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index 216b6a4..908d0f7 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -57,6 +57,7 @@
 
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -66,7 +67,7 @@
 /**
  * A helper interface to abstract commands sent across IMAP interface for a given account.
  */
-public class ImapHelper {
+public class ImapHelper implements Closeable {
 
     private static final String TAG = "ImapHelper";
 
@@ -126,6 +127,11 @@
                 VoicemailContract.Status.QUOTA_UNAVAILABLE);
     }
 
+    @Override
+    public void close() {
+        mImapStore.closeConnection();
+    }
+
     /**
      * If mImapStore is null, this means that there was a missing or badly formatted port number,
      * which means there aren't sufficient credentials for login. If mImapStore is succcessfully
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
index 306006d..b238c8d 100644
--- a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
@@ -148,9 +148,7 @@
             super.onAvailable(network);
             VvmLog.i(TAG, "new user: network available");
             ImapHelper helper = new ImapHelper(mContext, mPhoneAccount, network);
-
             try {
-
                 // VVM3 has inconsistent error language code to OMTP. Just issue a raw command
                 // here.
                 // TODO(b/29082671): use LocaleList
@@ -176,7 +174,10 @@
             } catch (MessagingException | IOException e) {
                 helper.handleEvent(OmtpEvents.VVM3_NEW_USER_SETUP_FAILED);
                 VvmLog.e(TAG, e.toString());
+            } finally {
+                helper.close();
             }
+
         }
 
         private boolean setPin(ImapHelper helper) throws IOException, MessagingException {
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index 74b1f66..9884e9d 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -235,35 +235,36 @@
         int retryCount = NETWORK_RETRY_COUNT;
         try {
             while (retryCount > 0) {
-                ImapHelper imapHelper = new ImapHelper(this, phoneAccount, network);
-                if (!imapHelper.isSuccessfullyInitialized()) {
-                    VvmLog.w(TAG, "Can't retrieve Imap credentials.");
-                    VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
-                            phoneAccount);
-                    return;
-                }
+                try (ImapHelper imapHelper = new ImapHelper(this, phoneAccount, network)) {
+                    if (!imapHelper.isSuccessfullyInitialized()) {
+                        VvmLog.w(TAG, "Can't retrieve Imap credentials.");
+                        VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
+                                phoneAccount);
+                        return;
+                    }
 
-                boolean success = true;
-                if (voicemail == null) {
-                    success = syncAll(action, imapHelper, phoneAccount);
-                } else {
-                    success = syncOne(imapHelper, voicemail, phoneAccount);
-                }
-                imapHelper.updateQuota();
+                    boolean success = true;
+                    if (voicemail == null) {
+                        success = syncAll(action, imapHelper, phoneAccount);
+                    } else {
+                        success = syncOne(imapHelper, voicemail, phoneAccount);
+                    }
+                    imapHelper.updateQuota();
 
-                // Need to check again for whether visual voicemail is enabled because it could have
-                // been disabled while waiting for the response from the network.
-                if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount) &&
-                        !success) {
-                    retryCount--;
-                    VvmLog.v(TAG, "Retrying " + action);
-                } else {
-                    // Nothing more to do here, just exit.
-                    VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
-                            phoneAccount);
+                    // Need to check again for whether visual voicemail is enabled because it could
+                    // have been disabled while waiting for the response from the network.
+                    if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount) &&
+                            !success) {
+                        retryCount--;
+                        VvmLog.v(TAG, "Retrying " + action);
+                    } else {
+                        // Nothing more to do here, just exit.
+                        VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
+                                phoneAccount);
 
-                    imapHelper.handleEvent(OmtpEvents.DATA_IMAP_OPERATION_COMPLETED);
-                    return;
+                        imapHelper.handleEvent(OmtpEvents.DATA_IMAP_OPERATION_COMPLETED);
+                        return;
+                    }
                 }
             }
         } finally {