EasOperation.java revision 2ec65207b3ac9589685eeb074cc04b4d300fe23f
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;
42ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.mail.utils.LogUtils;
43ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
44ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport org.apache.http.HttpEntity;
45b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport org.apache.http.client.methods.HttpUriRequest;
46ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport org.apache.http.entity.ByteArrayEntity;
47ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
48ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport java.io.IOException;
49d92a75c707461188e8743149476e8f49ef191b42Tony Mantlerimport java.security.cert.CertificateException;
504427badd747b7c172934014b9f95a1be1256f35aMartin Hibdonimport java.util.ArrayList;
51ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
52ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu/**
53ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Base class for all Exchange operations that use a POST to talk to the server.
54ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
55ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * The core of this class is {@link #performOperation}, which provides the skeleton of making
56ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * a request, handling common errors, and setting fields on the {@link SyncResult} if there is one.
57ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * This class abstracts the connection handling from its subclasses and callers.
58ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu *
596c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * {@link #performOperation} calls various abstract functions to create the request and parse the
606c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * response. For the most part subclasses can implement just these bits of functionality and rely
616c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * on {@link #performOperation} to do all the boilerplate etc.
626c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
636c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * There are also a set of functions that a subclass may override if it's substantially
646c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * different from the "normal" operation (e.g. autodiscover deviates from the standard URI since
656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * it's not account-specific so it needs to override {@link #getRequestUri()}), but the default
666c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * implementations of these functions should suffice for most operations.
676c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
686c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Some subclasses may need to override {@link #performOperation} to add validation and results
696c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * processing around a call to super.performOperation. Subclasses should avoid doing too much more
706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * than wrapping some handling around the chained call; if you find that's happening, it's likely
716c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * a sign that the base class needs to be enhanced.
726c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
736c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * One notable reason this wrapping happens is for operations that need to return a result directly
746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * to their callers (as opposed to simply writing the results to the provider, as is common with
756c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * sync operations). This happens for example in
766c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * {@link com.android.emailcommon.service.IEmailService} message handlers. In such cases, due to
776c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * how {@link com.android.exchange.service.EasService} uses this class, the subclass needs to
786c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * store the result as a member variable and then provide an accessor to read the result. Since
796c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * different operations have different results (or none at all), there is no function in the base
806c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * class for this.
816c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
826c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * Note that it is not practical to avoid the race between when an operation loads its account data
836c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * and when it uses it, as that would require some form of locking in the provider. There are three
846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * interesting situations where this might happen, and that this class must handle:
856c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
866c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 1) Deleted from provider: Any subsequent provider access should return an error. Operations
876c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    must detect this and terminate with an error.
886c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 2) Account sync settings change: Generally only affects Ping. We interrupt the operation and
896c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    load the new settings before proceeding.
906c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * 3) Sync suspended due to hold: A special case of the previous, and affects all operations, but
916c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    fortunately doesn't need special handling here. Correct provider functionality must generate
926c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *    write failures, so the handling for #1 should cover this case as well.
936c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu *
946c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * This class attempts to defer loading of account data as long as possible -- ideally we load
956c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * immediately before the network request -- but does not proactively check for changes after that.
966c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * This approach is a a practical balance between minimizing the race without adding too much
976c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu * complexity beyond what's required.
98ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */
99ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hupublic abstract class EasOperation {
100ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public static final String LOG_TAG = Eas.LOG_TAG;
101ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
102ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /** The maximum number of server redirects we allow before returning failure. */
103ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private static final int MAX_REDIRECTS = 3;
104ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
105ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /** Message MIME type for EAS version 14 and later. */
106ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private static final String EAS_14_MIME_TYPE = "application/vnd.ms-sync.wbxml";
107ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
108b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee    /**
109b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * EasOperation error codes below.  All subclasses should try to create error codes
110b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * that do not overlap these codes or the codes of other subclasses. The error
111b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * code values for each subclass should start in a different 100 range (i.e. -100,
112b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     * -200, etc...).
113b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee     */
114b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee
115328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the operation was cancelled via {@link #abort}. */
116328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_ABORT = -1;
117328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the operation was cancelled via {@link #restart}. */
118328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_RESTART = -2;
119328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the Exchange servers redirected too many times. */
120328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_TOO_MANY_REDIRECTS = -3;
121328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating the request failed due to a network problem. */
122328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_REQUEST_FAILURE = -4;
123328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating a 403 (forbidden) error. */
124328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_FORBIDDEN = -5;
125328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating an unresolved provisioning error. */
126328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_PROVISIONING_ERROR = -6;
127328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating an authentication problem. */
128328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public static final int RESULT_AUTHENTICATION_ERROR = -7;
129b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** Error code indicating the client is missing a certificate. */
130b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public static final int RESULT_CLIENT_CERTIFICATE_REQUIRED = -8;
131b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** Error code indicating we don't have a protocol version in common with the server. */
132b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public static final int RESULT_PROTOCOL_VERSION_UNSUPPORTED = -9;
1336c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** Error code indicating the account could not be loaded from the provider. */
1346c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    public static final int RESULT_ACCOUNT_ID_INVALID = -10;
1358c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    /** Error code indicating a hard data layer error. */
1368c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    public static final int RESULT_HARD_DATA_FAILURE = -11;
137328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /** Error code indicating some other failure. */
1388c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    public static final int RESULT_OTHER_FAILURE = -12;
139328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
140ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final Context mContext;
141328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
1426c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The provider id for the account this operation is on. */
1436c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private final long mAccountId;
1446c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1456c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The cached {@link Account} state; can be null if it hasn't been loaded yet. */
1466c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    protected Account mAccount;
1476c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1486c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /** The connection to use for this operation. This is created when {@link #mAccount} is set. */
1496c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    private EasServerConnection mConnection;
1506c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
151328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
1526c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Constructor which defers loading of account and connection info.
1536c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param context
1546c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param accountId
155328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
1566c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    protected EasOperation(final Context context, final long accountId) {
1576c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mContext = context;
1586c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccountId = accountId;
1596c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
160ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
1611df4a493b2efa34fce4bd8a70aca57203b4ed037Yu Ping Hu    // TODO: Make this private again when EasSyncHandler is converted to be a subclass.
1626c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    protected EasOperation(final Context context, final Account account,
163b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            final EasServerConnection connection) {
1646c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        this(context, account.mId);
1656c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount = account;
166b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        mConnection = connection;
167b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
168b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
169ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected EasOperation(final Context context, final Account account, final HostAuth hostAuth) {
1706c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        this(context, account, new EasServerConnection(context, account, hostAuth));
171ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
172ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
173ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected EasOperation(final Context context, final Account account) {
1746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        this(context, account, account.getOrCreateHostAuthRecv(context));
175ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
176ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
177b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
178b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * This constructor is for use by operations that are created by other operations, e.g.
1796c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * {@link EasProvision}. It reuses the account and connection of its parent.
180b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @param parentOperation The {@link EasOperation} that is creating us.
181b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
182b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected EasOperation(final EasOperation parentOperation) {
1836c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mContext = parentOperation.mContext;
1846c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccountId = parentOperation.mAccountId;
1856c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount = parentOperation.mAccount;
1866c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mConnection = parentOperation.mConnection;
1876c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
1886c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
1896c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
1906c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Some operations happen before the account exists (e.g. account validation).
1912ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon     * These operations cannot use {@link #init}, so instead we make a dummy account and
1926c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * supply a temporary {@link HostAuth}.
1936c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param hostAuth
1946c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
1956c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    protected final void setDummyAccount(final HostAuth hostAuth) {
1966c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount = new Account();
1976c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mAccount.mEmailAddress = hostAuth.mLogin;
1986c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        mConnection = new EasServerConnection(mContext, mAccount, hostAuth);
1996c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
2006c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
2016c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    /**
2026c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * Loads (or reloads) the {@link Account} for this operation, and sets up our connection to the
2032ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon     * server. This can be overridden to add additional functionality, but child implementations
2042ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon     * should always call super().
2056c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @param allowReload If false, do not perform a load if we already have an {@link Account}
2066c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     *                    (i.e. just keep the existing one); otherwise allow replacement of the
2076c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     *                    account. Note that this can result in a valid Account being replaced with
2086c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     *                    null if the account no longer exists.
2096c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     * @return Whether we now have a valid {@link Account} object.
2106c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu     */
2112ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon    public boolean init(final boolean allowReload) {
2126c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        if (mAccount == null || allowReload) {
2136c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            mAccount = Account.restoreAccountWithId(mContext, getAccountId());
2146c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            if (mAccount != null) {
2156c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                mConnection = new EasServerConnection(mContext, mAccount,
2166c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                        mAccount.getOrCreateHostAuthRecv(mContext));
2176c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            }
2186c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
2196c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        return (mAccount != null);
2206c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    }
2216c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
2226c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu    public final long getAccountId() {
2236c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        return mAccountId;
224328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
225ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
226c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    public final Account getAccount() {
227c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee        return mAccount;
228c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee    }
229c9e6e7587ac847c6b99d91097f8e1bc64e9e56daAnthony Lee
230ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
231ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Request that this operation terminate. Intended for use by the sync service to interrupt
232ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * running operations, primarily Ping.
233ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
234ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public final void abort() {
235ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        mConnection.stop(EasServerConnection.STOPPED_REASON_ABORT);
236ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
237ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
238ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
239ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Request that this operation restart. Intended for use by the sync service to interrupt
240ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * running operations, primarily Ping.
241ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
242ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public final void restart() {
243ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        mConnection.stop(EasServerConnection.STOPPED_REASON_RESTART);
244ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
245ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
246ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
247ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The skeleton of performing an operation. This function handles all the common code and
248ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * error handling, calling into virtual functions that are implemented or overridden by the
249ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * subclass to do the operation-specific logic.
250328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
251328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * The result codes work as follows:
252328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * - Negative values indicate common error codes and are defined above (the various RESULT_*
253328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *   constants).
254328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * - Non-negative values indicate the result of {@link #handleResponse}. These are obviously
255328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *   specific to the subclass, and may indicate success or error conditions.
256328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
257328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * The common error codes primarily indicate conditions that occur when performing the POST
258328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * itself, such as network errors and handling of the HTTP response. However, some errors that
259328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * can be indicated in the HTTP response code can also be indicated in the payload of the
260328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * response as well, so {@link #handleResponse} should in those cases return the appropriate
261328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * negative result code, which will be handled the same as if it had been indicated in the HTTP
262328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * response code.
263328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
264328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return A result code for the outcome of this operation, as described above.
265ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
2668c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    public int performOperation() {
2676c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        // Make sure the account is loaded if it hasn't already been.
2682ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon        if (!init(false)) {
2692ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon            // TODO: Fix this comment and error code, init() can now fail for reasons other than
2702ec65207b3ac9589685eeb074cc04b4d300fe23fMartin Hibdon            // failing to load the account.
2716c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            LogUtils.i(LOG_TAG, "Failed to load account %d before sending request for operation %s",
2726c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu                    getAccountId(), getCommand());
2736c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            return RESULT_ACCOUNT_ID_INVALID;
2746c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        }
2756c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu
276ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // We handle server redirects by looping, but we need to protect against too much looping.
277ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        int redirectCount = 0;
278ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
279ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        do {
280b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            // Perform the HTTP request and handle exceptions.
281ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            final EasResponse response;
282ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            try {
28361cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                try {
28461cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    response = mConnection.executeHttpUriRequest(makeRequest(), getTimeout());
28561cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                } finally {
28661cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    onRequestMade();
28761cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                }
288ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } catch (final IOException e) {
289ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // If we were stopped, return the appropriate result code.
290ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                switch (mConnection.getStoppedReason()) {
291ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    case EasServerConnection.STOPPED_REASON_ABORT:
292ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        return RESULT_ABORT;
293ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    case EasServerConnection.STOPPED_REASON_RESTART:
294ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        return RESULT_RESTART;
295ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    default:
296ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        break;
297ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
298ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // If we're here, then we had a IOException that's not from a stop request.
299dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                String message = e.getMessage();
300dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                if (message == null) {
301dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                    message = "(no message)";
302dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                }
303dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu                LogUtils.i(LOG_TAG, "IOException while sending request: %s", message);
304ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                return RESULT_REQUEST_FAILURE;
305d92a75c707461188e8743149476e8f49ef191b42Tony Mantler            } catch (final CertificateException e) {
306d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                LogUtils.i(LOG_TAG, "CertificateException while sending request: %s",
307d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                        e.getMessage());
308d92a75c707461188e8743149476e8f49ef191b42Tony Mantler                return RESULT_CLIENT_CERTIFICATE_REQUIRED;
309ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } catch (final IllegalStateException e) {
310ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // Subclasses use ISE to signal a hard error when building the request.
3116f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu                // TODO: Switch away from ISEs.
312ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                LogUtils.e(LOG_TAG, e, "Exception while sending request");
3138c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                return RESULT_HARD_DATA_FAILURE;
314ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            }
315ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
316ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            // The POST completed, so process the response.
317ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            try {
318328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                final int result;
319ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // First off, the success case.
320ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                if (response.isSuccess()) {
3217f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    int responseResult;
322ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    try {
3238c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                        responseResult = handleResponse(response);
324ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    } catch (final IOException e) {
325ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                        LogUtils.e(LOG_TAG, e, "Exception while handling response");
3262629e0f0eac5bb6c4d5982bdf65a989b03d46d3dAlon Albert                        return RESULT_REQUEST_FAILURE;
3277f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    } catch (final CommandStatusException e) {
3287f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // For some operations (notably Sync & FolderSync), errors are signaled in
3297f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // the payload of the response. These will have a HTTP 200 response, and the
3307f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // error condition is only detected during response parsing.
3317f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // The various parsers handle this by throwing a CommandStatusException.
3327f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        // TODO: Consider having the parsers return the errors instead of throwing.
3337f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        final int status = e.mStatus;
3347f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        LogUtils.e(LOG_TAG, "CommandStatusException: %s, %d", getCommand(), status);
3357f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        if (CommandStatusException.CommandStatus.isNeedsProvisioning(status)) {
3367f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_PROVISIONING_ERROR;
3377f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        } else if (CommandStatusException.CommandStatus.isDeniedAccess(status)) {
3387f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_FORBIDDEN;
3397f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        } else {
3407f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                            responseResult = RESULT_OTHER_FAILURE;
3417f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        }
342ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    }
3437f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    result = responseResult;
344328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                } else {
34561cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon                    result = handleHttpError(response.getStatus());
346ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
347ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
3487f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                // Non-negative results indicate success. Return immediately and bypass the error
3497f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                // handling.
3507f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                if (result >= 0) {
3517f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                    return result;
3527f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                }
3537f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu
354328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // If this operation has distinct handling for 403 errors, do that.
355328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (result == RESULT_FORBIDDEN || (response.isForbidden() && handleForbidden())) {
356ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    LogUtils.e(LOG_TAG, "Forbidden response");
357328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_FORBIDDEN;
358328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                }
359328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
360328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle provisioning errors.
361328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (result == RESULT_PROVISIONING_ERROR || response.isProvisionError()) {
3628c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                    if (handleProvisionError()) {
363328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        // The provisioning error has been taken care of, so we should re-do this
364328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        // request.
3657f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                        LogUtils.d(LOG_TAG, "Provisioning error handled during %s, retrying",
3667f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu                                getCommand());
367328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        continue;
368328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    }
369328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_PROVISIONING_ERROR;
370328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                }
371328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
372328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle authentication errors.
373328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (response.isAuthError()) {
374ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    LogUtils.e(LOG_TAG, "Authentication error");
375b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    if (response.isMissingCertificate()) {
376b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        return RESULT_CLIENT_CERTIFICATE_REQUIRED;
377b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    }
378328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    return RESULT_AUTHENTICATION_ERROR;
379ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
380ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
381328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                // Handle redirects.
382328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                if (response.isRedirectError()) {
383328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    ++redirectCount;
384328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    mConnection.redirectHostAuth(response.getRedirectAddress());
385328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    // Note that unlike other errors, we do NOT return here; we just keep looping.
386328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                } else {
387328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                    // All other errors.
3880c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu                    LogUtils.e(LOG_TAG, "Generic error for operation %s: status %d, result %d",
3890c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu                            getCommand(), response.getStatus(), result);
390ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    return RESULT_OTHER_FAILURE;
391ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
392ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } finally {
393ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                response.close();
394ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            }
395ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        } while (redirectCount < MAX_REDIRECTS);
396ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
397328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        // Non-redirects return immediately after handling, so the only way to reach here is if we
398ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // looped too many times.
399ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        LogUtils.e(LOG_TAG, "Too many redirects");
400ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return RESULT_TOO_MANY_REDIRECTS;
401ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
402ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
40361cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    protected void onRequestMade() {
40461cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // This can be overridden to do any cleanup that must happen after the request has
40561cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // been sent. It will always be called, regardless of the status of the request.
40661cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    }
40761cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon
40861cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    protected int handleHttpError(final int httpStatus) {
40961cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // This function can be overriden if the child class needs to change the result code
41061cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        // based on the http response status.
41161cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon        return RESULT_OTHER_FAILURE;
41261cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon    }
41361cb606031183d98cef654d1aaa3461f48746ad6Martin Hibdon
414ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
415b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Reset the protocol version to use for this connection. If it's changed, and our account is
416b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * persisted, also write back the changes to the DB.
417b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @param protocolVersion The new protocol version to use, as a string.
418ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
419b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final void setProtocolVersion(final String protocolVersion) {
4206c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        final long accountId = getAccountId();
4216c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu        if (mConnection.setProtocolVersion(protocolVersion) && accountId != Account.NOT_SAVED) {
4226c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu            final Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
423b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            final ContentValues cv = new ContentValues(2);
424b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            if (getProtocolVersion() >= 12.0) {
425b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                final int oldFlags = Utility.getFirstRowInt(mContext, uri,
426b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        Account.ACCOUNT_FLAGS_PROJECTION, null, null, null,
427b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        Account.ACCOUNT_FLAGS_COLUMN_FLAGS, 0);
428b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                final int newFlags = oldFlags
429b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                        | Account.FLAGS_SUPPORTS_GLOBAL_SEARCH + Account.FLAGS_SUPPORTS_SEARCH;
430b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                if (oldFlags != newFlags) {
431b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                    cv.put(EmailContent.AccountColumns.FLAGS, newFlags);
432b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                }
433b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            }
434b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            cv.put(EmailContent.AccountColumns.PROTOCOL_VERSION, protocolVersion);
435b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            mContext.getContentResolver().update(uri, cv, null, null);
436b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        }
437b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
438ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
439b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
440b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Create the request object for this operation.
441b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Most operations use a POST, but some use other request types (e.g. Options).
442b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return An {@link HttpUriRequest}.
443b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @throws IOException
444b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
445b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    private final HttpUriRequest makeRequest() throws IOException {
446b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final String requestUri = getRequestUri();
447b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        if (requestUri == null) {
448b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            return mConnection.makeOptions();
449b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        }
450b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return mConnection.makePost(requestUri, getRequestEntity(),
451b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                getRequestContentType(), addPolicyKeyHeaderToRequest());
452ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
453ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
454ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
455ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The following functions MUST be overridden by subclasses; these are things that are unique
456ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * to each operation.
457ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
458ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
459ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
460ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Get the name of the operation, used as the "Cmd=XXX" query param in the request URI. Note
4610c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * that if you override {@link #getRequestUri}, then this function may be unused for normal
4620c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * operation, but all subclasses should return something non-null for use with logging.
4630c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     * @return The name of the command for this operation as defined by the EAS protocol, or for
4640c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu     *         commands that don't need it, a suitable descriptive name for logging.
465ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
466ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected abstract String getCommand();
467ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
468ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
469b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Build the {@link HttpEntity} which is used to construct the POST. Typically this function
470ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * will build the Exchange request using a {@link Serializer} and then call {@link #makeEntity}.
471b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * If the subclass is not using a POST, then it should override this to return null.
472ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The {@link HttpEntity} to pass to {@link EasServerConnection#makePost}.
473ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @throws IOException
474ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
475ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected abstract HttpEntity getRequestEntity() throws IOException;
476ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
477ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
478ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Parse the response from the Exchange perform whatever actions are dictated by that.
479ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param response The {@link EasResponse} to our request.
480328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return A result code. Non-negative values are returned directly to the caller; negative
481328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *         values
482328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     *
483328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * that is returned to the caller of {@link #performOperation}.
484ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @throws IOException
485ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
4868c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    protected abstract int handleResponse(final EasResponse response)
4877f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu            throws IOException, CommandStatusException;
488ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
489ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
490ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * The following functions may be overriden by a subclass, but most operations will not need
491ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * to do so.
492ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
493ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
494ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
495ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Get the URI for the Exchange server and this operation. Most (signed in) operations need
496ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * not override this; the notable operation that needs to override it is auto-discover.
497ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return
498ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
499ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected String getRequestUri() {
500ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return mConnection.makeUriString(getCommand());
501ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
502ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
503ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
504ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return Whether to set the X-MS-PolicyKey header. Only Ping does not want this header.
505ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
506ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected boolean addPolicyKeyHeaderToRequest() {
507ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return true;
508ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
509ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
510ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
511ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The content type of this request.
512ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
513ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected String getRequestContentType() {
514ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return EAS_14_MIME_TYPE;
515ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
516ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
517ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
518ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The timeout to use for the POST.
519ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
520ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected long getTimeout() {
521ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return 30 * DateUtils.SECOND_IN_MILLIS;
522ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
523ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
524ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
525ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * If 403 responses should be handled in a special way, this function should be overridden to
526ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * do that.
527ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return Whether we handle 403 responses; if false, then treat 403 as a provisioning error.
528ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
529ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected boolean handleForbidden() {
530ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return false;
531ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
532ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
533ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
534328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * Handle a provisioning error. Subclasses may override this to do something different, e.g.
535328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * to validate rather than actually do the provisioning.
536328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return
537328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
5388c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    protected boolean handleProvisionError() {
539b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final EasProvision provisionOperation = new EasProvision(this);
5408c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu        return provisionOperation.provision();
541328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
542328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
543328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
544328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * Convenience methods for subclasses to use.
545328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
546328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
547328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
548ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Convenience method to make an {@link HttpEntity} from {@link Serializer}.
549ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
550ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final HttpEntity makeEntity(final Serializer s) {
551ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return new ByteArrayEntity(s.toByteArray());
552ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
553ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
554328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
555b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Check whether we should ask the server what protocol versions it supports and set this
556b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * account to use that version.
557b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether we need a new protocol version from the server.
558b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
559b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final boolean shouldGetProtocolVersion() {
560b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        // TODO: Find conditions under which we should check other than not having one yet.
561b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return !mConnection.isProtocolVersionSet();
562b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
563b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
564b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
565328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return The protocol version to use.
566328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
567ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected final double getProtocolVersion() {
568ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return mConnection.getProtocolVersion();
569ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
570ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
571ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
572328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return Our useragent.
573328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
574328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    protected final String getUserAgent() {
575328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        return mConnection.getUserAgent();
576328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
577328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
578328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
579b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether we succeeeded in registering the client cert.
580b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
581b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    protected final boolean registerClientCert() {
582b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return mConnection.registerClientCert();
583b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
584b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
585b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
586be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     * Add the device information to the current request.
587be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     * @param s The {@link Serializer} for our current request.
588a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param context The {@link Context} for current device.
589a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param userAgent The user agent string that our connection use.
590be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu     */
591a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    protected static void expandedAddDeviceInformationToSerializer(final Serializer s,
592a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            final Context context, final String userAgent) throws IOException {
5932e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String deviceId;
5942e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String phoneNumber;
5952e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        final String operator;
596a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final TelephonyManager tm = (TelephonyManager)context.getSystemService(
597a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee                Context.TELEPHONY_SERVICE);
5982e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (tm != null) {
5992e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            deviceId = tm.getDeviceId();
6002e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            phoneNumber = tm.getLine1Number();
601a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // TODO: This is not perfect and needs to be improved, for at least two reasons:
602a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // 1) SIM cards can override this name.
603a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            // 2) We don't resend this info to the server when we change networks.
604a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            final String operatorName = tm.getNetworkOperatorName();
605a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            final String operatorNumber = tm.getNetworkOperator();
606a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            if (!TextUtils.isEmpty(operatorName) && !TextUtils.isEmpty(operatorNumber)) {
607a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorName + " (" + operatorNumber + ")";
608a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            } else if (!TextUtils.isEmpty(operatorName)) {
609a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorName;
610a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            } else {
611a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu                operator = operatorNumber;
612a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu            }
6132e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        } else {
6142e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            deviceId = null;
6152e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            phoneNumber = null;
6162e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            operator = null;
6172e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
6182e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon
6192e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // TODO: Right now, we won't send this information unless the device is provisioned again.
6202e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // Potentially, this means that our phone number could be out of date if the user
6212e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // switches sims. Is there something we can do to force a reprovision?
622be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.start(Tags.SETTINGS_DEVICE_INFORMATION).start(Tags.SETTINGS_SET);
623be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.data(Tags.SETTINGS_MODEL, Build.MODEL);
6242e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (deviceId != null) {
6252e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_IMEI, tm.getDeviceId());
6262e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
62770755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        // Set the device friendly name, if we have one.
62870755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        // TODO: Longer term, this should be done without a provider call.
629a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final Bundle deviceName = context.getContentResolver().call(
63070755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu                EmailContent.CONTENT_URI, EmailContent.DEVICE_FRIENDLY_NAME, null, null);
631a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        if (deviceName != null) {
632a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            final String friendlyName = deviceName.getString(EmailContent.DEVICE_FRIENDLY_NAME);
63370755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu            if (!TextUtils.isEmpty(friendlyName)) {
63470755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu                s.data(Tags.SETTINGS_FRIENDLY_NAME, friendlyName);
63570755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu            }
63670755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu        }
637be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.data(Tags.SETTINGS_OS, "Android " + Build.VERSION.RELEASE);
6382e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (phoneNumber != null) {
6392e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_PHONE_NUMBER, phoneNumber);
6402e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
6412e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // TODO: Consider setting this, but make sure we know what it's used for.
6422e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // If the user changes the device's locale and we don't do a reprovision, the server's
6432e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // idea of the language will be wrong. Since we're not sure what this is used for,
6442e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        // right now we're leaving it out.
6452e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        //s.data(Tags.SETTINGS_OS_LANGUAGE, Locale.getDefault().getDisplayLanguage());
646a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        s.data(Tags.SETTINGS_USER_AGENT, userAgent);
6472e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        if (operator != null) {
6482e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon            s.data(Tags.SETTINGS_MOBILE_OPERATOR, operator);
6492e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon        }
650be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        s.end().end();  // SETTINGS_SET, SETTINGS_DEVICE_INFORMATION
651be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu    }
652be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu
653be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu    /**
654a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * Add the device information to the current request.
655a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     * @param s The {@link Serializer} that contains the payload for this request.
656a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee     */
657a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    protected final void addDeviceInformationToSerializer(final Serializer s)
658a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee            throws IOException {
659a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        final String userAgent = getUserAgent();
660a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee        expandedAddDeviceInformationToSerializer(s, mContext, userAgent);
661a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    }
662a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee
663a09e6cc2e8fca144de4d0b69f129b177f4a3219bAnthony Lee    /**
664ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Convenience method for adding a Message to an account's outbox
665ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param account The {@link Account} from which to send the message.
666ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param msg the message to send
667ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
668328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    protected final void sendMessage(final Account account, final EmailContent.Message msg) {
669ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        long mailboxId = Mailbox.findMailboxOfType(mContext, account.mId, Mailbox.TYPE_OUTBOX);
670ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // TODO: Improve system mailbox handling.
671ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (mailboxId == Mailbox.NO_MAILBOX) {
672ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            LogUtils.d(LOG_TAG, "No outbox for account %d, creating it", account.mId);
673ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            final Mailbox outbox =
674ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    Mailbox.newSystemMailbox(mContext, account.mId, Mailbox.TYPE_OUTBOX);
675ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            outbox.save(mContext);
676ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            mailboxId = outbox.mId;
677ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        }
678ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.mMailboxKey = mailboxId;
679ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.mAccountKey = account.mId;
680ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.save(mContext);
681ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        requestSyncForMailbox(new android.accounts.Account(account.mEmailAddress,
6824427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mailboxId);
683ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
684ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu
685ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    /**
686ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * Issue a {@link android.content.ContentResolver#requestSync} for a specific mailbox.
687ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param amAccount The {@link android.accounts.Account} for the account we're pinging.
688ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param mailboxId The id of the mailbox that needs to sync.
689ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     */
690ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected static void requestSyncForMailbox(final android.accounts.Account amAccount,
6914427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon            final long mailboxId) {
6924427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        final Bundle extras = Mailbox.createSyncBundle(mailboxId);
6934427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras);
6944427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        LogUtils.i(LOG_TAG, "requestSync EasOperation requestSyncForMailbox %s, %s",
6954427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon                amAccount.toString(), extras.toString());
6964427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    }
6974427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon
6984427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    protected static void requestSyncForMailboxes(final android.accounts.Account amAccount,
6994427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon            final ArrayList<Long> mailboxIds) {
7004427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        final Bundle extras = Mailbox.createSyncBundle(mailboxIds);
7014427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras);
7024427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        LogUtils.i(LOG_TAG, "requestSync EasOperation requestSyncForMailboxes  %s, %s",
7034427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon                amAccount.toString(), extras.toString());
7044427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    }
7054427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon
7064427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    /**
7074427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon     * RequestNoOpSync
7084427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon     * This requests a sync for a particular authority purely so that that account
7094427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon     * in settings will recognize that it is trying to sync, and will display the
7104427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon     * appropriate UI. In fact, all exchange data syncing actually happens through the
7114427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon     * EmailSyncAdapterService.
7124427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon     * @param amAccount
7134427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon     * @param authority
7144427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon     */
7154427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon    protected static void requestNoOpSync(final android.accounts.Account amAccount,
7164427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon            final String authority) {
717ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        final Bundle extras = new Bundle(1);
7184427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon        extras.putBoolean(Mailbox.SYNC_EXTRA_NOOP, true);
719ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        ContentResolver.requestSync(amAccount, authority, extras);
7203eef378426c7c88608f53f5a268baed40259ccf6Alon Albert        LogUtils.d(LOG_TAG, "requestSync EasOperation requestNoOpSync %s, %s",
7219c7165d4c6b90101b781f90b17451efd42a17929Martin Hibdon                amAccount.toString(), extras.toString());
722ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    }
7238c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu
7248c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    public static void writeResultToSyncResult(final int result, final SyncResult syncResult) {
7258c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu        switch (result) {
7268c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_TOO_MANY_REDIRECTS:
7278c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                syncResult.tooManyRetries = true;
7288c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                break;
7298c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_REQUEST_FAILURE:
7308c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                syncResult.stats.numIoExceptions = 1;
7318c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                break;
7328c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_FORBIDDEN:
7338c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_PROVISIONING_ERROR:
7348c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_AUTHENTICATION_ERROR:
7358c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_CLIENT_CERTIFICATE_REQUIRED:
7368c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                syncResult.stats.numAuthExceptions = 1;
7378c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                break;
7388c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_PROTOCOL_VERSION_UNSUPPORTED:
7398c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                // Only used in validate, so there's never a syncResult to write to here.
7408c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                break;
7418c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_ACCOUNT_ID_INVALID:
7428c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_HARD_DATA_FAILURE:
7438c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                syncResult.databaseError = true;
7448c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                break;
7458c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu            case RESULT_OTHER_FAILURE:
7468c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                // TODO: Is this correct?
7478c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                syncResult.stats.numIoExceptions = 1;
7488c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu                break;
7498c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu        }
7508c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu    }
751ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu}
752