EasServerConnection.java revision 9c7165d4c6b90101b781f90b17451efd42a17929
1b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu/*
2b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Copyright (C) 2013 The Android Open Source Project
3b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu *
4b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Licensed under the Apache License, Version 2.0 (the "License");
5b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * you may not use this file except in compliance with the License.
6b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * You may obtain a copy of the License at
7b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu *
8b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu *      http://www.apache.org/licenses/LICENSE-2.0
9b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu *
10b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Unless required by applicable law or agreed to in writing, software
11b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * distributed under the License is distributed on an "AS IS" BASIS,
12b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * See the License for the specific language governing permissions and
14b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * limitations under the License.
15b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu */
16b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
17b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hupackage com.android.exchange.service;
18b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
1962c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Huimport android.content.ContentResolver;
20328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport android.content.ContentUris;
21c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Huimport android.content.ContentValues;
22b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.content.Context;
23b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.net.Uri;
24b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.os.Build;
2562c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Huimport android.os.Bundle;
26b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.text.TextUtils;
27b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.text.format.DateUtils;
28b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.util.Base64;
29b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
30ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Huimport com.android.emailcommon.internet.MimeUtility;
31b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.emailcommon.provider.Account;
32c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Huimport com.android.emailcommon.provider.EmailContent;
33b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.emailcommon.provider.HostAuth;
34b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Huimport com.android.emailcommon.provider.Mailbox;
35f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Huimport com.android.emailcommon.service.AccountServiceProxy;
36b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.emailcommon.utility.EmailClientConnectionManager;
37328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport com.android.emailcommon.utility.Utility;
38b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.exchange.Eas;
39b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.exchange.EasResponse;
409383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport com.android.exchange.eas.EasConnectionCache;
416ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.exchange.utility.CurlLogger;
42c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Huimport com.android.mail.utils.LogUtils;
43b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
44b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.HttpEntity;
45b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.client.HttpClient;
46bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Huimport org.apache.http.client.methods.HttpOptions;
47b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.client.methods.HttpPost;
48b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport org.apache.http.client.methods.HttpUriRequest;
49b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.entity.ByteArrayEntity;
50b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.impl.client.DefaultHttpClient;
51b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.params.BasicHttpParams;
52b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.params.HttpConnectionParams;
53b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.params.HttpParams;
541d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albertimport org.apache.http.protocol.BasicHttpProcessor;
55b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
56b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport java.io.IOException;
57b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport java.net.URI;
58b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport java.security.cert.CertificateException;
59b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
60b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu/**
61b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu * Base class for communicating with an EAS server. Anything that needs to send messages to the
62b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu * server can subclass this to get access to the {@link #sendHttpClientPost} family of functions.
63c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * TODO: This class has a regrettable name. It's not a connection, but rather a task that happens
64c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * to have (and use) a connection to the server.
65b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu */
66ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hupublic class EasServerConnection {
67c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu    /** Logging tag. */
68110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert    private static final String TAG = Eas.LOG_TAG;
69c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu
70b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    /**
71b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * Timeout for establishing a connection to the server.
72b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
73b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    private static final long CONNECTION_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;
74b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
75b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    /**
76b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * Timeout for http requests after the connection has been established.
77b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
78b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    protected static final long COMMAND_TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
79b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
80b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    private static final String DEVICE_TYPE = "Android";
81328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    private static final String USER_AGENT = DEVICE_TYPE + '/' + Build.VERSION.RELEASE + '-' +
82b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        Eas.CLIENT_VERSION;
83b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
84ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /** Message MIME type for EAS version 14 and later. */
85ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    private static final String EAS_14_MIME_TYPE = "application/vnd.ms-sync.wbxml";
86b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
87ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    /**
88ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * Value for {@link #mStoppedReason} when we haven't been stopped.
89ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     */
90ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    public static final int STOPPED_REASON_NONE = 0;
91ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu
92ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    /**
93ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * Passed to {@link #stop} to indicate that this stop request should terminate this task.
94ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     */
95ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    public static final int STOPPED_REASON_ABORT = 1;
96ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu
97ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    /**
98ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * Passed to {@link #stop} to indicate that this stop request should restart this task (e.g. in
99ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * order to reload parameters).
100ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     */
101ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    public static final int STOPPED_REASON_RESTART = 2;
102ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu
103328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    private static final String[] ACCOUNT_SECURITY_KEY_PROJECTION =
104328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            { EmailContent.AccountColumns.SECURITY_SYNC_KEY };
105328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
106f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu    private static String sDeviceId = null;
107f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu
108b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    protected final Context mContext;
1090a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu    // TODO: Make this private if possible. Subclasses must be careful about altering the HostAuth
1100a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu    // to not screw up any connection caching (use redirectHostAuth).
11125b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    protected final HostAuth mHostAuth;
1120a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu    protected final Account mAccount;
113328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    private final long mAccountId;
114b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
115b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    // Bookkeeping for interrupting a request. This is primarily for use by Ping (there's currently
116b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    // no mechanism for stopping a sync).
117b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    // Access to these variables should be synchronized on this.
118b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    private HttpUriRequest mPendingRequest = null;
119b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    private boolean mStopped = false;
120ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    private int mStoppedReason = STOPPED_REASON_NONE;
121b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
122b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** The protocol version to use, as a double. */
123ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private double mProtocolVersion = 0.0d;
124b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** Whether {@link #setProtocolVersion} was last called with a non-null value. */
125b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    private boolean mProtocolVersionIsSet = false;
126d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu
127ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /**
128ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * The client for any requests made by this object. This is created lazily, and cleared
129ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * whenever our host auth is redirected.
130ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     */
131ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    private HttpClient mClient;
132ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
133ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /**
134ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * The connection manager for any requests made by this object. This is created lazily, and
135ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * cleared whenever our host auth is redirected.
136ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     */
137ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    private EmailClientConnectionManager mConnectionManager;
138ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
139ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public EasServerConnection(final Context context, final Account account,
1400a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu            final HostAuth hostAuth) {
141b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        mContext = context;
14225b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        mHostAuth = hostAuth;
1430a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu        mAccount = account;
144328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        mAccountId = account.mId;
145ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        setProtocolVersion(account.mProtocolVersion);
146b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
147b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
148ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public EasServerConnection(final Context context, final Account account) {
149c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu        this(context, account, HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv));
150c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu    }
151c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu
15225b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    protected EmailClientConnectionManager getClientConnectionManager() {
153ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        if (mConnectionManager == null) {
1549383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu            mConnectionManager =
1559383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu                    EasConnectionCache.instance().getConnectionManager(mContext, mHostAuth);
156ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        }
157ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return mConnectionManager;
15897f38cd6004b54afe9a949658d9962abe8f1f24eYu Ping Hu    }
15997f38cd6004b54afe9a949658d9962abe8f1f24eYu Ping Hu
160ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public void redirectHostAuth(final String newAddress) {
161ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        mClient = null;
162ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        mConnectionManager = null;
16325b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        mHostAuth.mAddress = newAddress;
164c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu        if (mHostAuth.isSaved()) {
1659383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu            EasConnectionCache.instance().uncacheConnectionManager(mHostAuth);
166c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu            final ContentValues cv = new ContentValues(1);
167c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu            cv.put(EmailContent.HostAuthColumns.ADDRESS, newAddress);
168c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu            mHostAuth.update(mContext, cv);
169c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu        }
170b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
171b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
172ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    private HttpClient getHttpClient(final long timeout) {
173ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        if (mClient == null) {
174ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            final HttpParams params = new BasicHttpParams();
175ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            HttpConnectionParams.setConnectionTimeout(params, (int)(CONNECTION_TIMEOUT));
176ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            HttpConnectionParams.setSoTimeout(params, (int)(timeout));
177ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            HttpConnectionParams.setSocketBufferSize(params, 8192);
1781d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert            mClient = new DefaultHttpClient(getClientConnectionManager(), params) {
1799c7165d4c6b90101b781f90b17451efd42a17929Martin Hibdon                @Override
1801d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                protected BasicHttpProcessor createHttpProcessor() {
1811d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                    final BasicHttpProcessor processor = super.createHttpProcessor();
1821d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                    processor.addRequestInterceptor(new CurlLogger());
1831d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                    return processor;
1841d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                }
1851d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert            };
186ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        }
187ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return mClient;
188b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
189b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
19025b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    private String makeAuthString() {
19125b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        final String cs = mHostAuth.mLogin + ":" + mHostAuth.mPassword;
192b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        return "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
193b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
194b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
19525b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    private String makeUserString() {
196f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu        if (sDeviceId == null) {
197f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu            sDeviceId = new AccountServiceProxy(mContext).getDeviceId();
198f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu            if (sDeviceId == null) {
199f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu                LogUtils.e(TAG, "Could not get device id, defaulting to '0'");
200f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu                sDeviceId = "0";
201f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu            }
202b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
20325b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        return "&User=" + Uri.encode(mHostAuth.mLogin) + "&DeviceId=" +
204f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu                sDeviceId + "&DeviceType=" + DEVICE_TYPE;
205b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
206b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
20725b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    private String makeBaseUriString() {
20825b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        return EmailClientConnectionManager.makeScheme(mHostAuth.shouldUseSsl(),
20925b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu                mHostAuth.shouldTrustAllServerCerts(), mHostAuth.mClientCertAlias) +
21025b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu                "://" + mHostAuth.mAddress + "/Microsoft-Server-ActiveSync";
211b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
212b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
213ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public String makeUriString(final String cmd) {
21425b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        String uriString = makeBaseUriString();
215b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        if (cmd != null) {
21625b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu            uriString += "?Cmd=" + cmd + makeUserString();
217b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
218b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        return uriString;
219b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
220b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
221ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private String makeUriString(final String cmd, final String extra) {
222ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return makeUriString(cmd) + extra;
223b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
224b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
225b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    /**
226d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu     * If a sync causes us to update our protocol version, this function must be called so that
227d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu     * subsequent calls to {@link #getProtocolVersion()} will do the right thing.
228b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether the protocol version changed.
229d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu     */
230b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public boolean setProtocolVersion(String protocolVersionString) {
231be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        mProtocolVersionIsSet = (protocolVersionString != null);
232ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (protocolVersionString == null) {
233ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            protocolVersionString = Eas.DEFAULT_PROTOCOL_VERSION;
234ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        }
235b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final double oldProtocolVersion = mProtocolVersion;
236ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        mProtocolVersion = Eas.getProtocolVersionDouble(protocolVersionString);
237b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return (oldProtocolVersion != mProtocolVersion);
238d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu    }
239d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu
240d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu    /**
241ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The protocol version for this connection.
242d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu     */
243ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public double getProtocolVersion() {
244ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return mProtocolVersion;
245d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu    }
246d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu
247d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu    /**
248328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return The useragent string for our client.
249328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
250328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public final String getUserAgent() {
251328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        return USER_AGENT;
252328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
253328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
254328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
255ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * Send an http OPTIONS request to server.
256ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @return The {@link EasResponse} from the Exchange server.
257ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @throws IOException
258b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
259ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    protected EasResponse sendHttpClientOptions() throws IOException {
260ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        // For OPTIONS, just use the base string and the single header
261ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        final HttpOptions method = new HttpOptions(URI.create(makeBaseUriString()));
26225b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        method.setHeader("Authorization", makeAuthString());
263328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        method.setHeader("User-Agent", getUserAgent());
264ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return EasResponse.fromHttpRequest(getClientConnectionManager(),
265ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu                getHttpClient(COMMAND_TIMEOUT), method);
266ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
267ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
268ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    protected void resetAuthorization(final HttpPost post) {
269ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.removeHeaders("Authorization");
270ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.setHeader("Authorization", makeAuthString());
271ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
272ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
273ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /**
274ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * Make an {@link HttpPost} for a specific request.
275ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param uri The uri for this request, as a {@link String}.
276ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param entity The {@link HttpEntity} for this request.
277ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param contentType The Content-Type for this request.
278ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param usePolicyKey Whether or not a policy key should be sent.
279ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @return
280ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     */
281ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public HttpPost makePost(final String uri, final HttpEntity entity, final String contentType,
282ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            final boolean usePolicyKey) {
283ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        final HttpPost post = new HttpPost(uri);
284ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.setHeader("Authorization", makeAuthString());
285ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        post.setHeader("MS-ASProtocolVersion", String.valueOf(mProtocolVersion));
286328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        post.setHeader("User-Agent", getUserAgent());
287ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.setHeader("Accept-Encoding", "gzip");
288ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        if (contentType != null) {
289ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            post.setHeader("Content-Type", contentType);
290ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        }
291b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        if (usePolicyKey) {
292b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            // If there's an account in existence, use its key; otherwise (we're creating the
293b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            // account), send "0".  The server will respond with code 449 if there are policies
294b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            // to be enforced
2950a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu            final String key;
296328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            final String accountKey;
297328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            if (mAccountId == Account.NO_ACCOUNT) {
298328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                accountKey = null;
299328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            } else {
300328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu               accountKey = Utility.getFirstRowString(mContext,
301328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId),
302328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        ACCOUNT_SECURITY_KEY_PROJECTION, null, null, null, 0);
303328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            }
3040a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu            if (!TextUtils.isEmpty(accountKey)) {
3050a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu                key = accountKey;
3060a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu            } else {
3070a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu                key = "0";
308b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            }
309ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            post.setHeader("X-MS-PolicyKey", key);
310b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
311ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.setEntity(entity);
312ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return post;
313bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu    }
314bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu
315bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu    /**
316b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Make an {@link HttpOptions} request for this connection.
317b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return The {@link HttpOptions} object.
318b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
319b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public HttpOptions makeOptions() {
320b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final HttpOptions method = new HttpOptions(URI.create(makeBaseUriString()));
321b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        method.setHeader("Authorization", makeAuthString());
322b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        method.setHeader("User-Agent", getUserAgent());
323b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return method;
324b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
325b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
326b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
327b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * Send a POST request to the server.
328b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @param cmd The command we're sending to the server.
329b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @param entity The {@link HttpEntity} containing the payload of the message.
330b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @param timeout The timeout for this POST.
331b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @return The response from the Exchange server.
332b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @throws IOException
333b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
3340a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu    protected EasResponse sendHttpClientPost(String cmd, final HttpEntity entity,
3350a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu            final long timeout) throws IOException {
336b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        final boolean isPingCommand = cmd.equals("Ping");
337b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
338b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // Split the mail sending commands
339b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        String extra = null;
340b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        boolean msg = false;
341b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) {
342b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            final int cmdLength = cmd.indexOf('&');
343b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            extra = cmd.substring(cmdLength);
344b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            cmd = cmd.substring(0, cmdLength);
345b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            msg = true;
346b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        } else if (cmd.startsWith("SendMail&")) {
347b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            msg = true;
348b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
349b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
350b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // Send the proper Content-Type header; it's always wbxml except for messages when
351b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // the EAS protocol version is < 14.0
352b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // If entity is null (e.g. for attachments), don't set this header
353ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        final String contentType;
354d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu        if (msg && (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE)) {
355ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            contentType = MimeUtility.MIME_TYPE_RFC822;
356b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        } else if (entity != null) {
357ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            contentType = EAS_14_MIME_TYPE;
358b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
359ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        else {
360ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            contentType = null;
361ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        }
362ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        final String uriString;
363ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (extra == null) {
364ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            uriString = makeUriString(cmd);
365ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        } else {
366ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            uriString = makeUriString(cmd, extra);
367ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        }
368ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        final HttpPost method = makePost(uriString, entity, contentType, !isPingCommand);
369b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // NOTE
370b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // The next lines are added at the insistence of $VENDOR, who is seeing inappropriate
371b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // network activity related to the Ping command on some networks with some servers.
372b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // This code should be removed when the underlying issue is resolved
373b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        if (isPingCommand) {
374b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            method.setHeader("Connection", "close");
375b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
376b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return executeHttpUriRequest(method, timeout);
377ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
378b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
379ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public EasResponse sendHttpClientPost(final String cmd, final byte[] bytes,
380ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            final long timeout) throws IOException {
381da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        final ByteArrayEntity entity;
382da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        if (bytes == null) {
383da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu            entity = null;
384da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        } else {
385da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu            entity = new ByteArrayEntity(bytes);
386da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        }
387da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        return sendHttpClientPost(cmd, entity, timeout);
388ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
389ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
390ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    protected EasResponse sendHttpClientPost(final String cmd, final byte[] bytes)
391ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            throws IOException {
392ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return sendHttpClientPost(cmd, bytes, COMMAND_TIMEOUT);
393ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
394ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
395ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /**
396b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Executes an {@link HttpUriRequest}.
397ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * Note: this function must not be called by multiple threads concurrently. Only one thread may
398ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * send server requests from a particular object at a time.
399ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param method The post to execute.
400ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param timeout The timeout to use.
401ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @return The response from the Exchange server.
402ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @throws IOException
403ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     */
404b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public EasResponse executeHttpUriRequest(final HttpUriRequest method, final long timeout)
405ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            throws IOException {
406ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        // The synchronized blocks are here to support the stop() function, specifically to handle
407ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        // when stop() is called first. Notably, they are NOT here in order to guard against
408ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        // concurrent access to this function, which is not supported.
409b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        synchronized (this) {
410b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            if (mStopped) {
411b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu                mStopped = false;
412b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu                // If this gets stopped after the POST actually starts, it throws an IOException.
413b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu                // Therefore if we get stopped here, let's throw the same sort of exception, so
414ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // callers can equate IOException with "this POST got killed for some reason".
415ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu                throw new IOException("Command was stopped before POST");
416b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            }
417b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu           mPendingRequest = method;
418b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
419ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        boolean postCompleted = false;
420b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        try {
421ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            final EasResponse response = EasResponse.fromHttpRequest(getClientConnectionManager(),
422ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    getHttpClient(timeout), method);
423ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            postCompleted = true;
424ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            return response;
425b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        } finally {
426b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            synchronized (this) {
427b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                mPendingRequest = null;
428ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                if (postCompleted) {
429ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    mStoppedReason = STOPPED_REASON_NONE;
430ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
431b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            }
432b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
433b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
434b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
435ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    protected EasResponse executePost(final HttpPost method) throws IOException {
436b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return executeHttpUriRequest(method, COMMAND_TIMEOUT);
437b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
438b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
439b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    /**
440ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * If called while this object is executing a POST, interrupt it with an {@link IOException}.
441ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * Otherwise cause the next attempt to execute a POST to be interrupted with an
442ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * {@link IOException}.
443ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * @param reason The reason for requesting a stop. This should be one of the STOPPED_REASON_*
444ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     *               constants defined in this class, other than {@link #STOPPED_REASON_NONE} which
445ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     *               is used to signify that no stop has occurred.
446ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     *               This class simply stores the value; subclasses are responsible for checking
447ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     *               this value when catching the {@link IOException} and responding appropriately.
448b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
449ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public synchronized void stop(final int reason) {
450ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // Only process legitimate reasons.
451ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (reason >= STOPPED_REASON_ABORT && reason <= STOPPED_REASON_RESTART) {
452b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            final boolean isMidPost = (mPendingRequest != null);
453ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            LogUtils.i(TAG, "%s with reason %d", (isMidPost ? "Interrupt" : "Stop next"), reason);
454ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            mStoppedReason = reason;
455ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            if (isMidPost) {
456b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                mPendingRequest.abort();
457ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } else {
458ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                mStopped = true;
459ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            }
460b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
461ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    }
462ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu
463ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    /**
464ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * @return The reason supplied to the last call to {@link #stop}, or
465ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     *         {@link #STOPPED_REASON_NONE} if {@link #stop} hasn't been called since the last
466ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     *         successful POST.
467ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     */
468ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public synchronized int getStoppedReason() {
469ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu        return mStoppedReason;
470b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
471b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu
472b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu    /**
473b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Try to register our client certificate, if needed.
474b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return True if we succeeded or didn't need a client cert, false if we failed to register it.
475b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
476b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public boolean registerClientCert() {
477b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        if (mHostAuth.mClientCertAlias != null) {
478b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            try {
479b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                getClientConnectionManager().registerClientCert(mContext, mHostAuth);
480b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            } catch (final CertificateException e) {
481b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                // The client certificate the user specified is invalid/inaccessible.
482b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                return false;
483b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            }
484b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        }
485b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return true;
486b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
487b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
488b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
489b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether {@link #setProtocolVersion} was last called with a non-null value. Note that
490b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     *         at construction time it is set to whatever protocol version is in the account.
491b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
492b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public boolean isProtocolVersionSet() {
493b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return mProtocolVersionIsSet;
494b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
495b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
496b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
497b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu     * Convenience method for adding a Message to an account's outbox
498ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param account The {@link Account} from which to send the message.
499ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @param msg The message to send
500b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu     */
501ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    protected void sendMessage(final Account account, final EmailContent.Message msg) {
502ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        long mailboxId = Mailbox.findMailboxOfType(mContext, account.mId, Mailbox.TYPE_OUTBOX);
50333620ab2d10ba408d69a2daa177ad949d4b46f62Yu Ping Hu        // TODO: Improve system mailbox handling.
50433620ab2d10ba408d69a2daa177ad949d4b46f62Yu Ping Hu        if (mailboxId == Mailbox.NO_MAILBOX) {
505ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            LogUtils.d(TAG, "No outbox for account %d, creating it", account.mId);
50633620ab2d10ba408d69a2daa177ad949d4b46f62Yu Ping Hu            final Mailbox outbox =
507ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    Mailbox.newSystemMailbox(mContext, account.mId, Mailbox.TYPE_OUTBOX);
50833620ab2d10ba408d69a2daa177ad949d4b46f62Yu Ping Hu            outbox.save(mContext);
50933620ab2d10ba408d69a2daa177ad949d4b46f62Yu Ping Hu            mailboxId = outbox.mId;
510b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu        }
51133620ab2d10ba408d69a2daa177ad949d4b46f62Yu Ping Hu        msg.mMailboxKey = mailboxId;
512ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        msg.mAccountKey = account.mId;
51333620ab2d10ba408d69a2daa177ad949d4b46f62Yu Ping Hu        msg.save(mContext);
514ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        requestSyncForMailbox(new android.accounts.Account(account.mEmailAddress,
51533620ab2d10ba408d69a2daa177ad949d4b46f62Yu Ping Hu                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), EmailContent.AUTHORITY, mailboxId);
516b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu    }
517b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu
51862c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu    /**
51962c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu     * Issue a {@link android.content.ContentResolver#requestSync} for a specific mailbox.
52062c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu     * @param amAccount The {@link android.accounts.Account} for the account we're pinging.
52162c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu     * @param authority The authority for the mailbox that needs to sync.
52262c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu     * @param mailboxId The id of the mailbox that needs to sync.
52362c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu     */
52462c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu    protected static void requestSyncForMailbox(final android.accounts.Account amAccount,
52562c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu            final String authority, final long mailboxId) {
52662c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu        final Bundle extras = new Bundle(1);
52762c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu        extras.putLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, mailboxId);
52862c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu        ContentResolver.requestSync(amAccount, authority, extras);
5299c7165d4c6b90101b781f90b17451efd42a17929Martin Hibdon        LogUtils.i(TAG, "requestSync EasServerConnection requestSyncForMailbox %s, %s",
5309c7165d4c6b90101b781f90b17451efd42a17929Martin Hibdon                amAccount.toString(), extras.toString());
53162c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu    }
532b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu}
533