EasOperation.java revision f8cccaecc8148d12d58ffcba5ce7366191316ac0
1ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu/*
2ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Copyright (C) 2013 The Android Open Source Project
3ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
4ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Licensed under the Apache License, Version 2.0 (the "License");
5ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * you may not use this file except in compliance with the License.
6ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * You may obtain a copy of the License at
7ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
8ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *      http://www.apache.org/licenses/LICENSE-2.0
9ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
10ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Unless required by applicable law or agreed to in writing, software
11ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * distributed under the License is distributed on an "AS IS" BASIS,
12ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * See the License for the specific language governing permissions and
14ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * limitations under the License.
15ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */
16ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
17ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hupackage com.android.exchange.eas;
18ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
19ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport android.content.ContentResolver;
20b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport android.content.ContentUris;
21b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport android.content.ContentValues;
22ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport android.content.Context;
23ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport android.content.SyncResult;
24b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport android.net.Uri;
25be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Huimport android.os.Build;
26ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport android.os.Bundle;
272e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdonimport android.telephony.TelephonyManager;
28a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Huimport android.text.TextUtils;
29ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport android.text.format.DateUtils;
30ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
31ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.emailcommon.provider.Account;
32ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.emailcommon.provider.EmailContent;
33ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.emailcommon.provider.HostAuth;
34ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.emailcommon.provider.Mailbox;
35b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport com.android.emailcommon.utility.Utility;
367f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Huimport com.android.exchange.CommandStatusException;
37ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.Eas;
38ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.EasResponse;
39ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.adapter.Serializer;
40be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Huimport com.android.exchange.adapter.Tags;
41ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.service.EasServerConnection;
426cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Huimport com.android.mail.providers.UIProvider;
43ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.mail.utils.LogUtils;
44275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdonimport com.google.common.annotations.VisibleForTesting;
45ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
46ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport org.apache.http.HttpEntity;
47b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport org.apache.http.client.methods.HttpUriRequest;
48ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport org.apache.http.entity.ByteArrayEntity;
49ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
50ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport java.io.IOException;
51d92a75c707461188e8743149476e8f49ef191b42Tony Mantlerimport java.security.cert.CertificateException;
524427badd747b7c172934014b9f95a1be1256f35aMartin Hibdonimport java.util.ArrayList;
53ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
54ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu/**
55ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Base class for all Exchange operations that use a POST to talk to the server.
56ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
57ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * The core of this class is {@link #performOperation}, which provides the skeleton of making
58ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * a request, handling common errors, and setting fields on the {@link SyncResult} if there is one.
59ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * This class abstracts the connection handling from its subclasses and callers.
60ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
616c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * {@link #performOperation} calls various abstract functions to create the request and parse the
626c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * response. For the most part subclasses can implement just these bits of functionality and rely
636c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * on {@link #performOperation} to do all the boilerplate etc.
646c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * There are also a set of functions that a subclass may override if it's substantially
666c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * different from the "normal" operation (e.g. autodiscover deviates from the standard URI since
676c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * it's not account-specific so it needs to override {@link #getRequestUri()}), but the default
686c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * implementations of these functions should suffice for most operations.
696c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Some subclasses may need to override {@link #performOperation} to add validation and results
716c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * processing around a call to super.performOperation. Subclasses should avoid doing too much more
726c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * than wrapping some handling around the chained call; if you find that's happening, it's likely
736c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * a sign that the base class needs to be enhanced.
746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
756c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * One notable reason this wrapping happens is for operations that need to return a result directly
766c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * to their callers (as opposed to simply writing the results to the provider, as is common with
776c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * sync operations). This happens for example in
786c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * {@link com.android.emailcommon.service.IEmailService} message handlers. In such cases, due to
796c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * how {@link com.android.exchange.service.EasService} uses this class, the subclass needs to
806c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * store the result as a member variable and then provide an accessor to read the result. Since
816c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * different operations have different results (or none at all), there is no function in the base
826c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * class for this.
836c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Note that it is not practical to avoid the race between when an operation loads its account data
856c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * and when it uses it, as that would require some form of locking in the provider. There are three
866c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * interesting situations where this might happen, and that this class must handle:
876c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
886c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 1) Deleted from provider: Any subsequent provider access should return an error. Operations
896c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    must detect this and terminate with an error.
906c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 2) Account sync settings change: Generally only affects Ping. We interrupt the operation and
916c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    load the new settings before proceeding.
926c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 3) Sync suspended due to hold: A special case of the previous, and affects all operations, but
936c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    fortunately doesn't need special handling here. Correct provider functionality must generate
946c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    write failures, so the handling for #1 should cover this case as well.
956c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
966c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * This class attempts to defer loading of account data as long as possible -- ideally we load
976c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * immediately before the network request -- but does not proactively check for changes after that.
986c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * This approach is a a practical balance between minimizing the race without adding too much
996c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * complexity beyond what's required.
100ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */
101ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hupublic abstract class EasOperation {
102f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon    public static final String LOG_TAG = LogUtils.TAG;
103ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
104ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /** The maximum number of server redirects we allow before returning failure. */
105ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private static final int MAX_REDIRECTS = 3;
106ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
107ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /** Message MIME type for EAS version 14 and later. */
108ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private static final String EAS_14_MIME_TYPE = "application/vnd.ms-sync.wbxml";
109ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
110b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee    /**
111b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * EasOperation error codes below.  All subclasses should try to create error codes
112b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * that do not overlap these codes or the codes of other subclasses. The error
113b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * code values for each subclass should start in a different 100 range (i.e. -100,
114b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * -200, etc...).
115b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     */
116b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee
11761ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    /** Minimum value for any non failure result. There may be multiple different non-failure
11861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon     * results, if so they should all be greater than or equal to this value. */
11961ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    public static final int RESULT_MIN_OK_RESULT = 0;
120328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the operation was cancelled via {@link #abort}. */
121328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_ABORT = -1;
122328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the operation was cancelled via {@link #restart}. */
123328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_RESTART = -2;
124328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the Exchange servers redirected too many times. */
125328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_TOO_MANY_REDIRECTS = -3;
126328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the request failed due to a network problem. */
12761ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    public static final int RESULT_NETWORK_PROBLEM = -4;
128328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating a 403 (forbidden) error. */
129328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_FORBIDDEN = -5;
130328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating an unresolved provisioning error. */
131328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_PROVISIONING_ERROR = -6;
132328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating an authentication problem. */
133328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_AUTHENTICATION_ERROR = -7;
134b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** Error code indicating the client is missing a certificate. */
135b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public static final int RESULT_CLIENT_CERTIFICATE_REQUIRED = -8;
136b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** Error code indicating we don't have a protocol version in common with the server. */
137b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public static final int RESULT_PROTOCOL_VERSION_UNSUPPORTED = -9;
13897a217631c5540bb7c40b3d184483cc35fe8d762Yu Ping Hu    /** Error code indicating a hard error when initializing the operation. */
13997a217631c5540bb7c40b3d184483cc35fe8d762Yu Ping Hu    public static final int RESULT_INITIALIZATION_FAILURE = -10;
1408c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    /** Error code indicating a hard data layer error. */
1418c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    public static final int RESULT_HARD_DATA_FAILURE = -11;
142c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    /** Error code indicating that this operation failed, but we should not abort the sync */
143c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    /** TODO: This is currently only used in EasOutboxSync, no other place handles it correctly */
144c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    public static final int RESULT_NON_FATAL_ERROR = -12;
145328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating some other failure. */
146c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    public static final int RESULT_OTHER_FAILURE = -99;
147c0efae08efe0b337aa406b50f9d2dbc6bbbd56fdYu Ping Hu    /** Constant to delimit where op specific error codes begin. */
148c0efae08efe0b337aa406b50f9d2dbc6bbbd56fdYu Ping Hu    public static final int RESULT_OP_SPECIFIC_ERROR_RESULT = -100;
149328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
150ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final Context mContext;
151328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
1526c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The provider id for the account this operation is on. */
1536c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private final long mAccountId;
1546c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1556c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The cached {@link Account} state; can be null if it hasn't been loaded yet. */
1566c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    protected Account mAccount;
1576c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1586c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The connection to use for this operation. This is created when {@link #mAccount} is set. */
1596c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private EasServerConnection mConnection;
1606c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
161c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    public class MessageInvalidException extends Exception {
162c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon        public MessageInvalidException(final String message) {
163c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon            super(message);
164c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon        }
165c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    }
166c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon
167275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon    @VisibleForTesting
168275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon    public void replaceEasServerConnection(EasServerConnection connection) {
169275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon        mConnection = connection;
170275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon    }
171275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon
17261ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    public static boolean isFatal(int result) {
17361ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon        return result < RESULT_MIN_OK_RESULT;
17461ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    }
17561ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon
176328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
1776c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Constructor which defers loading of account and connection info.
1786c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param context
1796c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param accountId
180328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
1816c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    protected EasOperation(final Context context, final long accountId) {
1826c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mContext = context;
1836c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccountId = accountId;
1846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
185ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
1866c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    protected EasOperation(final Context context, final Account account,
187b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            final EasServerConnection connection) {
1886c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        this(context, account.mId);
1896c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount = account;
190b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        mConnection = connection;
191b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
192b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
193ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected EasOperation(final Context context, final Account account, final HostAuth hostAuth) {
1946c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        this(context, account, new EasServerConnection(context, account, hostAuth));
195ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
196ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
197ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected EasOperation(final Context context, final Account account) {
1986c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        this(context, account, account.getOrCreateHostAuthRecv(context));
199ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
200ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
201b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
202b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * This constructor is for use by operations that are created by other operations, e.g.
2036c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * {@link EasProvision}. It reuses the account and connection of its parent.
204b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @param parentOperation The {@link EasOperation} that is creating us.
205b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
206b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected EasOperation(final EasOperation parentOperation) {
2076c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mContext = parentOperation.mContext;
2086c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccountId = parentOperation.mAccountId;
2096c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount = parentOperation.mAccount;
2106c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mConnection = parentOperation.mConnection;
2116c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
2126c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
2136c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
2146c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Some operations happen before the account exists (e.g. account validation).
2152ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon     * These operations cannot use {@link #init}, so instead we make a dummy account and
2166c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * supply a temporary {@link HostAuth}.
2176c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param hostAuth
2186c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
2196c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    protected final void setDummyAccount(final HostAuth hostAuth) {
2206c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount = new Account();
2216c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount.mEmailAddress = hostAuth.mLogin;
2226c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mConnection = new EasServerConnection(mContext, mAccount, hostAuth);
2236c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
2246c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
2256c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
2266c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Loads (or reloads) the {@link Account} for this operation, and sets up our connection to the
2272ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon     * server. This can be overridden to add additional functionality, but child implementations
2282ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon     * should always call super().
2296c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param allowReload If false, do not perform a load if we already have an {@link Account}
2306c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     *                    (i.e. just keep the existing one); otherwise allow replacement of the
2316c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     *                    account. Note that this can result in a valid Account being replaced with
2326c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     *                    null if the account no longer exists.
2336c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @return Whether we now have a valid {@link Account} object.
2346c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
2352ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon    public boolean init(final boolean allowReload) {
2366c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        if (mAccount == null || allowReload) {
2376c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mAccount = Account.restoreAccountWithId(mContext, getAccountId());
2386c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mAccount != null) {
2396c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                mConnection = new EasServerConnection(mContext, mAccount,
2406c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                        mAccount.getOrCreateHostAuthRecv(mContext));
2416c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
2426c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
2436c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        return (mAccount != null);
2446c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
2456c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
246275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon    /**
247275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon     * Sets the account. This is for use in cases where the account is not available upon
248275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon     * construction. This will also create the EasServerConnection.
249275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon     * @param account
250275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon     * @param hostAuth
251275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon     */
252275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon    protected void setAccount(final Account account, final HostAuth hostAuth) {
253275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon        mAccount = account;
254275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon        if (mAccount != null) {
255275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon            mConnection = new EasServerConnection(mContext, mAccount, hostAuth);
256275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon        }
257275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon    }
258275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon
2596c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    public final long getAccountId() {
2606c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        return mAccountId;
261328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
262ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
263c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    public final Account getAccount() {
264c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee        return mAccount;
265c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    }
266c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee
267ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
268ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Request that this operation terminate. Intended for use by the sync service to interrupt
269ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * running operations, primarily Ping.
270ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
271ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public final void abort() {
272ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        mConnection.stop(EasServerConnection.STOPPED_REASON_ABORT);
273ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
274ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
275ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
276ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Request that this operation restart. Intended for use by the sync service to interrupt
277ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * running operations, primarily Ping.
278ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
279ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public final void restart() {
280ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        mConnection.stop(EasServerConnection.STOPPED_REASON_RESTART);
281ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
282ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
283ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
284ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The skeleton of performing an operation. This function handles all the common code and
285ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * error handling, calling into virtual functions that are implemented or overridden by the
286ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * subclass to do the operation-specific logic.
287328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
288328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * The result codes work as follows:
289328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * - Negative values indicate common error codes and are defined above (the various RESULT_*
290328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *   constants).
291328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * - Non-negative values indicate the result of {@link #handleResponse}. These are obviously
292328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *   specific to the subclass, and may indicate success or error conditions.
293328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
294328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * The common error codes primarily indicate conditions that occur when performing the POST
295328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * itself, such as network errors and handling of the HTTP response. However, some errors that
296328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * can be indicated in the HTTP response code can also be indicated in the payload of the
297328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * response as well, so {@link #handleResponse} should in those cases return the appropriate
298328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * negative result code, which will be handled the same as if it had been indicated in the HTTP
299328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * response code.
300328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
301328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return A result code for the outcome of this operation, as described above.
302ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
3038c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    public int performOperation() {
3046c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        // Make sure the account is loaded if it hasn't already been.
3052ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon        if (!init(false)) {
30697a217631c5540bb7c40b3d184483cc35fe8d762Yu Ping Hu            LogUtils.i(LOG_TAG, "Failed to initialize %d before sending request for operation %s",
3076c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    getAccountId(), getCommand());
30897a217631c5540bb7c40b3d184483cc35fe8d762Yu Ping Hu            return RESULT_INITIALIZATION_FAILURE;
3096c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
3106c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
311ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // We handle server redirects by looping, but we need to protect against too much looping.
312ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        int redirectCount = 0;
313ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
314ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        do {
315b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            // Perform the HTTP request and handle exceptions.
316ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            final EasResponse response;
317ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            try {
31861cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                try {
31961cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    response = mConnection.executeHttpUriRequest(makeRequest(), getTimeout());
32061cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                } finally {
32161cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    onRequestMade();
32261cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                }
323ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } catch (final IOException e) {
324ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // If we were stopped, return the appropriate result code.
325ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                switch (mConnection.getStoppedReason()) {
326ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    case EasServerConnection.STOPPED_REASON_ABORT:
327ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        return RESULT_ABORT;
328ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    case EasServerConnection.STOPPED_REASON_RESTART:
329ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        return RESULT_RESTART;
330ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    default:
331ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        break;
332ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
333ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // If we're here, then we had a IOException that's not from a stop request.
334dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                String message = e.getMessage();
335dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                if (message == null) {
336dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                    message = "(no message)";
337dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                }
338dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                LogUtils.i(LOG_TAG, "IOException while sending request: %s", message);
33961ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                return RESULT_NETWORK_PROBLEM;
340d92a75c707461188e8743149476e8f49ef191b42Tony Mantler            } catch (final CertificateException e) {
341d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                LogUtils.i(LOG_TAG, "CertificateException while sending request: %s",
342d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                        e.getMessage());
343d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                return RESULT_CLIENT_CERTIFICATE_REQUIRED;
344c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon            } catch (final MessageInvalidException e) {
34561ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                // This indicates that there is something wrong with the message locally, and it
34661ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                // cannot be sent. We don't want to return success, because that's misleading,
34761ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                // but on the other hand, we don't want to abort the sync, because that would
34861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                // prevent other messages from being sent.
349c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon                LogUtils.d(LOG_TAG, "Exception sending request %s", e.getMessage());
350c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon                return RESULT_NON_FATAL_ERROR;
351ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } catch (final IllegalStateException e) {
352ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // Subclasses use ISE to signal a hard error when building the request.
3536f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu                // TODO: Switch away from ISEs.
354ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                LogUtils.e(LOG_TAG, e, "Exception while sending request");
3558c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                return RESULT_HARD_DATA_FAILURE;
356ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            }
357ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
358ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            // The POST completed, so process the response.
359ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            try {
360328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                final int result;
361ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // First off, the success case.
362ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                if (response.isSuccess()) {
3637f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    int responseResult;
364ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    try {
3658c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                        responseResult = handleResponse(response);
366ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    } catch (final IOException e) {
367ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        LogUtils.e(LOG_TAG, e, "Exception while handling response");
36861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                        return RESULT_NETWORK_PROBLEM;
3697f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    } catch (final CommandStatusException e) {
3707f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // For some operations (notably Sync & FolderSync), errors are signaled in
3717f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // the payload of the response. These will have a HTTP 200 response, and the
3727f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // error condition is only detected during response parsing.
3737f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // The various parsers handle this by throwing a CommandStatusException.
3747f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // TODO: Consider having the parsers return the errors instead of throwing.
3757f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        final int status = e.mStatus;
3767f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        LogUtils.e(LOG_TAG, "CommandStatusException: %s, %d", getCommand(), status);
3777f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        if (CommandStatusException.CommandStatus.isNeedsProvisioning(status)) {
3787f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_PROVISIONING_ERROR;
3797f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        } else if (CommandStatusException.CommandStatus.isDeniedAccess(status)) {
3807f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_FORBIDDEN;
3817f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        } else {
3827f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_OTHER_FAILURE;
3837f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        }
384ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    }
3857f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    result = responseResult;
386328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                } else {
38761cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    result = handleHttpError(response.getStatus());
388ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
389ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
3907f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                // Non-negative results indicate success. Return immediately and bypass the error
3917f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                // handling.
39261ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                if (result >= EasOperation.RESULT_MIN_OK_RESULT) {
3937f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    return result;
3947f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                }
3957f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu
396328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // If this operation has distinct handling for 403 errors, do that.
397328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (result == RESULT_FORBIDDEN || (response.isForbidden() && handleForbidden())) {
398ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    LogUtils.e(LOG_TAG, "Forbidden response");
399328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_FORBIDDEN;
400328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                }
401328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
402328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle provisioning errors.
403328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (result == RESULT_PROVISIONING_ERROR || response.isProvisionError()) {
4048c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                    if (handleProvisionError()) {
405328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        // The provisioning error has been taken care of, so we should re-do this
406328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        // request.
4077f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        LogUtils.d(LOG_TAG, "Provisioning error handled during %s, retrying",
4087f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                                getCommand());
409328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        continue;
410328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    }
411328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_PROVISIONING_ERROR;
412328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                }
413328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
414328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle authentication errors.
415328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (response.isAuthError()) {
416ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    LogUtils.e(LOG_TAG, "Authentication error");
417b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    if (response.isMissingCertificate()) {
418b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        return RESULT_CLIENT_CERTIFICATE_REQUIRED;
419b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    }
420328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_AUTHENTICATION_ERROR;
421ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
422ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
423328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle redirects.
424328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (response.isRedirectError()) {
425328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    ++redirectCount;
426328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    mConnection.redirectHostAuth(response.getRedirectAddress());
427328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    // Note that unlike other errors, we do NOT return here; we just keep looping.
428328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                } else {
429328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    // All other errors.
4300c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu                    LogUtils.e(LOG_TAG, "Generic error for operation %s: status %d, result %d",
4310c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu                            getCommand(), response.getStatus(), result);
432c0efae08efe0b337aa406b50f9d2dbc6bbbd56fdYu Ping Hu                    // TODO: This probably should return result.
433ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    return RESULT_OTHER_FAILURE;
434ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
435ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } finally {
436ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                response.close();
437ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            }
438ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        } while (redirectCount < MAX_REDIRECTS);
439ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
440328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        // Non-redirects return immediately after handling, so the only way to reach here is if we
441ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // looped too many times.
442ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        LogUtils.e(LOG_TAG, "Too many redirects");
443ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return RESULT_TOO_MANY_REDIRECTS;
444ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
445ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
44661cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    protected void onRequestMade() {
44761cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // This can be overridden to do any cleanup that must happen after the request has
44861cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // been sent. It will always be called, regardless of the status of the request.
44961cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    }
45061cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon
45161cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    protected int handleHttpError(final int httpStatus) {
45261cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // This function can be overriden if the child class needs to change the result code
45361cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // based on the http response status.
45461cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        return RESULT_OTHER_FAILURE;
45561cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    }
45661cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon
457ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
458b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Reset the protocol version to use for this connection. If it's changed, and our account is
45964fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * persisted, also write back the changes to the DB. Note that this function is called at
46064fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * the time of Account creation but does not update the Account object with the various flags
46164fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * at that point in time.
46264fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * TODO: Make sure that the Account flags are set properly in this function or a similar
46364fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * function in the future. Right now the Account setup activity sets the flags, this is not
46464fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * the right design.
465b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @param protocolVersion The new protocol version to use, as a string.
466ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
467b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final void setProtocolVersion(final String protocolVersion) {
4686c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        final long accountId = getAccountId();
4696c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        if (mConnection.setProtocolVersion(protocolVersion) && accountId != Account.NOT_SAVED) {
4706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            final Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
471b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            final ContentValues cv = new ContentValues(2);
472b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            if (getProtocolVersion() >= 12.0) {
473b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                final int oldFlags = Utility.getFirstRowInt(mContext, uri,
474b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        Account.ACCOUNT_FLAGS_PROJECTION, null, null, null,
475b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        Account.ACCOUNT_FLAGS_COLUMN_FLAGS, 0);
47664fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee                final int newFlags = oldFlags |
47764fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee                        Account.FLAGS_SUPPORTS_GLOBAL_SEARCH | Account.FLAGS_SUPPORTS_SEARCH |
47864fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee                                Account.FLAGS_SUPPORTS_SMART_FORWARD;
479b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                if (oldFlags != newFlags) {
480b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    cv.put(EmailContent.AccountColumns.FLAGS, newFlags);
481b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                }
482b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            }
483b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            cv.put(EmailContent.AccountColumns.PROTOCOL_VERSION, protocolVersion);
484b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            mContext.getContentResolver().update(uri, cv, null, null);
485b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        }
486b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
487ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
488b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
489b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Create the request object for this operation.
490b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Most operations use a POST, but some use other request types (e.g. Options).
491b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return An {@link HttpUriRequest}.
492b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @throws IOException
493b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
494c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    private final HttpUriRequest makeRequest() throws IOException, MessageInvalidException {
495b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final String requestUri = getRequestUri();
496b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        if (requestUri == null) {
497b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            return mConnection.makeOptions();
498b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        }
499275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon
500275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon        HttpUriRequest req = mConnection.makePost(requestUri, getRequestEntity(),
501b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                getRequestContentType(), addPolicyKeyHeaderToRequest());
502275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon        return req;
503ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
504ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
505ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
506ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The following functions MUST be overridden by subclasses; these are things that are unique
507ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * to each operation.
508ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
509ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
510ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
511ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Get the name of the operation, used as the "Cmd=XXX" query param in the request URI. Note
5120c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * that if you override {@link #getRequestUri}, then this function may be unused for normal
5130c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * operation, but all subclasses should return something non-null for use with logging.
5140c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * @return The name of the command for this operation as defined by the EAS protocol, or for
5150c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     *         commands that don't need it, a suitable descriptive name for logging.
516ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
517ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected abstract String getCommand();
518ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
519ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
520b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Build the {@link HttpEntity} which is used to construct the POST. Typically this function
521ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * will build the Exchange request using a {@link Serializer} and then call {@link #makeEntity}.
522b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * If the subclass is not using a POST, then it should override this to return null.
523275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon     * @return The {@link HttpEntity} to pass to {@link com.android.exchange.service.EasServerConnection#makePost}.
524ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @throws IOException
525ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
526c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    protected abstract HttpEntity getRequestEntity() throws IOException, MessageInvalidException;
527ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
528ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
529ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Parse the response from the Exchange perform whatever actions are dictated by that.
530ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param response The {@link EasResponse} to our request.
531328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return A result code. Non-negative values are returned directly to the caller; negative
532328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *         values
533328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
534328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * that is returned to the caller of {@link #performOperation}.
535ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @throws IOException
536ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
5378c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    protected abstract int handleResponse(final EasResponse response)
5387f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu            throws IOException, CommandStatusException;
539ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
540ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
541ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The following functions may be overriden by a subclass, but most operations will not need
542ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * to do so.
543ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
544ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
545ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
546ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Get the URI for the Exchange server and this operation. Most (signed in) operations need
547ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * not override this; the notable operation that needs to override it is auto-discover.
548ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return
549ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
550ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected String getRequestUri() {
551ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return mConnection.makeUriString(getCommand());
552ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
553ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
554ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
555ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return Whether to set the X-MS-PolicyKey header. Only Ping does not want this header.
556ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
557ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected boolean addPolicyKeyHeaderToRequest() {
558ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return true;
559ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
560ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
561ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
562ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The content type of this request.
563ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
564ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected String getRequestContentType() {
565ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return EAS_14_MIME_TYPE;
566ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
567ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
568ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
569ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The timeout to use for the POST.
570ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
571ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected long getTimeout() {
572ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return 30 * DateUtils.SECOND_IN_MILLIS;
573ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
574ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
575ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
576ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * If 403 responses should be handled in a special way, this function should be overridden to
577ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * do that.
578ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return Whether we handle 403 responses; if false, then treat 403 as a provisioning error.
579ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
580ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected boolean handleForbidden() {
581ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return false;
582ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
583ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
584ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
585328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * Handle a provisioning error. Subclasses may override this to do something different, e.g.
586328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * to validate rather than actually do the provisioning.
587328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return
588328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
5898c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    protected boolean handleProvisionError() {
590b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final EasProvision provisionOperation = new EasProvision(this);
5918c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu        return provisionOperation.provision();
592328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
593328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
594328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
595328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * Convenience methods for subclasses to use.
596328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
597328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
598328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
599ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Convenience method to make an {@link HttpEntity} from {@link Serializer}.
600ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
601ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final HttpEntity makeEntity(final Serializer s) {
602ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return new ByteArrayEntity(s.toByteArray());
603ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
604ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
605328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
606b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Check whether we should ask the server what protocol versions it supports and set this
607b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * account to use that version.
608b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether we need a new protocol version from the server.
609b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
610b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final boolean shouldGetProtocolVersion() {
611b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        // TODO: Find conditions under which we should check other than not having one yet.
612b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return !mConnection.isProtocolVersionSet();
613b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
614b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
615b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
616328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return The protocol version to use.
617328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
618ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final double getProtocolVersion() {
619ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return mConnection.getProtocolVersion();
620ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
621ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
622ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
623328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return Our useragent.
624328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
625328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    protected final String getUserAgent() {
626328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        return mConnection.getUserAgent();
627328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
628328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
629328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
630b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether we succeeeded in registering the client cert.
631b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
632b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final boolean registerClientCert() {
633b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return mConnection.registerClientCert();
634b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
635b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
636b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
637be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     * Add the device information to the current request.
638be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     * @param s The {@link Serializer} for our current request.
639a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param context The {@link Context} for current device.
640a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param userAgent The user agent string that our connection use.
641be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     */
642a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    protected static void expandedAddDeviceInformationToSerializer(final Serializer s,
643a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            final Context context, final String userAgent) throws IOException {
6442e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String deviceId;
6452e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String phoneNumber;
6462e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String operator;
647a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final TelephonyManager tm = (TelephonyManager)context.getSystemService(
648a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee                Context.TELEPHONY_SERVICE);
6492e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (tm != null) {
6502e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            deviceId = tm.getDeviceId();
6512e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            phoneNumber = tm.getLine1Number();
652a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // TODO: This is not perfect and needs to be improved, for at least two reasons:
653a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // 1) SIM cards can override this name.
654a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // 2) We don't resend this info to the server when we change networks.
655a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            final String operatorName = tm.getNetworkOperatorName();
656a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            final String operatorNumber = tm.getNetworkOperator();
657a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            if (!TextUtils.isEmpty(operatorName) && !TextUtils.isEmpty(operatorNumber)) {
658a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorName + " (" + operatorNumber + ")";
659a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            } else if (!TextUtils.isEmpty(operatorName)) {
660a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorName;
661a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            } else {
662a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorNumber;
663a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            }
6642e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        } else {
6652e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            deviceId = null;
6662e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            phoneNumber = null;
6672e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            operator = null;
6682e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
6692e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon
6702e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // TODO: Right now, we won't send this information unless the device is provisioned again.
6712e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // Potentially, this means that our phone number could be out of date if the user
6722e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // switches sims. Is there something we can do to force a reprovision?
673be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.start(Tags.SETTINGS_DEVICE_INFORMATION).start(Tags.SETTINGS_SET);
674be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.data(Tags.SETTINGS_MODEL, Build.MODEL);
6752e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (deviceId != null) {
6762e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_IMEI, tm.getDeviceId());
6772e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
67870755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        // Set the device friendly name, if we have one.
67970755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        // TODO: Longer term, this should be done without a provider call.
680a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final Bundle deviceName = context.getContentResolver().call(
68170755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu                EmailContent.CONTENT_URI, EmailContent.DEVICE_FRIENDLY_NAME, null, null);
682a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        if (deviceName != null) {
683a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            final String friendlyName = deviceName.getString(EmailContent.DEVICE_FRIENDLY_NAME);
68470755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu            if (!TextUtils.isEmpty(friendlyName)) {
68570755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu                s.data(Tags.SETTINGS_FRIENDLY_NAME, friendlyName);
68670755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu            }
68770755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        }
688be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.data(Tags.SETTINGS_OS, "Android " + Build.VERSION.RELEASE);
6892e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (phoneNumber != null) {
6902e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_PHONE_NUMBER, phoneNumber);
6912e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
6922e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // TODO: Consider setting this, but make sure we know what it's used for.
6932e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // If the user changes the device's locale and we don't do a reprovision, the server's
6942e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // idea of the language will be wrong. Since we're not sure what this is used for,
6952e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // right now we're leaving it out.
6962e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        //s.data(Tags.SETTINGS_OS_LANGUAGE, Locale.getDefault().getDisplayLanguage());
697a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        s.data(Tags.SETTINGS_USER_AGENT, userAgent);
6982e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (operator != null) {
6992e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_MOBILE_OPERATOR, operator);
7002e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
701be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.end().end();  // SETTINGS_SET, SETTINGS_DEVICE_INFORMATION
702be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu    }
703be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu
704be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu    /**
705a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * Add the device information to the current request.
706a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param s The {@link Serializer} that contains the payload for this request.
707a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     */
708a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    protected final void addDeviceInformationToSerializer(final Serializer s)
709a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            throws IOException {
710a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final String userAgent = getUserAgent();
711a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        expandedAddDeviceInformationToSerializer(s, mContext, userAgent);
712a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    }
713a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee
714a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    /**
715ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Convenience method for adding a Message to an account's outbox
716ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param account The {@link Account} from which to send the message.
717ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param msg the message to send
718ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
719328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    protected final void sendMessage(final Account account, final EmailContent.Message msg) {
720ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        long mailboxId = Mailbox.findMailboxOfType(mContext, account.mId, Mailbox.TYPE_OUTBOX);
721ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // TODO: Improve system mailbox handling.
722ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (mailboxId == Mailbox.NO_MAILBOX) {
723ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            LogUtils.d(LOG_TAG, "No outbox for account %d, creating it", account.mId);
724ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            final Mailbox outbox =
725ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    Mailbox.newSystemMailbox(mContext, account.mId, Mailbox.TYPE_OUTBOX);
726ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            outbox.save(mContext);
727ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            mailboxId = outbox.mId;
728ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        }
729ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.mMailboxKey = mailboxId;
730ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.mAccountKey = account.mId;
731ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.save(mContext);
732ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        requestSyncForMailbox(new android.accounts.Account(account.mEmailAddress,
7334427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mailboxId);
734ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
735ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
736ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
737ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Issue a {@link android.content.ContentResolver#requestSync} for a specific mailbox.
738ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param amAccount The {@link android.accounts.Account} for the account we're pinging.
739ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param mailboxId The id of the mailbox that needs to sync.
740ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
741ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected static void requestSyncForMailbox(final android.accounts.Account amAccount,
7424427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon            final long mailboxId) {
7434427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        final Bundle extras = Mailbox.createSyncBundle(mailboxId);
7444427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras);
7454427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        LogUtils.i(LOG_TAG, "requestSync EasOperation requestSyncForMailbox %s, %s",
7464427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon                amAccount.toString(), extras.toString());
7474427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    }
7484427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon
7494427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    protected static void requestSyncForMailboxes(final android.accounts.Account amAccount,
750f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon            final String authority, final ArrayList<Long> mailboxIds) {
7514427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        final Bundle extras = Mailbox.createSyncBundle(mailboxIds);
7525b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee        /**
7535b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee         * Please note that it is very possible that we are trying to send a request to the
7545b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee         * email sync adapter even though email push is turned off (i.e. this account might only
7555b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee         * be syncing calendar or contacts). In this situation we need to make sure that
7565b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee         * this request is marked as manual as to ensure that the sync manager does not drop it
7575b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee         * on the floor. Right now, this function is only called by EasPing, if it is every called
7585b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee         * by another caller, then we should reconsider if manual=true is the right thing to do.
7595b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee         */
7605b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
761ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        ContentResolver.requestSync(amAccount, authority, extras);
762f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon        LogUtils.i(LOG_TAG, "EasOperation requestSyncForMailboxes  %s, %s",
7639c7165d4c6b90101b781f90b17451efd42a17929Martin Hibdon                amAccount.toString(), extras.toString());
764ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
7658c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu
7666cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu    public static int translateSyncResultToUiResult(final int result) {
7676cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu        switch (result) {
7686cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu              case RESULT_TOO_MANY_REDIRECTS:
7696cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.INTERNAL_ERROR;
77061ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon            case RESULT_NETWORK_PROBLEM:
7716cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.CONNECTION_ERROR;
7726cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_FORBIDDEN:
7736cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_PROVISIONING_ERROR:
7746cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_AUTHENTICATION_ERROR:
7756cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_CLIENT_CERTIFICATE_REQUIRED:
7766cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.AUTH_ERROR;
7776cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_PROTOCOL_VERSION_UNSUPPORTED:
7786cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                // Only used in validate, so there's never a syncResult to write to here.
7796cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                break;
7806cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_INITIALIZATION_FAILURE:
7816cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_HARD_DATA_FAILURE:
7826cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.INTERNAL_ERROR;
7836cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_OTHER_FAILURE:
7846cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.INTERNAL_ERROR;
7856cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu        }
7866cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu        return UIProvider.LastSyncResult.SUCCESS;
7876cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu    }
788ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu}
789