Merge "Use consumeContent() to ensure release of HttpClient resources"
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index 70c531f..6a388be 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -412,73 +412,80 @@
             // Any string will do, but we'll go for "validate"
             svc.mDeviceId = "validate";
             HttpResponse resp = svc.sendHttpClientOptions();
-            int code = resp.getStatusLine().getStatusCode();
-            userLog("Validation (OPTIONS) response: " + code);
-            if (code == HttpStatus.SC_OK) {
-                // No exception means successful validation
-                Header commands = resp.getFirstHeader("MS-ASProtocolCommands");
-                Header versions = resp.getFirstHeader("ms-asprotocolversions");
-                // Make sure we've got the right protocol version set up
-                try {
-                    if (commands == null || versions == null) {
-                        userLog("OPTIONS response without commands or versions");
-                        // We'll treat this as a protocol exception
-                        throw new MessagingException(0);
+            HttpEntity entity = resp.getEntity();
+            try {
+                int code = resp.getStatusLine().getStatusCode();
+                userLog("Validation (OPTIONS) response: " + code);
+                if (code == HttpStatus.SC_OK) {
+                    // No exception means successful validation
+                    Header commands = resp.getFirstHeader("MS-ASProtocolCommands");
+                    Header versions = resp.getFirstHeader("ms-asprotocolversions");
+                    // Make sure we've got the right protocol version set up
+                    try {
+                        if (commands == null || versions == null) {
+                            userLog("OPTIONS response without commands or versions");
+                            // We'll treat this as a protocol exception
+                            throw new MessagingException(0);
+                        }
+                        setupProtocolVersion(svc, versions);
+                    } catch (MessagingException e) {
+                        bundle.putInt(EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE,
+                                MessagingException.PROTOCOL_VERSION_UNSUPPORTED);
+                        return bundle;
                     }
-                    setupProtocolVersion(svc, versions);
-                } catch (MessagingException e) {
-                    bundle.putInt(EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE,
-                            MessagingException.PROTOCOL_VERSION_UNSUPPORTED);
-                    return bundle;
-                }
 
-                // Run second test here for provisioning failures using FolderSync
-                userLog("Try folder sync");
-                // Send "0" as the sync key for new accounts; otherwise we'll use the current key
-                String syncKey = "0";
-                Account existingAccount =
-                    Utility.findExistingAccount(context, -1L, hostAddress, userName);
-                if (existingAccount != null && existingAccount.mSyncKey != null) {
-                    syncKey = existingAccount.mSyncKey;
-                }
-                Serializer s = new Serializer();
-                s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY).text(syncKey)
-                    .end().end().done();
-                resp = svc.sendHttpClientPost("FolderSync", s.toByteArray());
-                code = resp.getStatusLine().getStatusCode();
-                // We'll get one of the following responses if policies are required by the server
-                if (code == HttpStatus.SC_FORBIDDEN || code == HTTP_NEED_PROVISIONING) {
-                    // Get the policies and see if we are able to support them
-                    ProvisionParser pp = svc.canProvision();
-                    if (pp != null) {
-                        // If so, set the proper result code and save the PolicySet in our Bundle
-                        resultCode = MessagingException.SECURITY_POLICIES_REQUIRED;
-                        bundle.putParcelable(EmailServiceProxy.VALIDATE_BUNDLE_POLICY_SET,
-                                pp.getPolicySet());
-                    } else
-                        // If not, set the proper code (the account will not be created)
-                        resultCode = MessagingException.SECURITY_POLICIES_UNSUPPORTED;
-                } else if (code == HttpStatus.SC_NOT_FOUND) {
-                    // We get a 404 from OWA addresses (which are NOT EAS addresses)
-                    resultCode = MessagingException.PROTOCOL_VERSION_UNSUPPORTED;
-                } else if (code != HttpStatus.SC_OK) {
-                    // Fail generically with anything other than success
-                    userLog("Unexpected response for FolderSync: ", code);
-                    resultCode = MessagingException.UNSPECIFIED_EXCEPTION;
+                    // Run second test here for provisioning failures using FolderSync
+                    userLog("Try folder sync");
+                    // Send "0" as the sync key for new accounts; otherwise, use the current key
+                    String syncKey = "0";
+                    Account existingAccount =
+                        Utility.findExistingAccount(context, -1L, hostAddress, userName);
+                    if (existingAccount != null && existingAccount.mSyncKey != null) {
+                        syncKey = existingAccount.mSyncKey;
+                    }
+                    Serializer s = new Serializer();
+                    s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY).text(syncKey)
+                        .end().end().done();
+                    resp = svc.sendHttpClientPost("FolderSync", s.toByteArray());
+                    code = resp.getStatusLine().getStatusCode();
+                    // We'll get one of the following responses if policies are required
+                    if (code == HttpStatus.SC_FORBIDDEN || code == HTTP_NEED_PROVISIONING) {
+                        // Get the policies and see if we are able to support them
+                        ProvisionParser pp = svc.canProvision();
+                        if (pp != null) {
+                            // Set the proper result code and save the PolicySet in our Bundle
+                            resultCode = MessagingException.SECURITY_POLICIES_REQUIRED;
+                            bundle.putParcelable(EmailServiceProxy.VALIDATE_BUNDLE_POLICY_SET,
+                                    pp.getPolicySet());
+                        } else
+                            // If not, set the proper code (the account will not be created)
+                            resultCode = MessagingException.SECURITY_POLICIES_UNSUPPORTED;
+                    } else if (code == HttpStatus.SC_NOT_FOUND) {
+                        // We get a 404 from OWA addresses (which are NOT EAS addresses)
+                        resultCode = MessagingException.PROTOCOL_VERSION_UNSUPPORTED;
+                    } else if (code != HttpStatus.SC_OK) {
+                        // Fail generically with anything other than success
+                        userLog("Unexpected response for FolderSync: ", code);
+                        resultCode = MessagingException.UNSPECIFIED_EXCEPTION;
+                    } else {
+                        userLog("Validation successful");
+                    }
+                } else if (isAuthError(code)) {
+                    userLog("Authentication failed");
+                    resultCode = MessagingException.AUTHENTICATION_FAILED;
+                } else if (code == INTERNAL_SERVER_ERROR_CODE) {
+                    // For Exchange 2003, this could mean an authentication failure OR server error
+                    userLog("Internal server error");
+                    resultCode = MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR;
                 } else {
-                    userLog("Validation successful");
+                    // TODO Need to catch other kinds of errors (e.g. policy) For now, report code.
+                    userLog("Validation failed, reporting I/O error: ", code);
+                    resultCode = MessagingException.IOERROR;
                 }
-            } else if (isAuthError(code)) {
-                userLog("Authentication failed");
-                resultCode = MessagingException.AUTHENTICATION_FAILED;
-            } else if (code == INTERNAL_SERVER_ERROR_CODE) {
-                // For Exchange 2003, this could mean an authenticiation failure OR a server error
-                userLog("Internal server error");
-                resultCode = MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR;
-            } else {
-                // TODO Need to catch other kinds of errors (e.g. policy) For now, report the code.
-                userLog("Validation failed, reporting I/O error: ", code);
-                resultCode = MessagingException.IOERROR;
+            } finally {
+                if (entity != null) {
+                    entity.consumeContent();
+                }
             }
         } catch (IOException e) {
             Throwable cause = e.getCause();
@@ -631,15 +638,14 @@
                 resp = postAutodiscover(client, post, true /*canRetry*/);
             }
 
-            // Get the "final" code; if it's not 200, just return null
-            int code = resp.getStatusLine().getStatusCode();
-            userLog("Code: " + code);
-            if (code != HttpStatus.SC_OK) return null;
-
-            // At this point, we have a 200 response (SC_OK)
-            HttpEntity e = resp.getEntity();
-            InputStream is = e.getContent();
+            HttpEntity entity = resp.getEntity();
             try {
+                // Get the "final" code; if it's not 200, just return null
+                int code = resp.getStatusLine().getStatusCode();
+                userLog("Code: " + code);
+                if (code != HttpStatus.SC_OK) return null;
+
+                InputStream is = entity.getContent();
                 // The response to Autodiscover is regular XML (not WBXML)
                 // If we ever get an error in this process, we'll just punt and return null
                 XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
@@ -676,6 +682,10 @@
             } catch (XmlPullParserException e1) {
                 // This would indicate an I/O error of some sort
                 // We will simply return null and user can configure manually
+            } finally {
+               if (entity != null) {
+                   entity.consumeContent();
+               }
             }
         // There's no reason at all for exceptions to be thrown, and it's ok if so.
         // We just won't do auto-discover; user can configure manually
@@ -853,18 +863,29 @@
                 s.end().end().end().done();
                 if (DEBUG_GAL_SERVICE) svc.userLog("GAL lookup starting for " + ha.mAddress);
                 HttpResponse resp = svc.sendHttpClientPost("Search", s.toByteArray());
-                int code = resp.getStatusLine().getStatusCode();
-                if (code == HttpStatus.SC_OK) {
-                    InputStream is = resp.getEntity().getContent();
-                    GalParser gp = new GalParser(is, svc);
-                    if (gp.parse()) {
-                        if (DEBUG_GAL_SERVICE) svc.userLog("GAL lookup OK for " + ha.mAddress);
-                        return gp.getGalResult();
+                HttpEntity entity = resp.getEntity();
+                try {
+                    int code = resp.getStatusLine().getStatusCode();
+                    if (code == HttpStatus.SC_OK) {
+                        InputStream is = entity.getContent();
+                        try {
+                            GalParser gp = new GalParser(is, svc);
+                            if (gp.parse()) {
+                                if (DEBUG_GAL_SERVICE) svc.userLog("GAL lookup OK: " + ha.mAddress);
+                                return gp.getGalResult();
+                            } else {
+                                if (DEBUG_GAL_SERVICE) svc.userLog("GAL lookup: no matches");
+                            }
+                        } finally {
+                            is.close();
+                        }
                     } else {
-                        if (DEBUG_GAL_SERVICE) svc.userLog("GAL lookup returned no matches");
+                        svc.userLog("GAL lookup returned " + code);
                     }
-                } else {
-                    svc.userLog("GAL lookup returned " + code);
+                } finally {
+                    if (entity != null) {
+                        entity.consumeContent();
+                    }
                 }
             } catch (IOException e) {
                 // GAL is non-critical; we'll just go on
@@ -935,80 +956,85 @@
 
         String cmd = "GetAttachment&AttachmentName=" + att.mLocation;
         HttpResponse res = sendHttpClientPost(cmd, null, COMMAND_TIMEOUT);
+        HttpEntity entity = res.getEntity();
 
-        int status = res.getStatusLine().getStatusCode();
-        if (status == HttpStatus.SC_OK) {
-            HttpEntity e = res.getEntity();
-            int len = (int)e.getContentLength();
-            InputStream is = res.getEntity().getContent();
-            File f = (req.mDestination != null)
-                    ? new File(req.mDestination)
-                    : createUniqueFileInternal(req.mDestination, att.mFileName);
-            if (f != null) {
-                // Ensure that the target directory exists
-                File destDir = f.getParentFile();
-                if (!destDir.exists()) {
-                    destDir.mkdirs();
-                }
-                FileOutputStream os = new FileOutputStream(f);
-                // len > 0 means that Content-Length was set in the headers
-                // len < 0 means "chunked" transfer-encoding
-                if (len != 0) {
-                    try {
-                        mPendingRequest = req;
-                        byte[] bytes = new byte[CHUNK_SIZE];
-                        int length = len;
-                        // Loop terminates 1) when EOF is reached or 2) if an IOException occurs
-                        // One of these is guaranteed to occur
-                        int totalRead = 0;
-                        userLog("Attachment content-length: ", len);
-                        while (true) {
-                            int read = is.read(bytes, 0, CHUNK_SIZE);
+        try {
+            int status = res.getStatusLine().getStatusCode();
+            if (status == HttpStatus.SC_OK) {
+                int len = (int)entity.getContentLength();
+                InputStream is = entity.getContent();
+                File f = (req.mDestination != null) ? new File(req.mDestination) :
+                    createUniqueFileInternal(req.mDestination, att.mFileName);
+                if (f != null) {
+                    // Ensure that the target directory exists
+                    File destDir = f.getParentFile();
+                    if (!destDir.exists()) {
+                        destDir.mkdirs();
+                    }
+                    FileOutputStream os = new FileOutputStream(f);
+                    // len > 0 means that Content-Length was set in the headers
+                    // len < 0 means "chunked" transfer-encoding
+                    if (len != 0) {
+                        try {
+                            mPendingRequest = req;
+                            byte[] bytes = new byte[CHUNK_SIZE];
+                            int length = len;
+                            // Loop terminates 1) when EOF is reached or 2) IOException occurs
+                            // One of these is guaranteed to occur
+                            int totalRead = 0;
+                            userLog("Attachment content-length: ", len);
+                            while (true) {
+                                int read = is.read(bytes, 0, CHUNK_SIZE);
 
-                            // read < 0 means that EOF was reached
-                            if (read < 0) {
-                                userLog("Attachment load reached EOF, totalRead: ", totalRead);
-                                break;
-                            }
-
-                            // Keep track of how much we've read for progress callback
-                            totalRead += read;
-
-                            // Write these bytes out
-                            os.write(bytes, 0, read);
-
-                            // We can't report percentages if this is chunked; by definition, the
-                            // length of incoming data is unknown
-                            if (length > 0) {
-                                // Belt and suspenders check to prevent runaway reading
-                                if (totalRead > length) {
-                                    errorLog("totalRead is greater than attachment length?");
+                                // read < 0 means that EOF was reached
+                                if (read < 0) {
+                                    userLog("Attachment load reached EOF, totalRead: ", totalRead);
                                     break;
                                 }
-                                int pct = (totalRead * 100) / length;
-                                doProgressCallback(msg.mId, att.mId, pct);
+
+                                // Keep track of how much we've read for progress callback
+                                totalRead += read;
+
+                                // Write these bytes out
+                                os.write(bytes, 0, read);
+
+                                // We can't report percentages if this is chunked; the
+                                // length of incoming data is unknown
+                                if (length > 0) {
+                                    // Belt and suspenders check to prevent runaway reading
+                                    if (totalRead > length) {
+                                        errorLog("totalRead is greater than attachment length?");
+                                        break;
+                                    }
+                                    int pct = (totalRead * 100) / length;
+                                    doProgressCallback(msg.mId, att.mId, pct);
+                                }
                             }
-                       }
-                    } finally {
-                        mPendingRequest = null;
+                        } finally {
+                            mPendingRequest = null;
+                        }
+                    }
+                    os.flush();
+                    os.close();
+
+                    // EmailProvider will throw an exception if update an unsaved attachment
+                    if (att.isSaved()) {
+                        String contentUriString = (req.mContentUriString != null) ?
+                            req.mContentUriString :
+                            "file://" + f.getAbsolutePath();
+                        ContentValues cv = new ContentValues();
+                        cv.put(AttachmentColumns.CONTENT_URI, contentUriString);
+                        att.update(mContext, cv);
+                        doStatusCallback(msg.mId, att.mId, EmailServiceStatus.SUCCESS);
                     }
                 }
-                os.flush();
-                os.close();
-
-                // EmailProvider will throw an exception if we try to update an unsaved attachment
-                if (att.isSaved()) {
-                    String contentUriString = (req.mContentUriString != null)
-                            ? req.mContentUriString
-                            : "file://" + f.getAbsolutePath();
-                    ContentValues cv = new ContentValues();
-                    cv.put(AttachmentColumns.CONTENT_URI, contentUriString);
-                    att.update(mContext, cv);
-                    doStatusCallback(msg.mId, att.mId, EmailServiceStatus.SUCCESS);
-                }
+            } else {
+                doStatusCallback(msg.mId, att.mId, EmailServiceStatus.MESSAGE_NOT_FOUND);
             }
-        } else {
-            doStatusCallback(msg.mId, att.mId, EmailServiceStatus.MESSAGE_NOT_FOUND);
+        } finally {
+            if (entity != null) {
+                entity.consumeContent();
+            }
         }
     }
 
@@ -1115,44 +1141,52 @@
         s.data(Tags.MOVE_DSTFLDID, dstMailbox.mServerId);
         s.end().end().done();
         HttpResponse res = sendHttpClientPost("MoveItems", s.toByteArray());
-        int status = res.getStatusLine().getStatusCode();
-        if (status == HttpStatus.SC_OK) {
-            HttpEntity e = res.getEntity();
-            int len = (int)e.getContentLength();
-            InputStream is = res.getEntity().getContent();
-            if (len != 0) {
-                MoveItemsParser p = new MoveItemsParser(is, this);
-                p.parse();
-                int statusCode = p.getStatusCode();
-                ContentValues cv = new ContentValues();
-                if (statusCode == MoveItemsParser.STATUS_CODE_REVERT) {
-                    // Restore the old mailbox id
-                    cv.put(MessageColumns.MAILBOX_KEY, srcMailbox.mServerId);
-                    mContentResolver.update(ContentUris.withAppendedId(
-                            Message.CONTENT_URI, req.mMessageId), cv, null, null);
-                } else if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS) {
-                    // Update with the new server id
-                    cv.put(SyncColumns.SERVER_ID, p.getNewServerId());
-                    cv.put(Message.FLAGS, msg.mFlags | MESSAGE_FLAG_MOVED_MESSAGE);
-                    mContentResolver.update(ContentUris.withAppendedId(
-                            Message.CONTENT_URI, req.mMessageId), cv, null, null);
-                }
-                if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS ||
-                        statusCode == MoveItemsParser.STATUS_CODE_REVERT) {
-                    // If we revert or if we succeeded, we no longer need the update information
-                    // OR the now-duplicate email (the new copy will eventually be synced down)
-                    mContentResolver.delete(ContentUris.withAppendedId(
-                            Message.UPDATED_CONTENT_URI, req.mMessageId), null, null);
-                } else {
-                    // In this case, we're retrying, so do nothing.  The request will be handled
-                    // next sync
-                }
+        HttpEntity entity = res.getEntity();
+        try {
+            int status = res.getStatusLine().getStatusCode();
+            if (status == HttpStatus.SC_OK) {
+                    int len = (int) entity.getContentLength();
+                    InputStream is = res.getEntity().getContent();
+                    if (len != 0) {
+                        MoveItemsParser p = new MoveItemsParser(is, this);
+                        p.parse();
+                        int statusCode = p.getStatusCode();
+                        ContentValues cv = new ContentValues();
+                        if (statusCode == MoveItemsParser.STATUS_CODE_REVERT) {
+                            // Restore the old mailbox id
+                            cv.put(MessageColumns.MAILBOX_KEY, srcMailbox.mServerId);
+                            mContentResolver.update(
+                                    ContentUris.withAppendedId(Message.CONTENT_URI, req.mMessageId),
+                                    cv, null, null);
+                        } else if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS) {
+                            // Update with the new server id
+                            cv.put(SyncColumns.SERVER_ID, p.getNewServerId());
+                            cv.put(Message.FLAGS, msg.mFlags | MESSAGE_FLAG_MOVED_MESSAGE);
+                            mContentResolver.update(
+                                    ContentUris.withAppendedId(Message.CONTENT_URI, req.mMessageId),
+                                    cv, null, null);
+                        }
+                        if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS
+                                || statusCode == MoveItemsParser.STATUS_CODE_REVERT) {
+                            // If we revert or succeed, we no longer need the update information
+                            // OR the now-duplicate email (the new copy will be synced down)
+                            mContentResolver.delete(ContentUris.withAppendedId(
+                                    Message.UPDATED_CONTENT_URI, req.mMessageId), null, null);
+                        } else {
+                            // In this case, we're retrying, so do nothing.  The request will be
+                            // handled next sync
+                        }
+                    }
+            } else if (isAuthError(status)) {
+                throw new EasAuthenticationException();
+            } else {
+                userLog("Move items request failed, code: " + status);
+                throw new IOException();
             }
-        } else if (isAuthError(status)) {
-            throw new EasAuthenticationException();
-        } else {
-            userLog("Move items request failed, code: " + status);
-            throw new IOException();
+        } finally {
+            if (entity != null) {
+                entity.consumeContent();
+            }
         }
     }
 
@@ -1175,30 +1209,36 @@
         s.data(Tags.MREQ_REQ_ID, msg.mServerId);
         s.end().end().done();
         HttpResponse res = sendHttpClientPost("MeetingResponse", s.toByteArray());
-        int status = res.getStatusLine().getStatusCode();
-        if (status == HttpStatus.SC_OK) {
-            HttpEntity e = res.getEntity();
-            int len = (int)e.getContentLength();
-            InputStream is = res.getEntity().getContent();
-            if (len != 0) {
-                new MeetingResponseParser(is, this).parse();
-                String meetingInfo = msg.mMeetingInfo;
-                if (meetingInfo != null) {
-                    String responseRequested =
-                        new PackedString(meetingInfo).get(MeetingInfo.MEETING_RESPONSE_REQUESTED);
-                    // If there's no tag, or a non-zero tag, we send the response mail
-                    if ("0".equals(responseRequested)) {
-                        return;
+        HttpEntity entity = res.getEntity();
+        try {
+            int status = res.getStatusLine().getStatusCode();
+            if (status == HttpStatus.SC_OK) {
+                int len = (int)entity.getContentLength();
+                if (len != 0) {
+                    InputStream is = res.getEntity().getContent();
+                    new MeetingResponseParser(is, this).parse();
+                    String meetingInfo = msg.mMeetingInfo;
+                    if (meetingInfo != null) {
+                        String responseRequested = new PackedString(meetingInfo).get(
+                                MeetingInfo.MEETING_RESPONSE_REQUESTED);
+                        // If there's no tag, or a non-zero tag, we send the response mail
+                        if ("0".equals(responseRequested)) {
+                            return;
+                        }
                     }
+                    sendMeetingResponseMail(msg, req.mResponse);
                 }
-                sendMeetingResponseMail(msg, req.mResponse);
+            } else if (isAuthError(status)) {
+                throw new EasAuthenticationException();
+            } else {
+                userLog("Meeting response request failed, code: " + status);
+                throw new IOException();
             }
-        } else if (isAuthError(status)) {
-            throw new EasAuthenticationException();
-        } else {
-            userLog("Meeting response request failed, code: " + status);
-            throw new IOException();
-        }
+        } finally {
+            if (entity != null) {
+                entity.consumeContent();
+            }
+       }
     }
 
     /**
@@ -1462,27 +1502,34 @@
         s.start(Tags.PROVISION_POLICY).data(Tags.PROVISION_POLICY_TYPE, getPolicyType())
             .end().end().end().done();
         HttpResponse resp = sendHttpClientPost("Provision", s.toByteArray());
-        int code = resp.getStatusLine().getStatusCode();
-        if (code == HttpStatus.SC_OK) {
-            InputStream is = resp.getEntity().getContent();
-            ProvisionParser pp = new ProvisionParser(is, this);
-            if (pp.parse()) {
-                // The PolicySet in the ProvisionParser will have the requirements for all KNOWN
-                // policies.  If others are required, hasSupportablePolicySet will be false
-                if (pp.hasSupportablePolicySet()) {
-                    // If the policies are supportable (in this context, meaning that there are no
-                    // completely unimplemented policies required), just return the parser itself
-                    return pp;
-                } else {
-                    // Try to acknowledge using the "partial" status (i.e. we can partially
-                    // accommodate the required policies).  The server will agree to this if the
-                    // "allow non-provisionable devices" setting is enabled on the server
-                    String policyKey = acknowledgeProvision(pp.getPolicyKey(),
-                            PROVISION_STATUS_PARTIAL);
-                    // Return either the parser (success) or null (failure)
-                    return (policyKey != null) ? pp : null;
+        HttpEntity entity = resp.getEntity();
+        try {
+            int code = resp.getStatusLine().getStatusCode();
+            if (code == HttpStatus.SC_OK) {
+                InputStream is = entity.getContent();
+                ProvisionParser pp = new ProvisionParser(is, this);
+                if (pp.parse()) {
+                    // The PolicySet in the ProvisionParser will have the requirements for all KNOWN
+                    // policies.  If others are required, hasSupportablePolicySet will be false
+                    if (pp.hasSupportablePolicySet()) {
+                        // If the policies are supportable (in this context, meaning there are no
+                        // completely unimplemented policies required), return the parser itself
+                        return pp;
+                    } else {
+                        // Try to acknowledge using the "partial" status (i.e. we can partially
+                        // accommodate the required policies).  The server will agree to this if the
+                        // "allow non-provisionable devices" setting is enabled on the server
+                        String policyKey = acknowledgeProvision(pp.getPolicyKey(),
+                                PROVISION_STATUS_PARTIAL);
+                        // Return either the parser (success) or null (failure)
+                        return (policyKey != null) ? pp : null;
+                    }
                 }
             }
+        } finally {
+            if (entity != null) {
+                entity.consumeContent();
+            }
         }
         // On failures, simply return null
         return null;
@@ -1522,13 +1569,20 @@
         }
         s.end().done(); // PROVISION_PROVISION
         HttpResponse resp = sendHttpClientPost("Provision", s.toByteArray());
-        int code = resp.getStatusLine().getStatusCode();
-        if (code == HttpStatus.SC_OK) {
-            InputStream is = resp.getEntity().getContent();
-            ProvisionParser pp = new ProvisionParser(is, this);
-            if (pp.parse()) {
-                // Return the final policy key from the ProvisionParser
-                return pp.getPolicyKey();
+        HttpEntity entity = resp.getEntity();
+        try {
+            int code = resp.getStatusLine().getStatusCode();
+            if (code == HttpStatus.SC_OK) {
+                InputStream is = entity.getContent();
+                ProvisionParser pp = new ProvisionParser(is, this);
+                if (pp.parse()) {
+                    // Return the final policy key from the ProvisionParser
+                    return pp.getPolicyKey();
+                }
+            }
+        } finally {
+            if (entity != null) {
+                entity.consumeContent();
             }
         }
         // On failures, return null
@@ -1636,37 +1690,44 @@
                 s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
                     .text(mAccount.mSyncKey).end().end().done();
                 HttpResponse resp = sendHttpClientPost("FolderSync", s.toByteArray());
-                if (mStop) break;
-                int code = resp.getStatusLine().getStatusCode();
-                if (code == HttpStatus.SC_OK) {
-                    HttpEntity entity = resp.getEntity();
-                    int len = (int)entity.getContentLength();
-                    if (len != 0) {
-                        InputStream is = entity.getContent();
-                        // Returns true if we need to sync again
-                        if (new FolderSyncParser(is, new AccountSyncAdapter(this)).parse()) {
+                HttpEntity entity = resp.getEntity();
+                try {
+                    if (mStop) break;
+                    int code = resp.getStatusLine().getStatusCode();
+                    if (code == HttpStatus.SC_OK) {
+                            int len = (int)entity.getContentLength();
+                            if (len != 0) {
+                                InputStream is = entity.getContent();
+                                // Returns true if we need to sync again
+                                if (new FolderSyncParser(is, new AccountSyncAdapter(this))
+                                        .parse()) {
+                                    continue;
+                                }
+                            }
+                    } else if (isProvisionError(code)) {
+                        // If the sync error is a provisioning failure (perhaps policies changed),
+                        // let's try the provisioning procedure
+                        // Provisioning must only be attempted for the account mailbox - trying to
+                        // provision any other mailbox may result in race conditions and the
+                        // creation of multiple policy keys.
+                        if (!tryProvision()) {
+                            // Set the appropriate failure status
+                            mExitStatus = EXIT_SECURITY_FAILURE;
+                            return;
+                        } else {
+                            // If we succeeded, try again...
                             continue;
                         }
-                    }
-                } else if (isProvisionError(code)) {
-                    // If the sync error is a provisioning failure (perhaps the policies changed),
-                    // let's try the provisioning procedure
-                    // Provisioning must only be attempted for the account mailbox - trying to
-                    // provision any other mailbox may result in race conditions and the creation
-                    // of multiple policy keys.
-                    if (!tryProvision()) {
-                        // Set the appropriate failure status
-                        mExitStatus = EXIT_SECURITY_FAILURE;
+                    } else if (isAuthError(code)) {
+                        mExitStatus = EXIT_LOGIN_FAILURE;
                         return;
                     } else {
-                        // If we succeeded, try again...
-                        continue;
+                        userLog("FolderSync response error: ", code);
                     }
-                } else if (isAuthError(code)) {
-                    mExitStatus = EXIT_LOGIN_FAILURE;
-                    return;
-                } else {
-                    userLog("FolderSync response error: ", code);
+                } finally {
+                    if (entity != null) {
+                        entity.consumeContent();
+                    }
                 }
 
                 // Change all push/hold boxes to push
@@ -1906,48 +1967,55 @@
                     }
                     HttpResponse res =
                         sendPing(s.toByteArray(), forcePing ? mPingForceHeartbeat : pingHeartbeat);
+                    HttpEntity entity = res.getEntity();
 
-                    int code = res.getStatusLine().getStatusCode();
-                    userLog("Ping response: ", code);
+                    try {
+                        int code = res.getStatusLine().getStatusCode();
+                        userLog("Ping response: ", code);
 
-                    // Return immediately if we've been asked to stop during the ping
-                    if (mStop) {
-                        userLog("Stopping pingLoop");
-                        return;
-                    }
+                        // Return immediately if we've been asked to stop during the ping
+                        if (mStop) {
+                            userLog("Stopping pingLoop");
+                            return;
+                        }
 
-                    if (code == HttpStatus.SC_OK) {
-                        // Make sure to clear out any pending sync errors
-                        ExchangeService.removeFromSyncErrorMap(mMailboxId);
-                        HttpEntity e = res.getEntity();
-                        int len = (int)e.getContentLength();
-                        InputStream is = res.getEntity().getContent();
-                        if (len != 0) {
-                            int pingResult = parsePingResult(is, mContentResolver, pingErrorMap);
-                            // If our ping completed (status = 1), and we weren't forced and we're
-                            // not at the maximum, try increasing timeout by two minutes
-                            if (pingResult == PROTOCOL_PING_STATUS_COMPLETED && !forcePing) {
-                                if (pingHeartbeat > mPingHighWaterMark) {
-                                    mPingHighWaterMark = pingHeartbeat;
-                                    userLog("Setting high water mark at: ", mPingHighWaterMark);
-                                }
-                                if ((pingHeartbeat < mPingMaxHeartbeat) &&
-                                        !mPingHeartbeatDropped) {
-                                    pingHeartbeat += PING_HEARTBEAT_INCREMENT;
-                                    if (pingHeartbeat > mPingMaxHeartbeat) {
-                                        pingHeartbeat = mPingMaxHeartbeat;
+                        if (code == HttpStatus.SC_OK) {
+                            // Make sure to clear out any pending sync errors
+                            ExchangeService.removeFromSyncErrorMap(mMailboxId);
+                            int len = (int)entity.getContentLength();
+                            InputStream is = entity.getContent();
+                            if (len != 0) {
+                                int pingResult = parsePingResult(is, mContentResolver,
+                                        pingErrorMap);
+                                // If our ping completed (status = 1), and wasn't forced and we're
+                                // not at the maximum, try increasing timeout by two minutes
+                                if (pingResult == PROTOCOL_PING_STATUS_COMPLETED && !forcePing) {
+                                    if (pingHeartbeat > mPingHighWaterMark) {
+                                        mPingHighWaterMark = pingHeartbeat;
+                                        userLog("Setting high water mark at: ", mPingHighWaterMark);
                                     }
-                                    userLog("Increasing ping heartbeat to ", pingHeartbeat, "s");
+                                    if ((pingHeartbeat < mPingMaxHeartbeat) &&
+                                            !mPingHeartbeatDropped) {
+                                        pingHeartbeat += PING_HEARTBEAT_INCREMENT;
+                                        if (pingHeartbeat > mPingMaxHeartbeat) {
+                                            pingHeartbeat = mPingMaxHeartbeat;
+                                        }
+                                        userLog("Increase ping heartbeat to ", pingHeartbeat, "s");
+                                    }
                                 }
+                            } else {
+                                userLog("Ping returned empty result; throwing IOException");
+                                throw new IOException();
                             }
-                        } else {
-                            userLog("Ping returned empty result; throwing IOException");
+                        } else if (isAuthError(code)) {
+                            mExitStatus = EXIT_LOGIN_FAILURE;
+                            userLog("Authorization error during Ping: ", code);
                             throw new IOException();
                         }
-                    } else if (isAuthError(code)) {
-                        mExitStatus = EXIT_LOGIN_FAILURE;
-                        userLog("Authorization error during Ping: ", code);
-                        throw new IOException();
+                    } finally {
+                        if (entity != null) {
+                            entity.consumeContent();
+                        }
                     }
                 } catch (IOException e) {
                     String message = e.getMessage();
@@ -2192,59 +2260,66 @@
             s.end().end().end().done();
             HttpResponse resp = sendHttpClientPost("Sync", new ByteArrayEntity(s.toByteArray()),
                     timeout);
-            int code = resp.getStatusLine().getStatusCode();
-            if (code == HttpStatus.SC_OK) {
-                // In EAS 12.1, we can get "empty" sync responses, which indicate that there are
-                // no changes in the mailbox; handle that case here
-                Header header = resp.getFirstHeader("content-length");
-                if (header != null && header.getValue().equals("0")) {
-                    // If this happens, exit cleanly, and change the interval from push to ping
-                    // if necessary
-                    userLog("Empty sync response; finishing");
-                    if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
-                        userLog("Changing mailbox from push to ping");
-                        ContentValues cv = new ContentValues();
-                        cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
-                        mContentResolver.update(
-                                ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId), cv,
-                                null, null);
-                    }
-                    if (mRequestQueue.isEmpty()) {
-                        mExitStatus = EXIT_DONE;
-                        return;
-                    } else {
-                        continue;
-                    }
-                }
-                InputStream is = resp.getEntity().getContent();
-                if (is != null) {
-                    moreAvailable = target.parse(is);
-                    if (target.isLooping()) {
-                        loopingCount++;
-                        userLog("** Looping: " + loopingCount);
-                        // After the maximum number of loops, we'll set moreAvailable to false and
-                        // allow the sync loop to terminate
-                        if (moreAvailable && (loopingCount > MAX_LOOPING_COUNT)) {
-                            userLog("** Looping force stopped");
-                            moreAvailable = false;
+            HttpEntity entity = resp.getEntity();
+            try {
+                int code = resp.getStatusLine().getStatusCode();
+                if (code == HttpStatus.SC_OK) {
+                    // In EAS 12.1, we can get "empty" sync responses, which indicate that there are
+                    // no changes in the mailbox; handle that case here
+                    Header header = resp.getFirstHeader("content-length");
+                    if (header != null && header.getValue().equals("0")) {
+                        // If this happens, exit cleanly, and change the interval from push to ping
+                        // if necessary
+                        userLog("Empty sync response; finishing");
+                        if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
+                            userLog("Changing mailbox from push to ping");
+                            ContentValues cv = new ContentValues();
+                            cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
+                            mContentResolver.update(
+                                    ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId),
+                                    cv, null, null);
                         }
-                    } else {
-                        loopingCount = 0;
+                        if (mRequestQueue.isEmpty()) {
+                            mExitStatus = EXIT_DONE;
+                            return;
+                        } else {
+                            continue;
+                        }
                     }
-                    target.cleanup();
+                    InputStream is = entity.getContent();
+                    if (is != null) {
+                        moreAvailable = target.parse(is);
+                        if (target.isLooping()) {
+                            loopingCount++;
+                            userLog("** Looping: " + loopingCount);
+                            // After the maximum number of loops, we'll set moreAvailable to false
+                            // and allow the sync loop to terminate
+                            if (moreAvailable && (loopingCount > MAX_LOOPING_COUNT)) {
+                                userLog("** Looping force stopped");
+                                moreAvailable = false;
+                            }
+                        } else {
+                            loopingCount = 0;
+                        }
+                        target.cleanup();
+                    } else {
+                        userLog("Empty input stream in sync command response");
+                    }
                 } else {
-                    userLog("Empty input stream in sync command response");
+                    userLog("Sync response error: ", code);
+                    if (isProvisionError(code)) {
+                        mExitStatus = EXIT_SECURITY_FAILURE;
+                    } else if (isAuthError(code)) {
+                        mExitStatus = EXIT_LOGIN_FAILURE;
+                    } else {
+                        mExitStatus = EXIT_IO_ERROR;
+                    }
+                    return;
                 }
-            } else {
-                userLog("Sync response error: ", code);
-                if (isProvisionError(code)) {
-                    mExitStatus = EXIT_SECURITY_FAILURE;
-                } else if (isAuthError(code)) {
-                    mExitStatus = EXIT_LOGIN_FAILURE;
-                } else {
-                    mExitStatus = EXIT_IO_ERROR;
+            } finally {
+                if (entity != null) {
+                    entity.consumeContent();
                 }
-                return;
             }
         }
         mExitStatus = EXIT_DONE;