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;
27ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdonimport android.support.annotation.NonNull;
282e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdonimport android.telephony.TelephonyManager;
29a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Huimport android.text.TextUtils;
30ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport android.text.format.DateUtils;
31ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
32ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.emailcommon.provider.Account;
33ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.emailcommon.provider.EmailContent;
34ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.emailcommon.provider.HostAuth;
35ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.emailcommon.provider.Mailbox;
36b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport com.android.emailcommon.utility.Utility;
377f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Huimport com.android.exchange.CommandStatusException;
38ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.Eas;
39ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.EasResponse;
40ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.adapter.Serializer;
41be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Huimport com.android.exchange.adapter.Tags;
42ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.service.EasServerConnection;
436cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Huimport com.android.mail.providers.UIProvider;
44ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.mail.utils.LogUtils;
45275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdonimport com.google.common.annotations.VisibleForTesting;
46ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
47ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport org.apache.http.HttpEntity;
48b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport org.apache.http.client.methods.HttpUriRequest;
49ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport org.apache.http.entity.ByteArrayEntity;
50ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
51ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport java.io.IOException;
52d92a75c707461188e8743149476e8f49ef191b42Tony Mantlerimport java.security.cert.CertificateException;
534427badd747b7c172934014b9f95a1be1256f35aMartin Hibdonimport java.util.ArrayList;
54ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
55ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu/**
56ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Base class for all Exchange operations that use a POST to talk to the server.
57ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
58ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * The core of this class is {@link #performOperation}, which provides the skeleton of making
59ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * a request, handling common errors, and setting fields on the {@link SyncResult} if there is one.
60ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * This class abstracts the connection handling from its subclasses and callers.
61ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
626c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * {@link #performOperation} calls various abstract functions to create the request and parse the
636c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * response. For the most part subclasses can implement just these bits of functionality and rely
646c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * on {@link #performOperation} to do all the boilerplate etc.
656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
666c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * There are also a set of functions that a subclass may override if it's substantially
676c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * different from the "normal" operation (e.g. autodiscover deviates from the standard URI since
686c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * it's not account-specific so it needs to override {@link #getRequestUri()}), but the default
696c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * implementations of these functions should suffice for most operations.
706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
716c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Some subclasses may need to override {@link #performOperation} to add validation and results
726c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * processing around a call to super.performOperation. Subclasses should avoid doing too much more
736c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * than wrapping some handling around the chained call; if you find that's happening, it's likely
746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * a sign that the base class needs to be enhanced.
756c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
766c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * One notable reason this wrapping happens is for operations that need to return a result directly
776c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * to their callers (as opposed to simply writing the results to the provider, as is common with
786c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * sync operations). This happens for example in
796c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * {@link com.android.emailcommon.service.IEmailService} message handlers. In such cases, due to
806c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * how {@link com.android.exchange.service.EasService} uses this class, the subclass needs to
816c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * store the result as a member variable and then provide an accessor to read the result. Since
826c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * different operations have different results (or none at all), there is no function in the base
836c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * class for this.
846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
856c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Note that it is not practical to avoid the race between when an operation loads its account data
866c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * and when it uses it, as that would require some form of locking in the provider. There are three
876c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * interesting situations where this might happen, and that this class must handle:
886c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
896c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 1) Deleted from provider: Any subsequent provider access should return an error. Operations
906c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    must detect this and terminate with an error.
916c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 2) Account sync settings change: Generally only affects Ping. We interrupt the operation and
926c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    load the new settings before proceeding.
936c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 3) Sync suspended due to hold: A special case of the previous, and affects all operations, but
946c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    fortunately doesn't need special handling here. Correct provider functionality must generate
956c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    write failures, so the handling for #1 should cover this case as well.
966c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
976c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * This class attempts to defer loading of account data as long as possible -- ideally we load
986c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * immediately before the network request -- but does not proactively check for changes after that.
996c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * This approach is a a practical balance between minimizing the race without adding too much
1006c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * complexity beyond what's required.
101ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */
102ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hupublic abstract class EasOperation {
103f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon    public static final String LOG_TAG = LogUtils.TAG;
104ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
105ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /** The maximum number of server redirects we allow before returning failure. */
106ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private static final int MAX_REDIRECTS = 3;
107ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
108ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /** Message MIME type for EAS version 14 and later. */
109ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private static final String EAS_14_MIME_TYPE = "application/vnd.ms-sync.wbxml";
110ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
111b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee    /**
112b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * EasOperation error codes below.  All subclasses should try to create error codes
113b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * that do not overlap these codes or the codes of other subclasses. The error
114b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * code values for each subclass should start in a different 100 range (i.e. -100,
115b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * -200, etc...).
116b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     */
117b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee
11861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    /** Minimum value for any non failure result. There may be multiple different non-failure
11961ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon     * results, if so they should all be greater than or equal to this value. */
12061ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    public static final int RESULT_MIN_OK_RESULT = 0;
121328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the operation was cancelled via {@link #abort}. */
122328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_ABORT = -1;
123328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the operation was cancelled via {@link #restart}. */
124328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_RESTART = -2;
125328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the Exchange servers redirected too many times. */
126328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_TOO_MANY_REDIRECTS = -3;
127328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the request failed due to a network problem. */
12861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    public static final int RESULT_NETWORK_PROBLEM = -4;
129328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating a 403 (forbidden) error. */
130328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_FORBIDDEN = -5;
131328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating an unresolved provisioning error. */
132328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_PROVISIONING_ERROR = -6;
133328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating an authentication problem. */
134328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_AUTHENTICATION_ERROR = -7;
135b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** Error code indicating the client is missing a certificate. */
136b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public static final int RESULT_CLIENT_CERTIFICATE_REQUIRED = -8;
137b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** Error code indicating we don't have a protocol version in common with the server. */
138b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public static final int RESULT_PROTOCOL_VERSION_UNSUPPORTED = -9;
13997a217631c5540bb7c40b3d184483cc35fe8d762Yu Ping Hu    /** Error code indicating a hard error when initializing the operation. */
14097a217631c5540bb7c40b3d184483cc35fe8d762Yu Ping Hu    public static final int RESULT_INITIALIZATION_FAILURE = -10;
1418c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    /** Error code indicating a hard data layer error. */
1428c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    public static final int RESULT_HARD_DATA_FAILURE = -11;
143c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    /** Error code indicating that this operation failed, but we should not abort the sync */
144c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    /** TODO: This is currently only used in EasOutboxSync, no other place handles it correctly */
145c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    public static final int RESULT_NON_FATAL_ERROR = -12;
146328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating some other failure. */
147c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    public static final int RESULT_OTHER_FAILURE = -99;
148c0efae08efe0b337aa406b50f9d2dbc6bbbd56fdYu Ping Hu    /** Constant to delimit where op specific error codes begin. */
149c0efae08efe0b337aa406b50f9d2dbc6bbbd56fdYu Ping Hu    public static final int RESULT_OP_SPECIFIC_ERROR_RESULT = -100;
150328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
151ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final Context mContext;
152328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
1536c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The cached {@link Account} state; can be null if it hasn't been loaded yet. */
154ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon    protected final Account mAccount;
1556c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1566c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The connection to use for this operation. This is created when {@link #mAccount} is set. */
15703735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdon    protected EasServerConnection mConnection;
1586c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
159c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    public class MessageInvalidException extends Exception {
160c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon        public MessageInvalidException(final String message) {
161c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon            super(message);
162c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon        }
163c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    }
164c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon
16561ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    public static boolean isFatal(int result) {
16661ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon        return result < RESULT_MIN_OK_RESULT;
16761ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon    }
16861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon
169ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon    protected EasOperation(final Context context, @NonNull final Account account,
170b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            final EasServerConnection connection) {
171ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon        mContext = context;
1726c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount = account;
173b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        mConnection = connection;
174ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon        if (account == null) {
175ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon            throw new IllegalStateException("Null account in EasOperation");
176ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon        }
177b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
178b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
179ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected EasOperation(final Context context, final Account account, final HostAuth hostAuth) {
1806c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        this(context, account, new EasServerConnection(context, account, hostAuth));
181ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
182ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
183ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected EasOperation(final Context context, final Account account) {
1846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        this(context, account, account.getOrCreateHostAuthRecv(context));
185ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
186ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
187b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
188b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * This constructor is for use by operations that are created by other operations, e.g.
1896c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * {@link EasProvision}. It reuses the account and connection of its parent.
190b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @param parentOperation The {@link EasOperation} that is creating us.
191b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
192b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected EasOperation(final EasOperation parentOperation) {
1936c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mContext = parentOperation.mContext;
1946c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount = parentOperation.mAccount;
1956c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mConnection = parentOperation.mConnection;
1966c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
1976c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1986c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
199ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon     * This will always be called at the begining of performOperation and can be overridden
200ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon     * to do whatever setup is needed.
201ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon     * @return true if initialization succeeded, false otherwise.
2026c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
203ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon    public boolean init() {
204ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon        return true;
205275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon    }
206275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon
2076c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    public final long getAccountId() {
208ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon        return mAccount.getId();
209328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
210ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
211c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    public final Account getAccount() {
212c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee        return mAccount;
213c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    }
214c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee
215ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
216ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Request that this operation terminate. Intended for use by the sync service to interrupt
217ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * running operations, primarily Ping.
218ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
219ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public final void abort() {
220ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        mConnection.stop(EasServerConnection.STOPPED_REASON_ABORT);
221ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
222ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
223ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
224ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Request that this operation restart. Intended for use by the sync service to interrupt
225ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * running operations, primarily Ping.
226ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
227ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public final void restart() {
228ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        mConnection.stop(EasServerConnection.STOPPED_REASON_RESTART);
229ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
230ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
231ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
232451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon     * Should return true if the last operation encountered an error. Default implementation
233451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon     * always returns false, child classes can override.
234451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon     */
235451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    public final boolean lastSyncHadError() { return false; }
236451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon
237451160bfda22734f7b33106edfa4a6ce19b62121Martin Hibdon    /**
238ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The skeleton of performing an operation. This function handles all the common code and
239ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * error handling, calling into virtual functions that are implemented or overridden by the
240ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * subclass to do the operation-specific logic.
241328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
242328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * The result codes work as follows:
243328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * - Negative values indicate common error codes and are defined above (the various RESULT_*
244328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *   constants).
245328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * - Non-negative values indicate the result of {@link #handleResponse}. These are obviously
246328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *   specific to the subclass, and may indicate success or error conditions.
247328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
248328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * The common error codes primarily indicate conditions that occur when performing the POST
249328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * itself, such as network errors and handling of the HTTP response. However, some errors that
250328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * can be indicated in the HTTP response code can also be indicated in the payload of the
251328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * response as well, so {@link #handleResponse} should in those cases return the appropriate
252328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * negative result code, which will be handled the same as if it had been indicated in the HTTP
253328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * response code.
254328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
255328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return A result code for the outcome of this operation, as described above.
256ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
2578c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    public int performOperation() {
258ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon        if (!init()) {
25997a217631c5540bb7c40b3d184483cc35fe8d762Yu Ping Hu            LogUtils.i(LOG_TAG, "Failed to initialize %d before sending request for operation %s",
2606c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    getAccountId(), getCommand());
26197a217631c5540bb7c40b3d184483cc35fe8d762Yu Ping Hu            return RESULT_INITIALIZATION_FAILURE;
2626c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
263303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon        try {
264303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon            return performOperationInternal();
265303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon        } finally {
266303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon            onRequestComplete();
267303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon        }
268303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon    }
269303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon
270303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon    private int performOperationInternal() {
271ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // We handle server redirects by looping, but we need to protect against too much looping.
272ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        int redirectCount = 0;
273ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
274ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        do {
275b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            // Perform the HTTP request and handle exceptions.
276ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            final EasResponse response;
277ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            try {
27861cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                try {
27961cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    response = mConnection.executeHttpUriRequest(makeRequest(), getTimeout());
28061cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                } finally {
28161cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    onRequestMade();
28261cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                }
283ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } catch (final IOException e) {
284ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // If we were stopped, return the appropriate result code.
285ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                switch (mConnection.getStoppedReason()) {
286ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    case EasServerConnection.STOPPED_REASON_ABORT:
287ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        return RESULT_ABORT;
288ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    case EasServerConnection.STOPPED_REASON_RESTART:
289ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        return RESULT_RESTART;
290ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    default:
291ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        break;
292ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
293ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // If we're here, then we had a IOException that's not from a stop request.
294dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                String message = e.getMessage();
295dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                if (message == null) {
296dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                    message = "(no message)";
297dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                }
298dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                LogUtils.i(LOG_TAG, "IOException while sending request: %s", message);
29961ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                return RESULT_NETWORK_PROBLEM;
300d92a75c707461188e8743149476e8f49ef191b42Tony Mantler            } catch (final CertificateException e) {
301d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                LogUtils.i(LOG_TAG, "CertificateException while sending request: %s",
302d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                        e.getMessage());
303d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                return RESULT_CLIENT_CERTIFICATE_REQUIRED;
304c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon            } catch (final MessageInvalidException e) {
30561ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                // This indicates that there is something wrong with the message locally, and it
30661ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                // cannot be sent. We don't want to return success, because that's misleading,
30761ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                // but on the other hand, we don't want to abort the sync, because that would
30861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                // prevent other messages from being sent.
309c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon                LogUtils.d(LOG_TAG, "Exception sending request %s", e.getMessage());
310c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon                return RESULT_NON_FATAL_ERROR;
311ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } catch (final IllegalStateException e) {
312ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // Subclasses use ISE to signal a hard error when building the request.
3136f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu                // TODO: Switch away from ISEs.
314ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                LogUtils.e(LOG_TAG, e, "Exception while sending request");
3158c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                return RESULT_HARD_DATA_FAILURE;
316ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            }
317ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
318ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            // The POST completed, so process the response.
319ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            try {
320328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                final int result;
321ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // First off, the success case.
322ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                if (response.isSuccess()) {
3237f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    int responseResult;
324ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    try {
3258c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                        responseResult = handleResponse(response);
326ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    } catch (final IOException e) {
327ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        LogUtils.e(LOG_TAG, e, "Exception while handling response");
32861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                        return RESULT_NETWORK_PROBLEM;
3297f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    } catch (final CommandStatusException e) {
3307f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // For some operations (notably Sync & FolderSync), errors are signaled in
3317f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // the payload of the response. These will have a HTTP 200 response, and the
3327f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // error condition is only detected during response parsing.
3337f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // The various parsers handle this by throwing a CommandStatusException.
3347f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // TODO: Consider having the parsers return the errors instead of throwing.
3357f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        final int status = e.mStatus;
3367f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        LogUtils.e(LOG_TAG, "CommandStatusException: %s, %d", getCommand(), status);
3377f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        if (CommandStatusException.CommandStatus.isNeedsProvisioning(status)) {
3387f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_PROVISIONING_ERROR;
3397f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        } else if (CommandStatusException.CommandStatus.isDeniedAccess(status)) {
3407f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_FORBIDDEN;
3417f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        } else {
3427f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_OTHER_FAILURE;
3437f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        }
344ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    }
3457f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    result = responseResult;
346328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                } else {
34761cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    result = handleHttpError(response.getStatus());
348ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
349ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
3507f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                // Non-negative results indicate success. Return immediately and bypass the error
3517f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                // handling.
35261ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon                if (result >= EasOperation.RESULT_MIN_OK_RESULT) {
3537f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    return result;
3547f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                }
3557f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu
356328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // If this operation has distinct handling for 403 errors, do that.
357328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (result == RESULT_FORBIDDEN || (response.isForbidden() && handleForbidden())) {
358ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    LogUtils.e(LOG_TAG, "Forbidden response");
359328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_FORBIDDEN;
360328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                }
361328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
362328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle provisioning errors.
363328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (result == RESULT_PROVISIONING_ERROR || response.isProvisionError()) {
3648c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                    if (handleProvisionError()) {
365328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        // The provisioning error has been taken care of, so we should re-do this
366328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        // request.
3677f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        LogUtils.d(LOG_TAG, "Provisioning error handled during %s, retrying",
3687f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                                getCommand());
369328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        continue;
370328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    }
371328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_PROVISIONING_ERROR;
372328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                }
373328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
374328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle authentication errors.
375328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (response.isAuthError()) {
376ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    LogUtils.e(LOG_TAG, "Authentication error");
377b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    if (response.isMissingCertificate()) {
378b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        return RESULT_CLIENT_CERTIFICATE_REQUIRED;
379b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    }
380328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_AUTHENTICATION_ERROR;
381ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
382ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
383328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle redirects.
384328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (response.isRedirectError()) {
385328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    ++redirectCount;
386328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    mConnection.redirectHostAuth(response.getRedirectAddress());
387328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    // Note that unlike other errors, we do NOT return here; we just keep looping.
388328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                } else {
389328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    // All other errors.
3900c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu                    LogUtils.e(LOG_TAG, "Generic error for operation %s: status %d, result %d",
3910c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu                            getCommand(), response.getStatus(), result);
392c0efae08efe0b337aa406b50f9d2dbc6bbbd56fdYu Ping Hu                    // TODO: This probably should return result.
393ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    return RESULT_OTHER_FAILURE;
394ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
395ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } finally {
396ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                response.close();
397ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            }
398ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        } while (redirectCount < MAX_REDIRECTS);
399ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
400328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        // Non-redirects return immediately after handling, so the only way to reach here is if we
401ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // looped too many times.
402ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        LogUtils.e(LOG_TAG, "Too many redirects");
403ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return RESULT_TOO_MANY_REDIRECTS;
404ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
405ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
40661cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    protected void onRequestMade() {
40761cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // This can be overridden to do any cleanup that must happen after the request has
40861cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // been sent. It will always be called, regardless of the status of the request.
40961cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    }
41061cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon
411303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon    protected void onRequestComplete() {
412303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon        // This can be overridden to do any cleanup that must happen after the request has
413303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon        // finished. (i.e. either the response has come back and been processed, or some error
414303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon        // has occurred and we have given up.
415303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon        // It will always be called, regardless of the status of the response.
416303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon    }
417303a529e9fe182b00fb4a105597d43c9f4eedb79Martin Hibdon
41861cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    protected int handleHttpError(final int httpStatus) {
41961cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // This function can be overriden if the child class needs to change the result code
42061cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // based on the http response status.
42161cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        return RESULT_OTHER_FAILURE;
42261cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    }
42361cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon
424ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
425b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Reset the protocol version to use for this connection. If it's changed, and our account is
42664fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * persisted, also write back the changes to the DB. Note that this function is called at
42764fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * the time of Account creation but does not update the Account object with the various flags
42864fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * at that point in time.
42964fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * TODO: Make sure that the Account flags are set properly in this function or a similar
43064fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * function in the future. Right now the Account setup activity sets the flags, this is not
43164fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee     * the right design.
432b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @param protocolVersion The new protocol version to use, as a string.
433ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
434b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final void setProtocolVersion(final String protocolVersion) {
4356c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        final long accountId = getAccountId();
4366c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        if (mConnection.setProtocolVersion(protocolVersion) && accountId != Account.NOT_SAVED) {
4376c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            final Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
438b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            final ContentValues cv = new ContentValues(2);
439b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            if (getProtocolVersion() >= 12.0) {
440b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                final int oldFlags = Utility.getFirstRowInt(mContext, uri,
441b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        Account.ACCOUNT_FLAGS_PROJECTION, null, null, null,
442b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        Account.ACCOUNT_FLAGS_COLUMN_FLAGS, 0);
44364fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee                final int newFlags = oldFlags |
44464fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee                        Account.FLAGS_SUPPORTS_GLOBAL_SEARCH | Account.FLAGS_SUPPORTS_SEARCH |
44564fbef3d9c3a1e5c6f94d918daf44f74609923a1Anthony Lee                                Account.FLAGS_SUPPORTS_SMART_FORWARD;
446b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                if (oldFlags != newFlags) {
447b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    cv.put(EmailContent.AccountColumns.FLAGS, newFlags);
448b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                }
449b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            }
450b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            cv.put(EmailContent.AccountColumns.PROTOCOL_VERSION, protocolVersion);
451b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            mContext.getContentResolver().update(uri, cv, null, null);
452b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        }
453b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
454ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
455b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
456b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Create the request object for this operation.
45703735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdon     * The default is to use a POST, but some use other request types (e.g. Options).
458b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return An {@link HttpUriRequest}.
459b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @throws IOException
460b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
46103735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdon    protected HttpUriRequest makeRequest() throws IOException, MessageInvalidException {
462b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final String requestUri = getRequestUri();
463275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon        HttpUriRequest req = mConnection.makePost(requestUri, getRequestEntity(),
464b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                getRequestContentType(), addPolicyKeyHeaderToRequest());
465275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon        return req;
466ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
467ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
468ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
469ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The following functions MUST be overridden by subclasses; these are things that are unique
470ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * to each operation.
471ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
472ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
473ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
474ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Get the name of the operation, used as the "Cmd=XXX" query param in the request URI. Note
4750c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * that if you override {@link #getRequestUri}, then this function may be unused for normal
4760c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * operation, but all subclasses should return something non-null for use with logging.
4770c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * @return The name of the command for this operation as defined by the EAS protocol, or for
4780c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     *         commands that don't need it, a suitable descriptive name for logging.
479ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
480ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected abstract String getCommand();
481ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
482ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
483b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Build the {@link HttpEntity} which is used to construct the POST. Typically this function
484ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * will build the Exchange request using a {@link Serializer} and then call {@link #makeEntity}.
485b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * If the subclass is not using a POST, then it should override this to return null.
486275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon     * @return The {@link HttpEntity} to pass to {@link com.android.exchange.service.EasServerConnection#makePost}.
487ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @throws IOException
488ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
489c4227f53c3ebfdcb9095d01199cf06bc093b1bbeMartin Hibdon    protected abstract HttpEntity getRequestEntity() throws IOException, MessageInvalidException;
490ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
491ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
492ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Parse the response from the Exchange perform whatever actions are dictated by that.
493ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param response The {@link EasResponse} to our request.
494328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return A result code. Non-negative values are returned directly to the caller; negative
495328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *         values
496328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
497328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * that is returned to the caller of {@link #performOperation}.
498ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @throws IOException
499ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
5008c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    protected abstract int handleResponse(final EasResponse response)
5017f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu            throws IOException, CommandStatusException;
502ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
503ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
504ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The following functions may be overriden by a subclass, but most operations will not need
505ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * to do so.
506ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
507ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
508ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
509ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Get the URI for the Exchange server and this operation. Most (signed in) operations need
510ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * not override this; the notable operation that needs to override it is auto-discover.
511ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return
512ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
513ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected String getRequestUri() {
514ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return mConnection.makeUriString(getCommand());
515ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
516ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
517ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
518ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return Whether to set the X-MS-PolicyKey header. Only Ping does not want this header.
519ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
520ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected boolean addPolicyKeyHeaderToRequest() {
521ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return true;
522ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
523ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
524ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
525ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The content type of this request.
526ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
527ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected String getRequestContentType() {
528ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return EAS_14_MIME_TYPE;
529ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
530ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
531ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
532ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The timeout to use for the POST.
533ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
534ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected long getTimeout() {
535ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return 30 * DateUtils.SECOND_IN_MILLIS;
536ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
537ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
538ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
539ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * If 403 responses should be handled in a special way, this function should be overridden to
540ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * do that.
541ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return Whether we handle 403 responses; if false, then treat 403 as a provisioning error.
542ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
543ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected boolean handleForbidden() {
544ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return false;
545ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
546ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
547ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
548328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * Handle a provisioning error. Subclasses may override this to do something different, e.g.
549328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * to validate rather than actually do the provisioning.
550328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return
551328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
5528c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    protected boolean handleProvisionError() {
553b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final EasProvision provisionOperation = new EasProvision(this);
5548c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu        return provisionOperation.provision();
555328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
556328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
557328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
558328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * Convenience methods for subclasses to use.
559328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
560328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
561328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
562ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Convenience method to make an {@link HttpEntity} from {@link Serializer}.
563ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
564ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final HttpEntity makeEntity(final Serializer s) {
565ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return new ByteArrayEntity(s.toByteArray());
566ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
567ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
568328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
569b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Check whether we should ask the server what protocol versions it supports and set this
570b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * account to use that version.
571b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether we need a new protocol version from the server.
572b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
573b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final boolean shouldGetProtocolVersion() {
574b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        // TODO: Find conditions under which we should check other than not having one yet.
575b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return !mConnection.isProtocolVersionSet();
576b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
577b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
578b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
579328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return The protocol version to use.
580328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
581ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final double getProtocolVersion() {
582ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return mConnection.getProtocolVersion();
583ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
584ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
585ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
586328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return Our useragent.
587328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
588328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    protected final String getUserAgent() {
589328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        return mConnection.getUserAgent();
590328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
591328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
592328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
593b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether we succeeeded in registering the client cert.
594b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
595b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final boolean registerClientCert() {
596b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return mConnection.registerClientCert();
597b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
598b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
599b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
600be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     * Add the device information to the current request.
601be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     * @param s The {@link Serializer} for our current request.
602a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param context The {@link Context} for current device.
603a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param userAgent The user agent string that our connection use.
604be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     */
605a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    protected static void expandedAddDeviceInformationToSerializer(final Serializer s,
606a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            final Context context, final String userAgent) throws IOException {
6072e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String deviceId;
6082e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String phoneNumber;
6092e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String operator;
610a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final TelephonyManager tm = (TelephonyManager)context.getSystemService(
611a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee                Context.TELEPHONY_SERVICE);
6122e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (tm != null) {
6132e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            deviceId = tm.getDeviceId();
6142e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            phoneNumber = tm.getLine1Number();
615a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // TODO: This is not perfect and needs to be improved, for at least two reasons:
616a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // 1) SIM cards can override this name.
617a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // 2) We don't resend this info to the server when we change networks.
618a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            final String operatorName = tm.getNetworkOperatorName();
619a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            final String operatorNumber = tm.getNetworkOperator();
620a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            if (!TextUtils.isEmpty(operatorName) && !TextUtils.isEmpty(operatorNumber)) {
621a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorName + " (" + operatorNumber + ")";
622a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            } else if (!TextUtils.isEmpty(operatorName)) {
623a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorName;
624a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            } else {
625a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorNumber;
626a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            }
6272e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        } else {
6282e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            deviceId = null;
6292e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            phoneNumber = null;
6302e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            operator = null;
6312e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
6322e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon
6332e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // TODO: Right now, we won't send this information unless the device is provisioned again.
6342e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // Potentially, this means that our phone number could be out of date if the user
6352e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // switches sims. Is there something we can do to force a reprovision?
636be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.start(Tags.SETTINGS_DEVICE_INFORMATION).start(Tags.SETTINGS_SET);
637be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.data(Tags.SETTINGS_MODEL, Build.MODEL);
6382e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (deviceId != null) {
6392e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_IMEI, tm.getDeviceId());
6402e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
64170755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        // Set the device friendly name, if we have one.
64270755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        // TODO: Longer term, this should be done without a provider call.
643a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final Bundle deviceName = context.getContentResolver().call(
64470755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu                EmailContent.CONTENT_URI, EmailContent.DEVICE_FRIENDLY_NAME, null, null);
645a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        if (deviceName != null) {
646a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            final String friendlyName = deviceName.getString(EmailContent.DEVICE_FRIENDLY_NAME);
64770755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu            if (!TextUtils.isEmpty(friendlyName)) {
64870755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu                s.data(Tags.SETTINGS_FRIENDLY_NAME, friendlyName);
64970755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu            }
65070755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        }
651be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.data(Tags.SETTINGS_OS, "Android " + Build.VERSION.RELEASE);
6522e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (phoneNumber != null) {
6532e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_PHONE_NUMBER, phoneNumber);
6542e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
6552e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // TODO: Consider setting this, but make sure we know what it's used for.
6562e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // If the user changes the device's locale and we don't do a reprovision, the server's
6572e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // idea of the language will be wrong. Since we're not sure what this is used for,
6582e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // right now we're leaving it out.
6592e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        //s.data(Tags.SETTINGS_OS_LANGUAGE, Locale.getDefault().getDisplayLanguage());
660a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        s.data(Tags.SETTINGS_USER_AGENT, userAgent);
6612e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (operator != null) {
6622e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_MOBILE_OPERATOR, operator);
6632e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
664be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.end().end();  // SETTINGS_SET, SETTINGS_DEVICE_INFORMATION
665be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu    }
666be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu
667be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu    /**
668a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * Add the device information to the current request.
669a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param s The {@link Serializer} that contains the payload for this request.
670a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     */
671a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    protected final void addDeviceInformationToSerializer(final Serializer s)
672a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            throws IOException {
673a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final String userAgent = getUserAgent();
674a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        expandedAddDeviceInformationToSerializer(s, mContext, userAgent);
675a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    }
676a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee
677a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    /**
678ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Convenience method for adding a Message to an account's outbox
679ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param account The {@link Account} from which to send the message.
680ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param msg the message to send
681ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
682328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    protected final void sendMessage(final Account account, final EmailContent.Message msg) {
683ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        long mailboxId = Mailbox.findMailboxOfType(mContext, account.mId, Mailbox.TYPE_OUTBOX);
684ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // TODO: Improve system mailbox handling.
685ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (mailboxId == Mailbox.NO_MAILBOX) {
686ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            LogUtils.d(LOG_TAG, "No outbox for account %d, creating it", account.mId);
687ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            final Mailbox outbox =
688ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    Mailbox.newSystemMailbox(mContext, account.mId, Mailbox.TYPE_OUTBOX);
689ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            outbox.save(mContext);
690ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            mailboxId = outbox.mId;
691ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        }
692ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.mMailboxKey = mailboxId;
693ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.mAccountKey = account.mId;
694ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.save(mContext);
695ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        requestSyncForMailbox(new android.accounts.Account(account.mEmailAddress,
6964427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mailboxId);
697ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
698ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
699ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
700ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Issue a {@link android.content.ContentResolver#requestSync} for a specific mailbox.
701ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param amAccount The {@link android.accounts.Account} for the account we're pinging.
702ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param mailboxId The id of the mailbox that needs to sync.
703ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
704ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected static void requestSyncForMailbox(final android.accounts.Account amAccount,
7054427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon            final long mailboxId) {
7064427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        final Bundle extras = Mailbox.createSyncBundle(mailboxId);
7074427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras);
7084427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        LogUtils.i(LOG_TAG, "requestSync EasOperation requestSyncForMailbox %s, %s",
7094427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon                amAccount.toString(), extras.toString());
7104427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    }
7114427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon
7124427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    protected static void requestSyncForMailboxes(final android.accounts.Account amAccount,
713f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon            final String authority, final ArrayList<Long> mailboxIds) {
7144427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        final Bundle extras = Mailbox.createSyncBundle(mailboxIds);
7155b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee        /**
716db678bada9a25089f4ea32be0d8e6d678749459cMartin Hibdon         * TODO: Right now, this function is only called by EasPing, should this function be
717db678bada9a25089f4ea32be0d8e6d678749459cMartin Hibdon         * moved there?
7185b61ea31f07f1a623eb37f212ae2bf99d1717292Anthony Lee         */
719ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        ContentResolver.requestSync(amAccount, authority, extras);
720f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon        LogUtils.i(LOG_TAG, "EasOperation requestSyncForMailboxes  %s, %s",
7219c7165d4c6b90101b781f90b17451efd42a17929Martin Hibdon                amAccount.toString(), extras.toString());
722ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
7238c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu
7246cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu    public static int translateSyncResultToUiResult(final int result) {
7256cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu        switch (result) {
7266cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu              case RESULT_TOO_MANY_REDIRECTS:
7276cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.INTERNAL_ERROR;
72861ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon            case RESULT_NETWORK_PROBLEM:
7296cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.CONNECTION_ERROR;
7306cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_FORBIDDEN:
7316cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_PROVISIONING_ERROR:
7326cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_AUTHENTICATION_ERROR:
7336cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_CLIENT_CERTIFICATE_REQUIRED:
7346cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.AUTH_ERROR;
7356cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_PROTOCOL_VERSION_UNSUPPORTED:
7366cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                // Only used in validate, so there's never a syncResult to write to here.
7376cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                break;
7386cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_INITIALIZATION_FAILURE:
7396cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_HARD_DATA_FAILURE:
7406cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.INTERNAL_ERROR;
7416cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu            case RESULT_OTHER_FAILURE:
7426cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu                return UIProvider.LastSyncResult.INTERNAL_ERROR;
7436cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu        }
7446cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu        return UIProvider.LastSyncResult.SUCCESS;
7456cb6c70ea47035a27bd1d808babe39a2d1829c8dYu Ping Hu    }
746ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu}
747