EasOperation.java revision 70755e7a47c2199e6f047d5736ad7b63111aae82
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; 36ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.Eas; 37ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.EasResponse; 38ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.adapter.Serializer; 39be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Huimport com.android.exchange.adapter.Tags; 40ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.exchange.service.EasServerConnection; 41ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport com.android.mail.utils.LogUtils; 42ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 43ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport org.apache.http.HttpEntity; 44b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport org.apache.http.client.methods.HttpUriRequest; 45ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport org.apache.http.entity.ByteArrayEntity; 46ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 47ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Huimport java.io.IOException; 484427badd747b7c172934014b9f95a1be1256f35aMartin Hibdonimport java.util.ArrayList; 49ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 50ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu/** 51ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Base class for all Exchange operations that use a POST to talk to the server. 52ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * 53ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * The core of this class is {@link #performOperation}, which provides the skeleton of making 54ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * a request, handling common errors, and setting fields on the {@link SyncResult} if there is one. 55ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * This class abstracts the connection handling from its subclasses and callers. 56ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * 57ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * A subclass must implement the abstract functions below that create the request and parse the 58ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * response. There are also a set of functions that a subclass may override if it's substantially 59ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * different from the "normal" operation (e.g. most requests use the same request URI, but auto 60ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * discover deviates since it's not account-specific), but the default implementation should suffice 61ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * for most. The subclass must also define a public function which calls {@link #performOperation}, 62ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * possibly doing nothing other than that. (I chose to force subclasses to do this, rather than 63ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * provide that function in the base class, in order to force subclasses to consider, for example, 64ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * whether it needs a {@link SyncResult} parameter, and what the proper name for the "doWork" 65ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * function ought to be for the subclass.) 66ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 67ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hupublic abstract class EasOperation { 68ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu public static final String LOG_TAG = Eas.LOG_TAG; 69ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 70ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** The maximum number of server redirects we allow before returning failure. */ 71ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu private static final int MAX_REDIRECTS = 3; 72ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 73ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** Message MIME type for EAS version 14 and later. */ 74ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu private static final String EAS_14_MIME_TYPE = "application/vnd.ms-sync.wbxml"; 75ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 76328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** Error code indicating the operation was cancelled via {@link #abort}. */ 77328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu public static final int RESULT_ABORT = -1; 78328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** Error code indicating the operation was cancelled via {@link #restart}. */ 79328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu public static final int RESULT_RESTART = -2; 80328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** Error code indicating the Exchange servers redirected too many times. */ 81328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu public static final int RESULT_TOO_MANY_REDIRECTS = -3; 82328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** Error code indicating the request failed due to a network problem. */ 83328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu public static final int RESULT_REQUEST_FAILURE = -4; 84328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** Error code indicating a 403 (forbidden) error. */ 85328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu public static final int RESULT_FORBIDDEN = -5; 86328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** Error code indicating an unresolved provisioning error. */ 87328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu public static final int RESULT_PROVISIONING_ERROR = -6; 88328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** Error code indicating an authentication problem. */ 89328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu public static final int RESULT_AUTHENTICATION_ERROR = -7; 90b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu /** Error code indicating the client is missing a certificate. */ 91b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu public static final int RESULT_CLIENT_CERTIFICATE_REQUIRED = -8; 92b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu /** Error code indicating we don't have a protocol version in common with the server. */ 93b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu public static final int RESULT_PROTOCOL_VERSION_UNSUPPORTED = -9; 94328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** Error code indicating some other failure. */ 95b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu public static final int RESULT_OTHER_FAILURE = -10; 96328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 97ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected final Context mContext; 98328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 99328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** 100328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * The account id for this operation. 1016f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu * NOTE: You will be tempted to add a reference to the {@link Account} here. Resist. 1026f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu * It's too easy for that to lead to creep and stale data. 103328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu */ 1046f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu protected final long mAccountId; 105ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu private final EasServerConnection mConnection; 106ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 1071df4a493b2efa34fce4bd8a70aca57203b4ed037Yu Ping Hu // TODO: Make this private again when EasSyncHandler is converted to be a subclass. 1081df4a493b2efa34fce4bd8a70aca57203b4ed037Yu Ping Hu protected EasOperation(final Context context, final long accountId, 109b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu final EasServerConnection connection) { 110b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu mContext = context; 111b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu mAccountId = accountId; 112b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu mConnection = connection; 113b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 114b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu 115ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected EasOperation(final Context context, final Account account, final HostAuth hostAuth) { 116328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu this(context, account.mId, new EasServerConnection(context, account, hostAuth)); 117ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 118ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 119ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected EasOperation(final Context context, final Account account) { 120ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu this(context, account, HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv)); 121ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 122ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 123b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu /** 124b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * This constructor is for use by operations that are created by other operations, e.g. 125b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * {@link EasProvision}. 126b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * @param parentOperation The {@link EasOperation} that is creating us. 127b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu */ 128b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu protected EasOperation(final EasOperation parentOperation) { 129b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu this(parentOperation.mContext, parentOperation.mAccountId, parentOperation.mConnection); 130328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 131ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 132ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 133ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Request that this operation terminate. Intended for use by the sync service to interrupt 134ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * running operations, primarily Ping. 135ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 136ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu public final void abort() { 137ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu mConnection.stop(EasServerConnection.STOPPED_REASON_ABORT); 138ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 139ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 140ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 141ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Request that this operation restart. Intended for use by the sync service to interrupt 142ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * running operations, primarily Ping. 143ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 144ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu public final void restart() { 145ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu mConnection.stop(EasServerConnection.STOPPED_REASON_RESTART); 146ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 147ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 148ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 149ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * The skeleton of performing an operation. This function handles all the common code and 150ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * error handling, calling into virtual functions that are implemented or overridden by the 151ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * subclass to do the operation-specific logic. 152328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * 153328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * The result codes work as follows: 154328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * - Negative values indicate common error codes and are defined above (the various RESULT_* 155328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * constants). 156328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * - Non-negative values indicate the result of {@link #handleResponse}. These are obviously 157328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * specific to the subclass, and may indicate success or error conditions. 158328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * 159328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * The common error codes primarily indicate conditions that occur when performing the POST 160328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * itself, such as network errors and handling of the HTTP response. However, some errors that 161328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * can be indicated in the HTTP response code can also be indicated in the payload of the 162328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * response as well, so {@link #handleResponse} should in those cases return the appropriate 163328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * negative result code, which will be handled the same as if it had been indicated in the HTTP 164328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * response code. 165328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * 166ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @param syncResult If this operation is a sync, the {@link SyncResult} object that should 167ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * be written to for this sync; otherwise null. 168328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * @return A result code for the outcome of this operation, as described above. 169ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 170ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected final int performOperation(final SyncResult syncResult) { 171ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // We handle server redirects by looping, but we need to protect against too much looping. 172ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu int redirectCount = 0; 173ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 174ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu do { 175b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu // Perform the HTTP request and handle exceptions. 176ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu final EasResponse response; 177ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu try { 1780f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon if (registerClientCert()) { 1790f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon response = mConnection.executeHttpUriRequest(makeRequest(), getTimeout()); 1800f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon } else { 18139f4a323bc9dd497850e57971aabff97fb035111Paul Sliwowski LogUtils.e(LOG_TAG, "Problem registering client cert"); 1820f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon // TODO: Is this the best stat to increment? 1830f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon if (syncResult != null) { 1840f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon ++syncResult.stats.numAuthExceptions; 1850f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon } 1860f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon return RESULT_CLIENT_CERTIFICATE_REQUIRED; 1870f9116d3dfc7bc4210f551858006d4bd956d0cd3Martin Hibdon } 188ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } catch (final IOException e) { 189ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // If we were stopped, return the appropriate result code. 190ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu switch (mConnection.getStoppedReason()) { 191ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu case EasServerConnection.STOPPED_REASON_ABORT: 192ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return RESULT_ABORT; 193ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu case EasServerConnection.STOPPED_REASON_RESTART: 194ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return RESULT_RESTART; 195ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu default: 196ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu break; 197ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 198ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // If we're here, then we had a IOException that's not from a stop request. 199dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu String message = e.getMessage(); 200dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu if (message == null) { 201dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu message = "(no message)"; 202dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu } 203dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu LogUtils.i(LOG_TAG, "IOException while sending request: %s", message); 204ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu if (syncResult != null) { 205ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu ++syncResult.stats.numIoExceptions; 206ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 207ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return RESULT_REQUEST_FAILURE; 208ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } catch (final IllegalStateException e) { 209ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // Subclasses use ISE to signal a hard error when building the request. 2106f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu // TODO: Switch away from ISEs. 211ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu LogUtils.e(LOG_TAG, e, "Exception while sending request"); 212ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu if (syncResult != null) { 213ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu syncResult.databaseError = true; 214ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 215ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return RESULT_OTHER_FAILURE; 216ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 217ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 218ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // The POST completed, so process the response. 219ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu try { 220328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu final int result; 221ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // First off, the success case. 222ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu if (response.isSuccess()) { 223ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu try { 224328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu result = handleResponse(response, syncResult); 225328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (result >= 0) { 226328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return result; 227328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 228ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } catch (final IOException e) { 229ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu LogUtils.e(LOG_TAG, e, "Exception while handling response"); 230ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu if (syncResult != null) { 2312629e0f0eac5bb6c4d5982bdf65a989b03d46d3dAlon Albert ++syncResult.stats.numIoExceptions; 232ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 2332629e0f0eac5bb6c4d5982bdf65a989b03d46d3dAlon Albert return RESULT_REQUEST_FAILURE; 234ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 235328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } else { 236328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu result = RESULT_OTHER_FAILURE; 237ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 238ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 239328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // If this operation has distinct handling for 403 errors, do that. 240328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (result == RESULT_FORBIDDEN || (response.isForbidden() && handleForbidden())) { 241ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu LogUtils.e(LOG_TAG, "Forbidden response"); 242328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (syncResult != null) { 243328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // TODO: Is this the best stat to increment? 244328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu ++syncResult.stats.numAuthExceptions; 245328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 246328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return RESULT_FORBIDDEN; 247328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 248328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 249328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // Handle provisioning errors. 250328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (result == RESULT_PROVISIONING_ERROR || response.isProvisionError()) { 251328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (handleProvisionError(syncResult, mAccountId)) { 252328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // The provisioning error has been taken care of, so we should re-do this 253328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // request. 254328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu continue; 255328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 256328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (syncResult != null) { 25739f4a323bc9dd497850e57971aabff97fb035111Paul Sliwowski LogUtils.e(LOG_TAG, "Issue with provisioning"); 258328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // TODO: Is this the best stat to increment? 259328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu ++syncResult.stats.numAuthExceptions; 260328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 261328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return RESULT_PROVISIONING_ERROR; 262328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 263328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 264328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // Handle authentication errors. 265328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (response.isAuthError()) { 266ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu LogUtils.e(LOG_TAG, "Authentication error"); 267328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (syncResult != null) { 268328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu ++syncResult.stats.numAuthExceptions; 269328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 270b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu if (response.isMissingCertificate()) { 271b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu return RESULT_CLIENT_CERTIFICATE_REQUIRED; 272b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 273328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return RESULT_AUTHENTICATION_ERROR; 274ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 275ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 276328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // Handle redirects. 277328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (response.isRedirectError()) { 278328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu ++redirectCount; 279328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu mConnection.redirectHostAuth(response.getRedirectAddress()); 280328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // Note that unlike other errors, we do NOT return here; we just keep looping. 281328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } else { 282328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // All other errors. 2830c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu LogUtils.e(LOG_TAG, "Generic error for operation %s: status %d, result %d", 2840c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu getCommand(), response.getStatus(), result); 285ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu if (syncResult != null) { 286328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // TODO: Is this the best stat to increment? 287328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu ++syncResult.stats.numIoExceptions; 288ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 289ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return RESULT_OTHER_FAILURE; 290ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 291ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } finally { 292ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu response.close(); 293ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 294ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } while (redirectCount < MAX_REDIRECTS); 295ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 296328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu // Non-redirects return immediately after handling, so the only way to reach here is if we 297ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // looped too many times. 298ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu LogUtils.e(LOG_TAG, "Too many redirects"); 299ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu if (syncResult != null) { 300ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu syncResult.tooManyRetries = true; 301ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 302ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return RESULT_TOO_MANY_REDIRECTS; 303ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 304ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 305ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 306b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Reset the protocol version to use for this connection. If it's changed, and our account is 307b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * persisted, also write back the changes to the DB. 308b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * @param protocolVersion The new protocol version to use, as a string. 309ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 310b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu protected final void setProtocolVersion(final String protocolVersion) { 311b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu if (mConnection.setProtocolVersion(protocolVersion) && mAccountId != Account.NOT_SAVED) { 312b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu final Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId); 313b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu final ContentValues cv = new ContentValues(2); 314b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu if (getProtocolVersion() >= 12.0) { 315b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu final int oldFlags = Utility.getFirstRowInt(mContext, uri, 316b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu Account.ACCOUNT_FLAGS_PROJECTION, null, null, null, 317b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu Account.ACCOUNT_FLAGS_COLUMN_FLAGS, 0); 318b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu final int newFlags = oldFlags 319b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu | Account.FLAGS_SUPPORTS_GLOBAL_SEARCH + Account.FLAGS_SUPPORTS_SEARCH; 320b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu if (oldFlags != newFlags) { 321b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu cv.put(EmailContent.AccountColumns.FLAGS, newFlags); 322b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 323b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 324b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu cv.put(EmailContent.AccountColumns.PROTOCOL_VERSION, protocolVersion); 325b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu mContext.getContentResolver().update(uri, cv, null, null); 326b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 327b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 328ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 329b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu /** 330b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Create the request object for this operation. 331b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Most operations use a POST, but some use other request types (e.g. Options). 332b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * @return An {@link HttpUriRequest}. 333b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * @throws IOException 334b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu */ 335b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu private final HttpUriRequest makeRequest() throws IOException { 336b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu final String requestUri = getRequestUri(); 337b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu if (requestUri == null) { 338b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu return mConnection.makeOptions(); 339b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 340b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu return mConnection.makePost(requestUri, getRequestEntity(), 341b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu getRequestContentType(), addPolicyKeyHeaderToRequest()); 342ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 343ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 344ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 345ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * The following functions MUST be overridden by subclasses; these are things that are unique 346ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * to each operation. 347ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 348ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 349ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 350ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Get the name of the operation, used as the "Cmd=XXX" query param in the request URI. Note 3510c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu * that if you override {@link #getRequestUri}, then this function may be unused for normal 3520c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu * operation, but all subclasses should return something non-null for use with logging. 3530c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu * @return The name of the command for this operation as defined by the EAS protocol, or for 3540c899a81b9b6000a4dc2766415d6ead33641f679Yu Ping Hu * commands that don't need it, a suitable descriptive name for logging. 355ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 356ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected abstract String getCommand(); 357ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 358ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 359b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Build the {@link HttpEntity} which is used to construct the POST. Typically this function 360ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * will build the Exchange request using a {@link Serializer} and then call {@link #makeEntity}. 361b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * If the subclass is not using a POST, then it should override this to return null. 362ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @return The {@link HttpEntity} to pass to {@link EasServerConnection#makePost}. 363ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @throws IOException 364ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 365ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected abstract HttpEntity getRequestEntity() throws IOException; 366ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 367ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 368ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Parse the response from the Exchange perform whatever actions are dictated by that. 369ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @param response The {@link EasResponse} to our request. 370ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @param syncResult The {@link SyncResult} object for this operation, or null if we're not 371ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * handling a sync. 372328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * @return A result code. Non-negative values are returned directly to the caller; negative 373328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * values 374328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * 375328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * that is returned to the caller of {@link #performOperation}. 376ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @throws IOException 377ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 378ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected abstract int handleResponse(final EasResponse response, final SyncResult syncResult) 379ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu throws IOException; 380ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 381ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 382ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * The following functions may be overriden by a subclass, but most operations will not need 383ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * to do so. 384ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 385ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 386ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 387ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Get the URI for the Exchange server and this operation. Most (signed in) operations need 388ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * not override this; the notable operation that needs to override it is auto-discover. 389ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @return 390ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 391ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected String getRequestUri() { 392ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return mConnection.makeUriString(getCommand()); 393ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 394ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 395ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 396ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @return Whether to set the X-MS-PolicyKey header. Only Ping does not want this header. 397ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 398ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected boolean addPolicyKeyHeaderToRequest() { 399ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return true; 400ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 401ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 402ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 403ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @return The content type of this request. 404ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 405ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected String getRequestContentType() { 406ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return EAS_14_MIME_TYPE; 407ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 408ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 409ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 410ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @return The timeout to use for the POST. 411ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 412ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected long getTimeout() { 413ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return 30 * DateUtils.SECOND_IN_MILLIS; 414ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 415ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 416ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 417ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * If 403 responses should be handled in a special way, this function should be overridden to 418ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * do that. 419ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @return Whether we handle 403 responses; if false, then treat 403 as a provisioning error. 420ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 421ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected boolean handleForbidden() { 422ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return false; 423ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 424ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 425ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 426328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * Handle a provisioning error. Subclasses may override this to do something different, e.g. 427328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * to validate rather than actually do the provisioning. 428328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * @param syncResult 429328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * @param accountId 430328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * @return 431328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu */ 432328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu protected boolean handleProvisionError(final SyncResult syncResult, final long accountId) { 433b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu final EasProvision provisionOperation = new EasProvision(this); 434328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return provisionOperation.provision(syncResult, accountId); 435328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 436328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 437328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** 438328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * Convenience methods for subclasses to use. 439328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu */ 440328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 441328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** 442ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Convenience method to make an {@link HttpEntity} from {@link Serializer}. 443ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 444ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected final HttpEntity makeEntity(final Serializer s) { 445ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return new ByteArrayEntity(s.toByteArray()); 446ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 447ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 448328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** 449b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Check whether we should ask the server what protocol versions it supports and set this 450b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * account to use that version. 451b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * @return Whether we need a new protocol version from the server. 452b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu */ 453b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu protected final boolean shouldGetProtocolVersion() { 454b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu // TODO: Find conditions under which we should check other than not having one yet. 455b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu return !mConnection.isProtocolVersionSet(); 456b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 457b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu 458b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu /** 459328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * @return The protocol version to use. 460328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu */ 461ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected final double getProtocolVersion() { 462ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu return mConnection.getProtocolVersion(); 463ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 464ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 465ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 466328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * @return Our useragent. 467328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu */ 468328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu protected final String getUserAgent() { 469328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return mConnection.getUserAgent(); 470328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 471328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 472328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu /** 473b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * @return Whether we succeeeded in registering the client cert. 474b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu */ 475b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu protected final boolean registerClientCert() { 476b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu return mConnection.registerClientCert(); 477b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu } 478b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu 479b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu /** 480be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu * Add the device information to the current request. 481be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu * @param s The {@link Serializer} for our current request. 482be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu * @throws IOException 483be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu */ 484be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu protected final void addDeviceInformationToSerlializer(final Serializer s) throws IOException { 4852e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon final TelephonyManager tm = (TelephonyManager)mContext.getSystemService( 4862e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon Context.TELEPHONY_SERVICE); 4872e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon final String deviceId; 4882e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon final String phoneNumber; 4892e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon final String operator; 4902e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon if (tm != null) { 4912e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon deviceId = tm.getDeviceId(); 4922e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon phoneNumber = tm.getLine1Number(); 493a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu // TODO: This is not perfect and needs to be improved, for at least two reasons: 494a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu // 1) SIM cards can override this name. 495a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu // 2) We don't resend this info to the server when we change networks. 496a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu final String operatorName = tm.getNetworkOperatorName(); 497a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu final String operatorNumber = tm.getNetworkOperator(); 498a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu if (!TextUtils.isEmpty(operatorName) && !TextUtils.isEmpty(operatorNumber)) { 499a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu operator = operatorName + " (" + operatorNumber + ")"; 500a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu } else if (!TextUtils.isEmpty(operatorName)) { 501a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu operator = operatorName; 502a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu } else { 503a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu operator = operatorNumber; 504a75ec1c369a613ef7aad62a8ffe08707e91673f5Yu Ping Hu } 5052e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon } else { 5062e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon deviceId = null; 5072e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon phoneNumber = null; 5082e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon operator = null; 5092e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon } 5102e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon 5112e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon // TODO: Right now, we won't send this information unless the device is provisioned again. 5122e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon // Potentially, this means that our phone number could be out of date if the user 5132e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon // switches sims. Is there something we can do to force a reprovision? 514be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu s.start(Tags.SETTINGS_DEVICE_INFORMATION).start(Tags.SETTINGS_SET); 515be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu s.data(Tags.SETTINGS_MODEL, Build.MODEL); 5162e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon if (deviceId != null) { 5172e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon s.data(Tags.SETTINGS_IMEI, tm.getDeviceId()); 5182e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon } 51970755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu // Set the device friendly name, if we have one. 52070755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu // TODO: Longer term, this should be done without a provider call. 52170755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu final Bundle bundle = mContext.getContentResolver().call( 52270755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu EmailContent.CONTENT_URI, EmailContent.DEVICE_FRIENDLY_NAME, null, null); 52370755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu if (bundle != null) { 52470755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu final String friendlyName = bundle.getString(EmailContent.DEVICE_FRIENDLY_NAME); 52570755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu if (!TextUtils.isEmpty(friendlyName)) { 52670755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu s.data(Tags.SETTINGS_FRIENDLY_NAME, friendlyName); 52770755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu } 52870755e7a47c2199e6f047d5736ad7b63111aae82Yu Ping Hu } 529be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu s.data(Tags.SETTINGS_OS, "Android " + Build.VERSION.RELEASE); 5302e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon if (phoneNumber != null) { 5312e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon s.data(Tags.SETTINGS_PHONE_NUMBER, phoneNumber); 5322e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon } 5332e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon // TODO: Consider setting this, but make sure we know what it's used for. 5342e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon // If the user changes the device's locale and we don't do a reprovision, the server's 5352e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon // idea of the language will be wrong. Since we're not sure what this is used for, 5362e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon // right now we're leaving it out. 5372e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon //s.data(Tags.SETTINGS_OS_LANGUAGE, Locale.getDefault().getDisplayLanguage()); 538be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu s.data(Tags.SETTINGS_USER_AGENT, getUserAgent()); 5392e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon if (operator != null) { 5402e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon s.data(Tags.SETTINGS_MOBILE_OPERATOR, operator); 5412e47c34cba847b47992420f8634ae51dddc90cd5Martin Hibdon } 542be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu s.end().end(); // SETTINGS_SET, SETTINGS_DEVICE_INFORMATION 543be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu } 544be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu 545be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu /** 546ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Convenience method for adding a Message to an account's outbox 547ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @param account The {@link Account} from which to send the message. 548ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @param msg the message to send 549ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 550328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu protected final void sendMessage(final Account account, final EmailContent.Message msg) { 551ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu long mailboxId = Mailbox.findMailboxOfType(mContext, account.mId, Mailbox.TYPE_OUTBOX); 552ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // TODO: Improve system mailbox handling. 553ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu if (mailboxId == Mailbox.NO_MAILBOX) { 554ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu LogUtils.d(LOG_TAG, "No outbox for account %d, creating it", account.mId); 555ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu final Mailbox outbox = 556ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu Mailbox.newSystemMailbox(mContext, account.mId, Mailbox.TYPE_OUTBOX); 557ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu outbox.save(mContext); 558ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu mailboxId = outbox.mId; 559ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 560ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu msg.mMailboxKey = mailboxId; 561ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu msg.mAccountKey = account.mId; 562ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu msg.save(mContext); 563ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu requestSyncForMailbox(new android.accounts.Account(account.mEmailAddress, 5644427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mailboxId); 565ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 566ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 567ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu /** 568ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Issue a {@link android.content.ContentResolver#requestSync} for a specific mailbox. 569ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @param amAccount The {@link android.accounts.Account} for the account we're pinging. 570ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * @param mailboxId The id of the mailbox that needs to sync. 571ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 572ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu protected static void requestSyncForMailbox(final android.accounts.Account amAccount, 5734427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon final long mailboxId) { 5744427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon final Bundle extras = Mailbox.createSyncBundle(mailboxId); 5754427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras); 5764427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon LogUtils.i(LOG_TAG, "requestSync EasOperation requestSyncForMailbox %s, %s", 5774427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon amAccount.toString(), extras.toString()); 5784427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon } 5794427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon 5804427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon protected static void requestSyncForMailboxes(final android.accounts.Account amAccount, 5814427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon final ArrayList<Long> mailboxIds) { 5824427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon final Bundle extras = Mailbox.createSyncBundle(mailboxIds); 5834427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras); 5844427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon LogUtils.i(LOG_TAG, "requestSync EasOperation requestSyncForMailboxes %s, %s", 5854427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon amAccount.toString(), extras.toString()); 5864427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon } 5874427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon 5884427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon /** 5894427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon * RequestNoOpSync 5904427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon * This requests a sync for a particular authority purely so that that account 5914427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon * in settings will recognize that it is trying to sync, and will display the 5924427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon * appropriate UI. In fact, all exchange data syncing actually happens through the 5934427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon * EmailSyncAdapterService. 5944427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon * @param amAccount 5954427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon * @param authority 5964427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon */ 5974427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon protected static void requestNoOpSync(final android.accounts.Account amAccount, 5984427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon final String authority) { 599ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu final Bundle extras = new Bundle(1); 6004427badd747b7c172934014b9f95a1be1256f35aMartin Hibdon extras.putBoolean(Mailbox.SYNC_EXTRA_NOOP, true); 601ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu ContentResolver.requestSync(amAccount, authority, extras); 6023eef378426c7c88608f53f5a268baed40259ccf6Alon Albert LogUtils.d(LOG_TAG, "requestSync EasOperation requestNoOpSync %s, %s", 6039c7165d4c6b90101b781f90b17451efd42a17929Martin Hibdon amAccount.toString(), extras.toString()); 604ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } 605ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu} 606