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