1ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/* 2ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Copyright (C) 2008-2009 Marc Blank 3ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed to The Android Open Source Project. 4ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 5ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 6ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * you may not use this file except in compliance with the License. 7ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * You may obtain a copy of the License at 8ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 9ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 10ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 11ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Unless required by applicable law or agreed to in writing, software 12ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 13ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * See the License for the specific language governing permissions and 15ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * limitations under the License. 16ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 17ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 18ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpackage com.android.exchange; 19ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 20d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.content.ContentResolver; 21d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.content.ContentUris; 22d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.content.ContentValues; 23d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.content.Context; 24d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.content.Entity; 25d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.database.Cursor; 26c0dce2284e50f2b638bad8c1fa6e2028e46ea5d9Marc Blankimport android.net.TrafficStats; 27d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.net.Uri; 28d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.os.Build; 29d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.os.Bundle; 30d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.os.RemoteException; 31d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.provider.CalendarContract.Attendees; 32d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.provider.CalendarContract.Events; 33d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.text.TextUtils; 34847322e4b75287643a8381e28401ee5cf4945a44Yu Ping Huimport android.text.format.DateUtils; 35d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.util.Base64; 36d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blankimport android.util.Xml; 37d586a4a9e631e79abad807f2bb328a3856e9eb23Marc Blank 38c0dce2284e50f2b638bad8c1fa6e2028e46ea5d9Marc Blankimport com.android.emailcommon.TrafficFlags; 39c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.Address; 40c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.MeetingInfo; 41c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.MessagingException; 42c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.PackedString; 437372782488977df778a33d990401ce9e397f646bMarc Blankimport com.android.emailcommon.provider.Account; 44c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns; 45c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Message; 46c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.MessageColumns; 47c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.SyncColumns; 48c936e6dc6b6979350083a8ab28a24ba8cdda45e0Ben Komaloimport com.android.emailcommon.provider.HostAuth; 494d8774462ace9a45154b2df418b9f2fe7a9c685dBen Komaloimport com.android.emailcommon.provider.Mailbox; 503ba50d433ee89b252cd1dd9fd3eee1f2baa3945bMarc Blankimport com.android.emailcommon.provider.Policy; 510164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blankimport com.android.emailcommon.provider.ProviderUnavailableException; 5258dac270fd3a2fb1ff6cb5287ef9b61ea5080e41Marc Blankimport com.android.emailcommon.service.EmailServiceConstants; 5358dac270fd3a2fb1ff6cb5287ef9b61ea5080e41Marc Blankimport com.android.emailcommon.service.EmailServiceProxy; 5458dac270fd3a2fb1ff6cb5287ef9b61ea5080e41Marc Blankimport com.android.emailcommon.service.EmailServiceStatus; 55e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport com.android.emailcommon.service.PolicyServiceProxy; 562bf91acb8174aab82582ce975a4b60702c810a9cMarc Blankimport com.android.emailcommon.utility.EmailClientConnectionManager; 572bf91acb8174aab82582ce975a4b60702c810a9cMarc Blankimport com.android.emailcommon.utility.Utility; 58e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport com.android.emailsync.AbstractSyncService; 59e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport com.android.emailsync.PartRequest; 60e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport com.android.emailsync.Request; 6177186bb1a174432ef272584374942d8b9228e39cMarc Blankimport com.android.exchange.CommandStatusException.CommandStatus; 627c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.AbstractSyncAdapter; 6348af7392c82262d17700e3fbdccf3a582809d449Marc Blankimport com.android.exchange.adapter.AccountSyncAdapter; 64bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blankimport com.android.exchange.adapter.AttachmentLoader; 657c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.EmailSyncAdapter; 667c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.FolderSyncParser; 6796bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadlerimport com.android.exchange.adapter.GalParser; 687531be7774769c84b499b1de5dc46da3a9468316Marc Blankimport com.android.exchange.adapter.MeetingResponseParser; 694471a6960d352242cc65bddf7888cc5335840c74Marc Blankimport com.android.exchange.adapter.MoveItemsParser; 70bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blankimport com.android.exchange.adapter.Parser.EmptyStreamException; 718692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blankimport com.android.exchange.adapter.ProvisionParser; 727c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.Serializer; 738b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blankimport com.android.exchange.adapter.SettingsParser; 747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.Tags; 7596bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadlerimport com.android.exchange.provider.GalResult; 765c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.exchange.utility.CalendarUtilities; 776ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.exchange.utility.CurlLogger; 78942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedyimport com.android.mail.utils.LogUtils; 79538ffdd571446a24d1436b5931c9f112ec200b51Marc Blankimport com.google.common.annotations.VisibleForTesting; 80ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 818047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.Header; 8200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.HttpEntity; 8300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.HttpResponse; 841b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blankimport org.apache.http.HttpStatus; 858047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.HttpClient; 868047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.methods.HttpOptions; 8700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.client.methods.HttpPost; 888047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.methods.HttpRequestBase; 898047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.entity.ByteArrayEntity; 904d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.apache.http.entity.StringEntity; 9100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.impl.client.DefaultHttpClient; 928047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.BasicHttpParams; 938047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.HttpConnectionParams; 948047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.HttpParams; 956ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport org.apache.http.protocol.BasicHttpProcessor; 964d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.xmlpull.v1.XmlPullParser; 974d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.xmlpull.v1.XmlPullParserException; 984d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.xmlpull.v1.XmlPullParserFactory; 994d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.xmlpull.v1.XmlSerializer; 10000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 1014d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport java.io.ByteArrayOutputStream; 10200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException; 10300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream; 1045b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blankimport java.lang.Thread.State; 10500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URI; 106e44d5875af006f4217718a1c0fc0e235af3863afMarc Blankimport java.security.cert.CertificateException; 10700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 108ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankpublic class EasSyncService extends AbstractSyncService { 1099b0ebbbe8e94b8da8b95ef4a84d76ab1e8f6a676Marc Blank // DO NOT CHECK IN SET TO TRUE 1109b0ebbbe8e94b8da8b95ef4a84d76ab1e8f6a676Marc Blank public static final boolean DEBUG_GAL_SERVICE = false; 111a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler 112e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank protected static final String PING_COMMAND = "Ping"; 11316b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // Command timeout is the the time allowed for reading data from an open connection before an 11416b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // IOException is thrown. After a small added allowance, our watchdog alarm goes off (allowing 11516b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // us to detect a silently dropped connection). The allowance is defined below. 116847322e4b75287643a8381e28401ee5cf4945a44Yu Ping Hu static public final int COMMAND_TIMEOUT = (int)(30 * DateUtils.SECOND_IN_MILLIS); 11716b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // Connection timeout is the time given to connect to the server before reporting an IOException 118847322e4b75287643a8381e28401ee5cf4945a44Yu Ping Hu static private final int CONNECTION_TIMEOUT = (int)(20 * DateUtils.SECOND_IN_MILLIS); 11916b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // The extra time allowed beyond the COMMAND_TIMEOUT before which our watchdog alarm triggers 120847322e4b75287643a8381e28401ee5cf4945a44Yu Ping Hu static private final int WATCHDOG_TIMEOUT_ALLOWANCE = (int)(30 * DateUtils.SECOND_IN_MILLIS); 121c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank 1224d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank static private final String AUTO_DISCOVER_SCHEMA_PREFIX = 1234d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank "http://schemas.microsoft.com/exchange/autodiscover/mobilesync/"; 1244d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank static private final String AUTO_DISCOVER_PAGE = "/autodiscover/autodiscover.xml"; 125e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank static protected final int EAS_REDIRECT_CODE = 451; 1264d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 1276534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank static public final int INTERNAL_SERVER_ERROR_CODE = 500; 1288f29d002c63fdd370e201ea92c532beac4e818e9Marc Blank 129b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank static public final String EAS_12_POLICY_TYPE = "MS-EAS-Provisioning-WBXML"; 130b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank static public final String EAS_2_POLICY_TYPE = "MS-WAP-Provisioning-XML"; 131b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank 132a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank static public final int MESSAGE_FLAG_MOVED_MESSAGE = 1 << Message.FLAG_SYNC_ADAPTER_SHIFT; 1335b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // The amount of time we allow for a thread to release its post lock after receiving an alert 134847322e4b75287643a8381e28401ee5cf4945a44Yu Ping Hu static private final int POST_LOCK_TIMEOUT = (int)(10 * DateUtils.SECOND_IN_MILLIS); 1355b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 1367dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank // The EAS protocol Provision status for "we implement all of the policies" 1377dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank static private final String PROVISION_STATUS_OK = "1"; 1387dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank // The EAS protocol Provision status meaning "we partially implement the policies" 1397dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank static private final String PROVISION_STATUS_PARTIAL = "2"; 1407dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank 1410209d8248a237f68b4ae6ed1917a6ea28d30eb32Marc Blank static /*package*/ final String DEVICE_TYPE = "Android"; 142e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank static final String USER_AGENT = DEVICE_TYPE + '/' + Build.VERSION.RELEASE + '-' + 1430209d8248a237f68b4ae6ed1917a6ea28d30eb32Marc Blank Eas.CLIENT_VERSION; 1440209d8248a237f68b4ae6ed1917a6ea28d30eb32Marc Blank 145e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Maximum number of times we'll allow a sync to "loop" with MoreAvailable true before 146e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // forcing it to stop. This number has been determined empirically. 147e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank static private final int MAX_LOOPING_COUNT = 100; 148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Reasonable default 149d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank public String mProtocolVersion = Eas.DEFAULT_PROTOCOL_VERSION; 15000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank public Double mProtocolVersionDouble; 15185f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank protected String mDeviceId = null; 1526dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank @VisibleForTesting 1536dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank String mAuthString = null; 1546dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank @VisibleForTesting 1556dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank String mUserString = null; 1566dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank @VisibleForTesting 1576dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank String mBaseUriString = null; 158ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String mHostAddress; 159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String mUserName; 160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String mPassword; 161e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo 162e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // The HttpPost in progress 163e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank private volatile HttpPost mPendingPost = null; 164e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Whether a POST was aborted due to alarm (watchdog alarm) 165e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank protected boolean mPostAborted = false; 166e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Whether a POST was aborted due to reset 167e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank protected boolean mPostReset = false; 168e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 169e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo // The parameters for the connection must be modified through setConnectionParameters 1703fedf8da5e2bd8c5b5a128ffe27e6bb42ebc611dMarc Blank private HostAuth mHostAuth; 1711b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private boolean mSsl = true; 1725843b85178a359446f81770ed7734604a1b2fa7dMarc Blank private boolean mTrustSsl = false; 173e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo private String mClientCertAlias = null; 174e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo 175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public ContentResolver mContentResolver; 176ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank // Whether or not the sync service is valid (usable) 177ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank public boolean mIsValid = true; 178ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 17961c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank // Whether the most recent upsync failed (status 7) 18061c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank public boolean mUpsyncFailed = false; 18161c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank 182e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank protected EasSyncService(Context _context, Mailbox _mailbox) { 183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank super(_context, _mailbox); 184ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mContentResolver = _context.getContentResolver(); 185ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank if (mAccount == null) { 186ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank mIsValid = false; 187ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank return; 188ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank } 189ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank HostAuth ha = HostAuth.restoreHostAuthWithId(_context, mAccount.mHostAuthKeyRecv); 190ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank if (ha == null) { 191ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank mIsValid = false; 192ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank return; 193ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank } 194ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0; 195c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank mTrustSsl = (ha.mFlags & HostAuth.FLAG_TRUST_ALL) != 0; 196ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 197ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 198ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private EasSyncService(String prefix) { 199ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank super(prefix); 200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 201ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public EasSyncService() { 203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank this("EAS Validation"); 204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 206e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank public static EasSyncService getServiceForMailbox(Context context, Mailbox m) { 207e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank switch(m.mType) { 208e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank case Mailbox.TYPE_EAS_ACCOUNT_MAILBOX: 209e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank return new EasAccountService(context, m); 210e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank case Mailbox.TYPE_OUTBOX: 211e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank return new EasOutboxService(context, m); 212e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank default: 213e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank return new EasSyncService(context, m); 214e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 215e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 216e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 217847322e4b75287643a8381e28401ee5cf4945a44Yu Ping Hu @Override 218e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank public void resetCalendarSyncKey() { 219de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // resetCalendarSyncKey is declared in AbstractSyncAdapterService, 220de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // so we need to define this function, by since CalendarSyncAdapter no longer exists, 221de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // we need to remove this: 222de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// CalendarSyncAdapter adapter = new CalendarSyncAdapter(this); 223de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// try { 224de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// adapter.setSyncKey("0", false); 225de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// } catch (IOException e) { 226de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// // The provider can't be reached; nothing to be done 227de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// } 228de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 229de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // For reference, this is what CalendarSyncAdapter.setSyncKey() did: 230de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// synchronized (sSyncKeyLock) { 231de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// if ("0".equals(syncKey) || !inCommands) { 232de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// ContentProviderClient client = mService.mContentResolver 233de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// .acquireContentProviderClient(CalendarContract.CONTENT_URI); 234de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// try { 235de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// SyncStateContract.Helpers.set( 236de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// client, 237de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// asSyncAdapter(SyncState.CONTENT_URI, mEmailAddress, 238de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mAccountManagerAccount, 239de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// syncKey.getBytes()); 240de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// userLog("SyncKey set to ", syncKey, " in CalendarProvider"); 241de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// } catch (RemoteException e) { 242de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// throw new IOException("Can't set SyncKey in CalendarProvider"); 243de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// } 244de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// } 245de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// mMailbox.mSyncKey = syncKey; 246de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// } 247e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 248e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 2495b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank /** 2505b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * Try to wake up a sync thread that is waiting on an HttpClient POST and has waited past its 2515b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * socket timeout without having thrown an Exception 2525b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * 2535b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * @return true if the POST was successfully stopped; false if we've failed and interrupted 2545b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * the thread 2555b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank */ 256e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo @Override 2575b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank public boolean alarm() { 2585b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank HttpPost post; 2591d7c38cd7be83e124b0c7e952b85234d4ee64f47Marc Blank if (mThread == null) return true; 2605b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank String threadName = mThread.getName(); 2615b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 2625b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Synchronize here so that we are guaranteed to have valid mPendingPost and mPostLock 2635b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // executePostWithTimeout (which executes the HttpPost) also uses this lock 2641b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank synchronized(getSynchronizer()) { 2655b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Get a reference to the current post lock 2665b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank post = mPendingPost; 2675b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (post != null) { 2685b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (Eas.USER_LOG) { 2695b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank URI uri = post.getURI(); 2705b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (uri != null) { 2715b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank String query = uri.getQuery(); 2725b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (query == null) { 2735b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank query = "POST"; 2745b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2755b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank userLog(threadName, ": Alert, aborting ", query); 2765b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } else { 2775b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank userLog(threadName, ": Alert, no URI?"); 278e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 279e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 2805b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Abort the POST 281ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank mPostAborted = true; 2825b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank post.abort(); 283e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } else { 2845b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // If there's no POST, we're done 285e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank userLog("Alert, no pending POST"); 2865b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank return true; 2875b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2885b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2895b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 2905b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Wait for the POST to finish 2915b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank try { 2925b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank Thread.sleep(POST_LOCK_TIMEOUT); 2935b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } catch (InterruptedException e) { 2945b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2955b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 2965b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank State s = mThread.getState(); 2975b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (Eas.USER_LOG) { 2985b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank userLog(threadName + ": State = " + s.name()); 2995b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 3005b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 3015b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank synchronized (getSynchronizer()) { 3025b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // If the thread is still hanging around and the same post is pending, let's try to 3035b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // stop the thread with an interrupt. 3045b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if ((s != State.TERMINATED) && (mPendingPost != null) && (mPendingPost == post)) { 3055b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank mStop = true; 3065b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank mThread.interrupt(); 3075b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank userLog("Interrupting..."); 3085b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Let the caller know we had to interrupt the thread 3095b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank return false; 310e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 311e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 3125b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Let the caller know that the alarm was handled normally 3135b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank return true; 314e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 315e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank 316e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank @Override 317e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank public void reset() { 318e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank synchronized(getSynchronizer()) { 319e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank if (mPendingPost != null) { 320e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank URI uri = mPendingPost.getURI(); 321e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank if (uri != null) { 322e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank String query = uri.getQuery(); 323e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank if (query.startsWith("Cmd=Ping")) { 324e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank userLog("Reset, aborting Ping"); 325e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank mPostReset = true; 326e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank mPendingPost.abort(); 327e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 328e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 3291b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void stop() { 335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mStop = true; 336c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank synchronized(getSynchronizer()) { 337c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank if (mPendingPost != null) { 338c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank mPendingPost.abort(); 339c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 340c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 3415c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank } 342ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 343e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank void setupProtocolVersion(EasSyncService service, Header versionHeader) 344d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank throws MessagingException { 345d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // The string is a comma separated list of EAS versions in ascending order 34677186bb1a174432ef272584374942d8b9228e39cMarc Blank // e.g. 1.0,2.0,2.5,12.0,12.1,14.0,14.1 347d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank String supportedVersions = versionHeader.getValue(); 348d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank userLog("Server supports versions: ", supportedVersions); 349d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank String[] supportedVersionsArray = supportedVersions.split(","); 350d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank String ourVersion = null; 351d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // Find the most recent version we support 352d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank for (String version: supportedVersionsArray) { 353d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (version.equals(Eas.SUPPORTED_PROTOCOL_EX2003) || 354e7d9602fce0d4b404d68716da7eb0567da9dad47Marc Blank version.equals(Eas.SUPPORTED_PROTOCOL_EX2007) || 35577186bb1a174432ef272584374942d8b9228e39cMarc Blank version.equals(Eas.SUPPORTED_PROTOCOL_EX2007_SP1) || 35677186bb1a174432ef272584374942d8b9228e39cMarc Blank version.equals(Eas.SUPPORTED_PROTOCOL_EX2010) || 35777186bb1a174432ef272584374942d8b9228e39cMarc Blank version.equals(Eas.SUPPORTED_PROTOCOL_EX2010_SP1)) { 358d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank ourVersion = version; 3598692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 360d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank } 361d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // If we don't support any of the servers supported versions, throw an exception here 362d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // This will cause validation to fail 363d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (ourVersion == null) { 364942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.w(TAG, "No supported EAS versions: " + supportedVersions); 365d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank throw new MessagingException(MessagingException.PROTOCOL_VERSION_UNSUPPORTED); 366d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank } else { 3678b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank // Debug code for testing EAS 14.0; disables support for EAS 14.1 3688b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank // "adb shell setprop log.tag.Exchange14 VERBOSE" 3698b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank if (ourVersion.equals(Eas.SUPPORTED_PROTOCOL_EX2010_SP1) && 370942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.isLoggable("Exchange14", LogUtils.VERBOSE)) { 3718b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank ourVersion = Eas.SUPPORTED_PROTOCOL_EX2010; 3728b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } 373d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank service.mProtocolVersion = ourVersion; 374cb5c48824628e93e98ca24edec46f05c54851af1Marc Blank service.mProtocolVersionDouble = Eas.getProtocolVersionDouble(ourVersion); 37582c3ed7521f8419c05a8b139018321c27a9f1587Marc Blank Account account = service.mAccount; 37682c3ed7521f8419c05a8b139018321c27a9f1587Marc Blank if (account != null) { 37782c3ed7521f8419c05a8b139018321c27a9f1587Marc Blank account.mProtocolVersion = ourVersion; 37882c3ed7521f8419c05a8b139018321c27a9f1587Marc Blank // Fixup search flags, if they're not set 37982c3ed7521f8419c05a8b139018321c27a9f1587Marc Blank if (service.mProtocolVersionDouble >= 12.0 && 38082c3ed7521f8419c05a8b139018321c27a9f1587Marc Blank (account.mFlags & Account.FLAGS_SUPPORTS_SEARCH) == 0) { 381097cd98bed66adfffc623f977b776974296cc3faMarc Blank if (account.isSaved()) { 382097cd98bed66adfffc623f977b776974296cc3faMarc Blank ContentValues cv = new ContentValues(); 383097cd98bed66adfffc623f977b776974296cc3faMarc Blank account.mFlags |= 384097cd98bed66adfffc623f977b776974296cc3faMarc Blank Account.FLAGS_SUPPORTS_GLOBAL_SEARCH + Account.FLAGS_SUPPORTS_SEARCH; 385097cd98bed66adfffc623f977b776974296cc3faMarc Blank cv.put(AccountColumns.FLAGS, account.mFlags); 386097cd98bed66adfffc623f977b776974296cc3faMarc Blank account.update(service.mContext, cv); 387097cd98bed66adfffc623f977b776974296cc3faMarc Blank } 38882c3ed7521f8419c05a8b139018321c27a9f1587Marc Blank } 3898692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 3908692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 3918692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 3928692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank 39326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank /** 39426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * Create an EasSyncService for the specified account 39526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * 39626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * @param context the caller's context 39726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * @param account the account 39826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * @return the service, or null if the account is on hold or hasn't been initialized 39926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank */ 40077e0f0dc7b064e0a320d11c33ce9da2f8ae5ba3bMarc Blank public static EasSyncService setupServiceForAccount(Context context, Account account) { 40126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank // Just return null if we're on security hold 40226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) { 40326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank return null; 40426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 40526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank // If there's no protocol version, we're not initialized 40626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank String protocolVersion = account.mProtocolVersion; 40726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank if (protocolVersion == null) { 40826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank return null; 40926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 41026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank EasSyncService svc = new EasSyncService("OutOfBand"); 41126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank HostAuth ha = HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv); 41226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank svc.mProtocolVersion = protocolVersion; 41326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank svc.mProtocolVersionDouble = Eas.getProtocolVersionDouble(protocolVersion); 41426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank svc.mContext = context; 41526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank svc.mHostAddress = ha.mAddress; 41626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank svc.mUserName = ha.mLogin; 41726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank svc.mPassword = ha.mPassword; 41826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank try { 419e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank svc.setConnectionParameters(ha); 42026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank svc.mDeviceId = ExchangeService.getDeviceId(context); 42136a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo } catch (CertificateException e) { 42236a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo return null; 42326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 42426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank svc.mAccount = account; 42526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank return svc; 42626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 42726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 42848ac73e73700994435bf182a3bccb43e96b68811Marc Blank /** 42948ac73e73700994435bf182a3bccb43e96b68811Marc Blank * Get a redirect address and validate against it 43048ac73e73700994435bf182a3bccb43e96b68811Marc Blank * @param resp the EasResponse to our POST 43148ac73e73700994435bf182a3bccb43e96b68811Marc Blank * @param hostAuth the HostAuth we're using to validate 43248ac73e73700994435bf182a3bccb43e96b68811Marc Blank * @return true if we have an updated HostAuth (with redirect address); false otherwise 43348ac73e73700994435bf182a3bccb43e96b68811Marc Blank */ 434e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank protected boolean getValidateRedirect(EasResponse resp, HostAuth hostAuth) { 43548ac73e73700994435bf182a3bccb43e96b68811Marc Blank Header locHeader = resp.getHeader("X-MS-Location"); 43648ac73e73700994435bf182a3bccb43e96b68811Marc Blank if (locHeader != null) { 43748ac73e73700994435bf182a3bccb43e96b68811Marc Blank String loc; 43848ac73e73700994435bf182a3bccb43e96b68811Marc Blank try { 43948ac73e73700994435bf182a3bccb43e96b68811Marc Blank loc = locHeader.getValue(); 44048ac73e73700994435bf182a3bccb43e96b68811Marc Blank // Reset our host address and uncache our base uri 44148ac73e73700994435bf182a3bccb43e96b68811Marc Blank mHostAddress = Uri.parse(loc).getHost(); 44248ac73e73700994435bf182a3bccb43e96b68811Marc Blank mBaseUriString = null; 44348ac73e73700994435bf182a3bccb43e96b68811Marc Blank hostAuth.mAddress = mHostAddress; 44448ac73e73700994435bf182a3bccb43e96b68811Marc Blank userLog("Redirecting to: " + loc); 44548ac73e73700994435bf182a3bccb43e96b68811Marc Blank return true; 44648ac73e73700994435bf182a3bccb43e96b68811Marc Blank } catch (RuntimeException e) { 44748ac73e73700994435bf182a3bccb43e96b68811Marc Blank // Just don't crash if the Uri is illegal 44848ac73e73700994435bf182a3bccb43e96b68811Marc Blank } 44948ac73e73700994435bf182a3bccb43e96b68811Marc Blank } 45048ac73e73700994435bf182a3bccb43e96b68811Marc Blank return false; 45148ac73e73700994435bf182a3bccb43e96b68811Marc Blank } 45248ac73e73700994435bf182a3bccb43e96b68811Marc Blank 45348ac73e73700994435bf182a3bccb43e96b68811Marc Blank private static final int MAX_REDIRECTS = 3; 45448ac73e73700994435bf182a3bccb43e96b68811Marc Blank private int mRedirectCount = 0; 45548ac73e73700994435bf182a3bccb43e96b68811Marc Blank 456fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank @Override 45748ac73e73700994435bf182a3bccb43e96b68811Marc Blank public Bundle validateAccount(HostAuth hostAuth, Context context) { 4587b4c3ab965df57afdcddc5bce109467458c409f4Marc Blank Bundle bundle = new Bundle(); 4597b4c3ab965df57afdcddc5bce109467458c409f4Marc Blank int resultCode = MessagingException.NO_ERROR; 460ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 46191e6d2a4ff733417090b06f8644615a0fb9160c0Ben Komalo userLog("Testing EAS: ", hostAuth.mAddress, ", ", hostAuth.mLogin, 46287b42fc62ef62eed2188d11da4f0a8e0b635230aBen Komalo ", ssl = ", hostAuth.shouldUseSsl() ? "1" : "0"); 4633b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank mContext = context; 4643b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank mHostAddress = hostAuth.mAddress; 4653b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank mUserName = hostAuth.mLogin; 4663b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank mPassword = hostAuth.mPassword; 467e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo 468e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank setConnectionParameters(hostAuth); 4693b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank mDeviceId = ExchangeService.getDeviceId(context); 4703b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank mAccount = new Account(); 4713b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank mAccount.mEmailAddress = hostAuth.mLogin; 4723b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank EasResponse resp = sendHttpClientOptions(); 47354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 474bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int code = resp.getStatus(); 47554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Validation (OPTIONS) response: " + code); 47654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (code == HttpStatus.SC_OK) { 47754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // No exception means successful validation 478bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank Header commands = resp.getHeader("MS-ASProtocolCommands"); 479bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank Header versions = resp.getHeader("ms-asprotocolversions"); 48054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // Make sure we've got the right protocol version set up 48154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 48254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (commands == null || versions == null) { 48354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("OPTIONS response without commands or versions"); 48454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // We'll treat this as a protocol exception 48554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank throw new MessagingException(0); 48654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 4873b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank setupProtocolVersion(this, versions); 48854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } catch (MessagingException e) { 48954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank bundle.putInt(EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE, 49054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank MessagingException.PROTOCOL_VERSION_UNSUPPORTED); 49154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank return bundle; 4927b4c3ab965df57afdcddc5bce109467458c409f4Marc Blank } 4938692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank 49454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // Run second test here for provisioning failures using FolderSync 49554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Try folder sync"); 49654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // Send "0" as the sync key for new accounts; otherwise, use the current key 49754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank String syncKey = "0"; 49891e6d2a4ff733417090b06f8644615a0fb9160c0Ben Komalo Account existingAccount = Utility.findExistingAccount( 49991e6d2a4ff733417090b06f8644615a0fb9160c0Ben Komalo context, -1L, hostAuth.mAddress, hostAuth.mLogin); 50054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (existingAccount != null && existingAccount.mSyncKey != null) { 50154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank syncKey = existingAccount.mSyncKey; 50254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 50354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank Serializer s = new Serializer(); 50454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY).text(syncKey) 50554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank .end().end().done(); 5063b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank resp = sendHttpClientPost("FolderSync", s.toByteArray()); 507bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank code = resp.getStatus(); 5080b06bcd31ccdfd15478e8ee846fd963775ca036aMarc Blank // Handle HTTP error responses accordingly 5090b06bcd31ccdfd15478e8ee846fd963775ca036aMarc Blank if (code == HttpStatus.SC_FORBIDDEN) { 5100b06bcd31ccdfd15478e8ee846fd963775ca036aMarc Blank // For validation only, we take 403 as ACCESS_DENIED (the account isn't 5110b06bcd31ccdfd15478e8ee846fd963775ca036aMarc Blank // authorized, possibly due to device type) 5120b06bcd31ccdfd15478e8ee846fd963775ca036aMarc Blank resultCode = MessagingException.ACCESS_DENIED; 513ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } else if (resp.isProvisionError()) { 5140b06bcd31ccdfd15478e8ee846fd963775ca036aMarc Blank // The device needs to have security policies enforced 51577186bb1a174432ef272584374942d8b9228e39cMarc Blank throw new CommandStatusException(CommandStatus.NEEDS_PROVISIONING); 51654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } else if (code == HttpStatus.SC_NOT_FOUND) { 51754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // We get a 404 from OWA addresses (which are NOT EAS addresses) 51854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank resultCode = MessagingException.PROTOCOL_VERSION_UNSUPPORTED; 5191b52d3a2a7526810c18c040665d75a467b12677cMarc Blank } else if (code == HttpStatus.SC_UNAUTHORIZED) { 5200b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo resultCode = resp.isMissingCertificate() 52136a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo ? MessagingException.CLIENT_CERTIFICATE_REQUIRED 5220b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo : MessagingException.AUTHENTICATION_FAILED; 52354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } else if (code != HttpStatus.SC_OK) { 52448ac73e73700994435bf182a3bccb43e96b68811Marc Blank if ((code == EAS_REDIRECT_CODE) && (mRedirectCount++ < MAX_REDIRECTS) && 52548ac73e73700994435bf182a3bccb43e96b68811Marc Blank getValidateRedirect(resp, hostAuth)) { 52648ac73e73700994435bf182a3bccb43e96b68811Marc Blank return validateAccount(hostAuth, context); 52748ac73e73700994435bf182a3bccb43e96b68811Marc Blank } 52854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // Fail generically with anything other than success 52954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Unexpected response for FolderSync: ", code); 53054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank resultCode = MessagingException.UNSPECIFIED_EXCEPTION; 53154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } else { 53277186bb1a174432ef272584374942d8b9228e39cMarc Blank // We need to parse the result to see if we've got a provisioning issue 53377186bb1a174432ef272584374942d8b9228e39cMarc Blank // (EAS 14.0 only) 53477186bb1a174432ef272584374942d8b9228e39cMarc Blank if (!resp.isEmpty()) { 53577186bb1a174432ef272584374942d8b9228e39cMarc Blank InputStream is = resp.getInputStream(); 53677186bb1a174432ef272584374942d8b9228e39cMarc Blank // Create the parser with statusOnly set to true; we only care about 53777186bb1a174432ef272584374942d8b9228e39cMarc Blank // seeing if a CommandStatusException is thrown (indicating a 53877186bb1a174432ef272584374942d8b9228e39cMarc Blank // provisioning failure) 5393b73884f0524f21ad4a37127c8bcdaafca193e28Marc Blank new FolderSyncParser(is, new AccountSyncAdapter(this), true).parse(); 54077186bb1a174432ef272584374942d8b9228e39cMarc Blank } 54154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Validation successful"); 54254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 543ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } else if (resp.isAuthError()) { 54454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Authentication failed"); 5450b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo resultCode = resp.isMissingCertificate() 54636a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo ? MessagingException.CLIENT_CERTIFICATE_REQUIRED 5470b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo : MessagingException.AUTHENTICATION_FAILED; 54854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } else if (code == INTERNAL_SERVER_ERROR_CODE) { 54954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // For Exchange 2003, this could mean an authentication failure OR server error 55054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Internal server error"); 55154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank resultCode = MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR; 5527b4c3ab965df57afdcddc5bce109467458c409f4Marc Blank } else { 55348ac73e73700994435bf182a3bccb43e96b68811Marc Blank if ((code == EAS_REDIRECT_CODE) && (mRedirectCount++ < MAX_REDIRECTS) && 55448ac73e73700994435bf182a3bccb43e96b68811Marc Blank getValidateRedirect(resp, hostAuth)) { 55548ac73e73700994435bf182a3bccb43e96b68811Marc Blank return validateAccount(hostAuth, context); 55648ac73e73700994435bf182a3bccb43e96b68811Marc Blank } 55754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // TODO Need to catch other kinds of errors (e.g. policy) For now, report code. 55854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Validation failed, reporting I/O error: ", code); 55954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank resultCode = MessagingException.IOERROR; 56054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 56177186bb1a174432ef272584374942d8b9228e39cMarc Blank } catch (CommandStatusException e) { 56277186bb1a174432ef272584374942d8b9228e39cMarc Blank int status = e.mStatus; 56377186bb1a174432ef272584374942d8b9228e39cMarc Blank if (CommandStatus.isNeedsProvisioning(status)) { 56477186bb1a174432ef272584374942d8b9228e39cMarc Blank // Get the policies and see if we are able to support them 565e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ProvisionParser pp = canProvision(this); 5663524b49b0e8eda6a3f5d46f08fa90b44fcb5318eMarc Blank if (pp != null && pp.hasSupportablePolicySet()) { 56777186bb1a174432ef272584374942d8b9228e39cMarc Blank // Set the proper result code and save the PolicySet in our Bundle 56877186bb1a174432ef272584374942d8b9228e39cMarc Blank resultCode = MessagingException.SECURITY_POLICIES_REQUIRED; 56977186bb1a174432ef272584374942d8b9228e39cMarc Blank bundle.putParcelable(EmailServiceProxy.VALIDATE_BUNDLE_POLICY_SET, 5703ba50d433ee89b252cd1dd9fd3eee1f2baa3945bMarc Blank pp.getPolicy()); 5718b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank if (mProtocolVersionDouble == Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) { 5728b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank mAccount.mSecuritySyncKey = pp.getSecuritySyncKey(); 5738b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank if (!sendSettings()) { 5748b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank userLog("Denied access: ", CommandStatus.toString(status)); 5758b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank resultCode = MessagingException.ACCESS_DENIED; 5768b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } 5778b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } 578e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } else { 57977186bb1a174432ef272584374942d8b9228e39cMarc Blank // If not, set the proper code (the account will not be created) 58077186bb1a174432ef272584374942d8b9228e39cMarc Blank resultCode = MessagingException.SECURITY_POLICIES_UNSUPPORTED; 581e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank bundle.putParcelable(EmailServiceProxy.VALIDATE_BUNDLE_POLICY_SET, 582e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank pp.getPolicy()); 583e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 58477186bb1a174432ef272584374942d8b9228e39cMarc Blank } else if (CommandStatus.isDeniedAccess(status)) { 58577186bb1a174432ef272584374942d8b9228e39cMarc Blank userLog("Denied access: ", CommandStatus.toString(status)); 58677186bb1a174432ef272584374942d8b9228e39cMarc Blank resultCode = MessagingException.ACCESS_DENIED; 58777186bb1a174432ef272584374942d8b9228e39cMarc Blank } else if (CommandStatus.isTransientError(status)) { 58877186bb1a174432ef272584374942d8b9228e39cMarc Blank userLog("Transient error: ", CommandStatus.toString(status)); 58977186bb1a174432ef272584374942d8b9228e39cMarc Blank resultCode = MessagingException.IOERROR; 59077186bb1a174432ef272584374942d8b9228e39cMarc Blank } else { 59177186bb1a174432ef272584374942d8b9228e39cMarc Blank userLog("Unexpected response: ", CommandStatus.toString(status)); 59277186bb1a174432ef272584374942d8b9228e39cMarc Blank resultCode = MessagingException.UNSPECIFIED_EXCEPTION; 59377186bb1a174432ef272584374942d8b9228e39cMarc Blank } 59454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 595bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank resp.close(); 596bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 597ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (IOException e) { 598e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank Throwable cause = e.getCause(); 599e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank if (cause != null && cause instanceof CertificateException) { 60036a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo // This could be because the server's certificate failed to validate. 601e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank userLog("CertificateException caught: ", e.getMessage()); 6027b4c3ab965df57afdcddc5bce109467458c409f4Marc Blank resultCode = MessagingException.GENERAL_SECURITY; 603e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank } 604e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank userLog("IOException caught: ", e.getMessage()); 6057b4c3ab965df57afdcddc5bce109467458c409f4Marc Blank resultCode = MessagingException.IOERROR; 60636a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo } catch (CertificateException e) { 60736a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo // This occurs if the client certificate the user specified is invalid/inaccessible. 60836a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo userLog("CertificateException caught: ", e.getMessage()); 60936a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo resultCode = MessagingException.CLIENT_CERTIFICATE_ERROR; 610ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 6117b4c3ab965df57afdcddc5bce109467458c409f4Marc Blank bundle.putInt(EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE, resultCode); 6127b4c3ab965df57afdcddc5bce109467458c409f4Marc Blank return bundle; 613ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 614ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 6154d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank /** 6164d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * Gets the redirect location from the HTTP headers and uses that to modify the HttpPost so that 6174d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * it can be reused 6184d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * 6194d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param resp the HttpResponse that indicates a redirect (451) 6204d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param post the HttpPost that was originally sent to the server 6214d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @return the HttpPost, updated with the redirect location 6224d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank */ 623942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy private static HttpPost getRedirect(HttpResponse resp, HttpPost post) { 6244d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank Header locHeader = resp.getFirstHeader("X-MS-Location"); 6254d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (locHeader != null) { 6264d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String loc = locHeader.getValue(); 6274d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // If we've gotten one and it shows signs of looking like an address, we try 6284d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // sending our request there 6294d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (loc != null && loc.startsWith("http")) { 6304d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post.setURI(URI.create(loc)); 6314d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return post; 6324d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6334d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6344d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return null; 6354d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6364d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 6374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank /** 6384d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * Send the POST command to the autodiscover server, handling a redirect, if necessary, and 63983ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler * return the HttpResponse. If we get a 401 (unauthorized) error and we're using the 64083ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler * full email address, try the bare user name instead (e.g. foo instead of foo@bar.com) 6414d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * 6424d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param client the HttpClient to be used for the request 6434d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param post the HttpPost we're going to send 64483ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler * @param canRetry whether we can retry using the bare name on an authentication failure (401) 6454d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @return an HttpResponse from the original or redirect server 6464d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @throws IOException on any IOException within the HttpClient code 6474d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @throws MessagingException 6484d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank */ 649bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank private EasResponse postAutodiscover(HttpClient client, HttpPost post, boolean canRetry) 6504d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws IOException, MessagingException { 6514d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Posting autodiscover to: " + post.getURI()); 652bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank EasResponse resp = executePostWithTimeout(client, post, COMMAND_TIMEOUT); 653bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int code = resp.getStatus(); 6544d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // On a redirect, try the new location 65548ac73e73700994435bf182a3bccb43e96b68811Marc Blank if (code == EAS_REDIRECT_CODE) { 656ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu //post = getRedirect(resp.mResponse, post); 6574d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (post != null) { 6584d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Posting autodiscover to redirect: " + post.getURI()); 659adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank return executePostWithTimeout(client, post, COMMAND_TIMEOUT); 6604d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 66183ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // 401 (Unauthorized) is for true auth errors when used in Autodiscover 66252f7f7effdd320594cd1c5c8c67443ad51eca3afMarc Blank } else if (code == HttpStatus.SC_UNAUTHORIZED) { 66383ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler if (canRetry && mUserName.contains("@")) { 66483ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // Try again using the bare user name 66583ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler int atSignIndex = mUserName.indexOf('@'); 66683ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler mUserName = mUserName.substring(0, atSignIndex); 6676dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank cacheAuthUserAndBaseUriStrings(); 66883ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler userLog("401 received; trying username: ", mUserName); 66983ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // Recreate the basic authentication string and reset the header 67083ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler post.removeHeaders("Authorization"); 67183ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler post.setHeader("Authorization", mAuthString); 67283ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler return postAutodiscover(client, post, false); 67383ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler } 6744d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throw new MessagingException(MessagingException.AUTHENTICATION_FAILED); 67583ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // 403 (and others) we'll just punt on 6764d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (code != HttpStatus.SC_OK) { 6774d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We'll try the next address if this doesn't work 6784d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Code: " + code + ", throwing IOException"); 6794d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throw new IOException(); 6804d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6814d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return resp; 6824d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6834d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 6844d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank /** 685e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank * Convert an EAS server url to a HostAuth host address 686e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank * @param url a url, as provided by the Exchange server 687e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank * @return our equivalent host address 688e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank */ 689e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank protected String autodiscoverUrlToHostAddress(String url) { 690e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (url == null) return null; 691e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // We need to extract the server address from a url 692e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank return Uri.parse(url).getHost(); 693e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 694e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 695e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank /** 6964d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * Use the Exchange 2007 AutoDiscover feature to try to retrieve server information using 6974d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * only an email address and the password 6984d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * 699e2788afb86984dae29255fd9c7e24c78d90dbdadYu Ping Hu * @param context Our {@link Context}. 7004d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @return a HostAuth ready to be saved in an Account or null (failure) 7014d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank */ 702fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank public Bundle tryAutodiscover(Context context, HostAuth hostAuth) throws RemoteException { 7034d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank XmlSerializer s = Xml.newSerializer(); 7044d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank ByteArrayOutputStream os = new ByteArrayOutputStream(1024); 7054d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank Bundle bundle = new Bundle(); 7064d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 7074d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.NO_ERROR); 7084d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank try { 7094d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Build the XML document that's sent to the autodiscover server(s) 7104d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.setOutput(os, "UTF-8"); 7114d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startDocument("UTF-8", false); 7124d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startTag(null, "Autodiscover"); 7134d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.attribute(null, "xmlns", AUTO_DISCOVER_SCHEMA_PREFIX + "requestschema/2006"); 7144d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startTag(null, "Request"); 715fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank s.startTag(null, "EMailAddress").text(hostAuth.mLogin).endTag(null, "EMailAddress"); 7164d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startTag(null, "AcceptableResponseSchema"); 7174d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.text(AUTO_DISCOVER_SCHEMA_PREFIX + "responseschema/2006"); 7184d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.endTag(null, "AcceptableResponseSchema"); 7194d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.endTag(null, "Request"); 7204d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.endTag(null, "Autodiscover"); 7214d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.endDocument(); 7224d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String req = os.toString(); 7234d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 724fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank // Initialize user name, password, etc. 725fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank mContext = context; 726fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank mHostAuth = hostAuth; 727fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank mUserName = hostAuth.mLogin; 728fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank mPassword = hostAuth.mPassword; 729fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank mSsl = hostAuth.shouldUseSsl(); 730e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 73183ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // Make sure the authentication string is recreated and cached 7326dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank cacheAuthUserAndBaseUriStrings(); 7334d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 7344d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Split out the domain name 735fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank int amp = mUserName.indexOf('@'); 7364d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // The UI ensures that userName is a valid email address 7374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (amp < 0) { 7384d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throw new RemoteException(); 7394d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 740fd64bd37caf49cd3e2acacee12c20bedd807e362Marc Blank String domain = mUserName.substring(amp + 1); 7414d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 7424d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // There are up to four attempts here; the two URLs that we're supposed to try per the 7434d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // specification, and up to one redirect for each (handled in postAutodiscover) 74483ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // Note: The expectation is that, of these four attempts, only a single server will 74583ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // actually be identified as the autodiscover server. For the identified server, 74683ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // we may also try a 2nd connection with a different format (bare name). 7474d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 7484d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Try the domain first and see if we can get a response 7494d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HttpPost post = new HttpPost("https://" + domain + AUTO_DISCOVER_PAGE); 75020da011530088036d2bf45d3836d6986a4b5d423Marc Blank setHeaders(post, false); 7514d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post.setHeader("Content-Type", "text/xml"); 7524d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post.setEntity(new StringEntity(req)); 7534d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HttpClient client = getHttpClient(COMMAND_TIMEOUT); 754bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank EasResponse resp; 7554d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank try { 75683ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler resp = postAutodiscover(client, post, true /*canRetry*/); 7574d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (IOException e1) { 758adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank userLog("IOException in autodiscover; trying alternate address"); 7594d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We catch the IOException here because we have an alternate address to try 7604d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post.setURI(URI.create("https://autodiscover." + domain + AUTO_DISCOVER_PAGE)); 7614d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // If we fail here, we're out of options, so we let the outer try catch the 7624d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // IOException and return null 76383ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler resp = postAutodiscover(client, post, true /*canRetry*/); 7644d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7654d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 7664d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank try { 76754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // Get the "final" code; if it's not 200, just return null 768bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int code = resp.getStatus(); 76954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Code: " + code); 77054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (code != HttpStatus.SC_OK) return null; 77154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank 772bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank InputStream is = resp.getInputStream(); 7734d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // The response to Autodiscover is regular XML (not WBXML) 7744d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // If we ever get an error in this process, we'll just punt and return null 7754d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 7764d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank XmlPullParser parser = factory.newPullParser(); 7774d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parser.setInput(is, "UTF-8"); 7784d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.getEventType(); 7794d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.START_DOCUMENT) { 7804d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank type = parser.next(); 7814d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.START_TAG) { 7824d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 7834d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("Autodiscover")) { 7844d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth = new HostAuth(); 7854d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseAutodiscover(parser, hostAuth); 7864d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // On success, we'll have a server address and login 78783ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler if (hostAuth.mAddress != null) { 7884d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Fill in the rest of the HostAuth 78983ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // We use the user name and password that were successful during 79083ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler // the autodiscover process 79183ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler hostAuth.mLogin = mUserName; 79283ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler hostAuth.mPassword = mPassword; 793e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo // Note: there is no way we can auto-discover the proper client 794e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo // SSL certificate to use, if one is needed. 7954d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth.mPort = 443; 7962bf91acb8174aab82582ce975a4b60702c810a9cMarc Blank hostAuth.mProtocol = Eas.PROTOCOL; 7974d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth.mFlags = 7984d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HostAuth.FLAG_SSL | HostAuth.FLAG_AUTHENTICATE; 7994d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putParcelable( 8004d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank EmailServiceProxy.AUTO_DISCOVER_BUNDLE_HOST_AUTH, hostAuth); 8014d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else { 8024d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 8034d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.UNSPECIFIED_EXCEPTION); 8044d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8054d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8064d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8074d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8084d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (XmlPullParserException e1) { 8094d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // This would indicate an I/O error of some sort 8104d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We will simply return null and user can configure manually 81154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 812bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank resp.close(); 8134d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8144d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // There's no reason at all for exceptions to be thrown, and it's ok if so. 8154d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We just won't do auto-discover; user can configure manually 8164d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (IllegalArgumentException e) { 8174d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 8184d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.UNSPECIFIED_EXCEPTION); 8194d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (IllegalStateException e) { 8204d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 8214d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.UNSPECIFIED_EXCEPTION); 8224d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (IOException e) { 8234d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("IOException in Autodiscover", e); 8244d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 8254d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.IOERROR); 8264d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (MessagingException e) { 8274d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 828df1b1f66c8c0708e99221eff0bba602859bc8a58Marc Blank MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED); 8294d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8304d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return bundle; 8314d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8324d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 8334d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseServer(XmlPullParser parser, HostAuth hostAuth) 8344d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 8354d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank boolean mobileSync = false; 8364d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 8374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 8384d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Server")) { 8394d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 8404d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 8414d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 8424d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("Type")) { 8434d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (parser.nextText().equals("MobileSync")) { 8444d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank mobileSync = true; 8454d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8464d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (mobileSync && name.equals("Url")) { 847e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String hostAddress = 848e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank autodiscoverUrlToHostAddress(parser.nextText()); 849e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (hostAddress != null) { 850e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank hostAuth.mAddress = hostAddress; 851e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank userLog("Autodiscover, server: " + hostAddress); 8524d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8534d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8544d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8554d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8564d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8574d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 8584d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseSettings(XmlPullParser parser, HostAuth hostAuth) 8594d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 8604d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 8614d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 8624d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Settings")) { 8634d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 8644d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 8654d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 8664d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("Server")) { 8674d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseServer(parser, hostAuth); 8684d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8694d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8704d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8714d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8724d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 8734d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseAction(XmlPullParser parser, HostAuth hostAuth) 8744d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 8754d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 8764d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 8774d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Action")) { 8784d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 8794d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 8804d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 8814d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("Error")) { 8824d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Should parse the error 8834d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (name.equals("Redirect")) { 884942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.d(TAG, "Redirect: " + parser.nextText()); 8854d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (name.equals("Settings")) { 8864d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseSettings(parser, hostAuth); 8874d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8884d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8894d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8904d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 8914d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 8924d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseUser(XmlPullParser parser, HostAuth hostAuth) 8934d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 8944d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 8954d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 8964d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("User")) { 8974d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 8984d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 8994d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 9004d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("EMailAddress")) { 9014d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String addr = parser.nextText(); 90283ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler userLog("Autodiscover, email: " + addr); 9034d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (name.equals("DisplayName")) { 9044d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String dn = parser.nextText(); 9054d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Autodiscover, user: " + dn); 9064d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9074d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9084d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9094d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9104d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 9114d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseResponse(XmlPullParser parser, HostAuth hostAuth) 9124d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 9134d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 9144d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 9154d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Response")) { 9164d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 9174d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 9184d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 9194d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("User")) { 9204d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseUser(parser, hostAuth); 9214d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (name.equals("Action")) { 9224d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseAction(parser, hostAuth); 9234d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9244d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9254d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9264d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9274d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 9284d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseAutodiscover(XmlPullParser parser, HostAuth hostAuth) 9294d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 9304d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 9314d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.nextTag(); 9324d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Autodiscover")) { 9334d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 9344d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG && parser.getName().equals("Response")) { 9354d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseResponse(parser, hostAuth); 9364d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9384d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 9394d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 94096bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler /** 94196bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * Contact the GAL and obtain a list of matching accounts 94296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * @param context caller's context 94396bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * @param accountId the account Id to search 94496bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * @param filter the characters entered so far 9455aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * @return a result record or null for no data 94696bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * 94796bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * TODO: shorter timeout for interactive lookup 94896bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * TODO: make watchdog actually work (it doesn't understand our service w/Mailbox == 0) 94996bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * TODO: figure out why sendHttpClientPost() hangs - possibly pool exhaustion 95096bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler */ 951469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank static public GalResult searchGal(Context context, long accountId, String filter, int limit) { 95226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank Account acct = Account.restoreAccountWithId(context, accountId); 95396bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler if (acct != null) { 95426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank EasSyncService svc = setupServiceForAccount(context, acct); 95526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank if (svc == null) return null; 95696bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler try { 95796bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler Serializer s = new Serializer(); 95896bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler s.start(Tags.SEARCH_SEARCH).start(Tags.SEARCH_STORE); 95996bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler s.data(Tags.SEARCH_NAME, "GAL").data(Tags.SEARCH_QUERY, filter); 96096bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler s.start(Tags.SEARCH_OPTIONS); 961469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank s.data(Tags.SEARCH_RANGE, "0-" + Integer.toString(limit - 1)); 96296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler s.end().end().end().done(); 963bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank EasResponse resp = svc.sendHttpClientPost("Search", s.toByteArray()); 96454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 965bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int code = resp.getStatus(); 96654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (code == HttpStatus.SC_OK) { 967bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank InputStream is = resp.getInputStream(); 96854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 96954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank GalParser gp = new GalParser(is, svc); 97054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (gp.parse()) { 97154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank return gp.getGalResult(); 97254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 97354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 97454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank is.close(); 97554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 976a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler } else { 97754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank svc.userLog("GAL lookup returned " + code); 97854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 97954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 980bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank resp.close(); 98196bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 98296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } catch (IOException e) { 98396bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler // GAL is non-critical; we'll just go on 984a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler svc.userLog("GAL lookup exception " + e); 98596bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 98696bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 98796bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler return null; 98896bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 9897531be7774769c84b499b1de5dc46da3a9468316Marc Blank /** 9905c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * Send an email responding to a Message that has been marked as a meeting request. The message 9915c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * will consist a little bit of event information and an iCalendar attachment 9925c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @param msg the meeting request email 9935c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank */ 994346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank private void sendMeetingResponseMail(Message msg, int response) { 9955c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Get the meeting information; we'd better have some... 99677186bb1a174432ef272584374942d8b9228e39cMarc Blank if (msg.mMeetingInfo == null) return; 9975c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank PackedString meetingInfo = new PackedString(msg.mMeetingInfo); 9985c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 9995c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // This will come as "First Last" <box@server.blah>, so we use Address to 10005c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // parse it into parts; we only need the email address part for the ics file 10015c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Address[] addrs = Address.parse(meetingInfo.get(MeetingInfo.MEETING_ORGANIZER_EMAIL)); 10025c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // It shouldn't be possible, but handle it anyway 10035c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (addrs.length != 1) return; 10045c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank String organizerEmail = addrs[0].getAddress(); 10055c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 10065c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank String dtStamp = meetingInfo.get(MeetingInfo.MEETING_DTSTAMP); 10075c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank String dtStart = meetingInfo.get(MeetingInfo.MEETING_DTSTART); 10085c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank String dtEnd = meetingInfo.get(MeetingInfo.MEETING_DTEND); 10095c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 10105c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // What we're doing here is to create an Entity that looks like an Event as it would be 10115c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // stored by CalendarProvider 10125c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank ContentValues entityValues = new ContentValues(); 10135c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Entity entity = new Entity(entityValues); 10145c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 10155c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Fill in times, location, title, and organizer 10165c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entityValues.put("DTSTAMP", 10175c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank CalendarUtilities.convertEmailDateTimeToCalendarDateTime(dtStamp)); 10188e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda entityValues.put(Events.DTSTART, Utility.parseEmailDateTimeToMillis(dtStart)); 10198e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda entityValues.put(Events.DTEND, Utility.parseEmailDateTimeToMillis(dtEnd)); 10205c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entityValues.put(Events.EVENT_LOCATION, meetingInfo.get(MeetingInfo.MEETING_LOCATION)); 10215c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entityValues.put(Events.TITLE, meetingInfo.get(MeetingInfo.MEETING_TITLE)); 10225c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entityValues.put(Events.ORGANIZER, organizerEmail); 10235c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 10245c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Add ourselves as an attendee, using our account email address 10255c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank ContentValues attendeeValues = new ContentValues(); 10265c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank attendeeValues.put(Attendees.ATTENDEE_RELATIONSHIP, 10275c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Attendees.RELATIONSHIP_ATTENDEE); 10285c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank attendeeValues.put(Attendees.ATTENDEE_EMAIL, mAccount.mEmailAddress); 10295c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entity.addSubValue(Attendees.CONTENT_URI, attendeeValues); 10305c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 10315c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Add the organizer 10325c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank ContentValues organizerValues = new ContentValues(); 10335c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank organizerValues.put(Attendees.ATTENDEE_RELATIONSHIP, 10345c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Attendees.RELATIONSHIP_ORGANIZER); 10355c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank organizerValues.put(Attendees.ATTENDEE_EMAIL, organizerEmail); 10365c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entity.addSubValue(Attendees.CONTENT_URI, organizerValues); 10375c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 10385c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Create a message from the Entity we've built. The message will have fields like 10395c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // to, subject, date, and text filled in. There will also be an "inline" attachment 10405c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // which is in iCalendar format 1041346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank int flag; 1042346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank switch(response) { 1043346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank case EmailServiceConstants.MEETING_REQUEST_ACCEPTED: 1044346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank flag = Message.FLAG_OUTGOING_MEETING_ACCEPT; 1045346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank break; 1046346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank case EmailServiceConstants.MEETING_REQUEST_DECLINED: 1047346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank flag = Message.FLAG_OUTGOING_MEETING_DECLINE; 1048346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank break; 1049346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank case EmailServiceConstants.MEETING_REQUEST_TENTATIVE: 1050346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank default: 1051346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank flag = Message.FLAG_OUTGOING_MEETING_TENTATIVE; 1052346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank break; 1053346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank } 10545c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Message outgoingMsg = 1055346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank CalendarUtilities.createMessageForEntity(mContext, entity, flag, 10565c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank meetingInfo.get(MeetingInfo.MEETING_UID), mAccount); 10575c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Assuming we got a message back (we might not if the event has been deleted), send it 10585c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (outgoingMsg != null) { 10595c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank EasOutboxService.sendMessage(mContext, mAccount.mId, outgoingMsg); 10605c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 10615c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 10625c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 10635c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank /** 10644471a6960d352242cc65bddf7888cc5335840c74Marc Blank * Responds to a move request. The MessageMoveRequest is basically our 10654471a6960d352242cc65bddf7888cc5335840c74Marc Blank * wrapper for the MoveItems service call 10664471a6960d352242cc65bddf7888cc5335840c74Marc Blank * @param req the request (message id and "to" mailbox id) 10674471a6960d352242cc65bddf7888cc5335840c74Marc Blank * @throws IOException 10684471a6960d352242cc65bddf7888cc5335840c74Marc Blank */ 10694471a6960d352242cc65bddf7888cc5335840c74Marc Blank protected void messageMoveRequest(MessageMoveRequest req) throws IOException { 10704471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Retrieve the message and mailbox; punt if either are null 10714471a6960d352242cc65bddf7888cc5335840c74Marc Blank Message msg = Message.restoreMessageWithId(mContext, req.mMessageId); 10724471a6960d352242cc65bddf7888cc5335840c74Marc Blank if (msg == null) return; 10734471a6960d352242cc65bddf7888cc5335840c74Marc Blank Cursor c = mContentResolver.query(ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI, 10744471a6960d352242cc65bddf7888cc5335840c74Marc Blank msg.mId), new String[] {MessageColumns.MAILBOX_KEY}, null, null, null); 1075db2f99c98a6eea4cf33daaba3c349b8c263c3e42Marc Blank if (c == null) throw new ProviderUnavailableException(); 10764471a6960d352242cc65bddf7888cc5335840c74Marc Blank Mailbox srcMailbox = null; 10774471a6960d352242cc65bddf7888cc5335840c74Marc Blank try { 10784471a6960d352242cc65bddf7888cc5335840c74Marc Blank if (!c.moveToNext()) return; 10794471a6960d352242cc65bddf7888cc5335840c74Marc Blank srcMailbox = Mailbox.restoreMailboxWithId(mContext, c.getLong(0)); 10804471a6960d352242cc65bddf7888cc5335840c74Marc Blank } finally { 10814471a6960d352242cc65bddf7888cc5335840c74Marc Blank c.close(); 10824471a6960d352242cc65bddf7888cc5335840c74Marc Blank } 10834471a6960d352242cc65bddf7888cc5335840c74Marc Blank if (srcMailbox == null) return; 10844471a6960d352242cc65bddf7888cc5335840c74Marc Blank Mailbox dstMailbox = Mailbox.restoreMailboxWithId(mContext, req.mMailboxId); 10854471a6960d352242cc65bddf7888cc5335840c74Marc Blank if (dstMailbox == null) return; 10864471a6960d352242cc65bddf7888cc5335840c74Marc Blank Serializer s = new Serializer(); 10874471a6960d352242cc65bddf7888cc5335840c74Marc Blank s.start(Tags.MOVE_MOVE_ITEMS).start(Tags.MOVE_MOVE); 10884471a6960d352242cc65bddf7888cc5335840c74Marc Blank s.data(Tags.MOVE_SRCMSGID, msg.mServerId); 10894471a6960d352242cc65bddf7888cc5335840c74Marc Blank s.data(Tags.MOVE_SRCFLDID, srcMailbox.mServerId); 10904471a6960d352242cc65bddf7888cc5335840c74Marc Blank s.data(Tags.MOVE_DSTFLDID, dstMailbox.mServerId); 10914471a6960d352242cc65bddf7888cc5335840c74Marc Blank s.end().end().done(); 1092bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank EasResponse resp = sendHttpClientPost("MoveItems", s.toByteArray()); 109354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 1094bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int status = resp.getStatus(); 109554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (status == HttpStatus.SC_OK) { 1096bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank if (!resp.isEmpty()) { 1097bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank InputStream is = resp.getInputStream(); 10987d35f61afc910b8f21d3225762c251df10dc76eaYu Ping Hu MoveItemsParser p = new MoveItemsParser(is); 1099bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank p.parse(); 1100bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int statusCode = p.getStatusCode(); 1101bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank ContentValues cv = new ContentValues(); 1102bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank if (statusCode == MoveItemsParser.STATUS_CODE_REVERT) { 1103bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // Restore the old mailbox id 1104bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank cv.put(MessageColumns.MAILBOX_KEY, srcMailbox.mServerId); 1105bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank mContentResolver.update( 1106bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, req.mMessageId), 1107bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank cv, null, null); 1108bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } else if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS) { 1109bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // Update with the new server id 1110bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank cv.put(SyncColumns.SERVER_ID, p.getNewServerId()); 1111bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank cv.put(Message.FLAGS, msg.mFlags | MESSAGE_FLAG_MOVED_MESSAGE); 1112bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank mContentResolver.update( 1113bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, req.mMessageId), 1114bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank cv, null, null); 1115bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 1116bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS 1117bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank || statusCode == MoveItemsParser.STATUS_CODE_REVERT) { 1118bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // If we revert or succeed, we no longer need the update information 1119bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // OR the now-duplicate email (the new copy will be synced down) 1120bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank mContentResolver.delete(ContentUris.withAppendedId( 1121bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank Message.UPDATED_CONTENT_URI, req.mMessageId), null, null); 1122bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } else { 1123bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // In this case, we're retrying, so do nothing. The request will be 1124bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // handled next sync 112554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 1126bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 1127ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } else if (resp.isAuthError()) { 112854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank throw new EasAuthenticationException(); 112954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } else { 113054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Move items request failed, code: " + status); 113154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank throw new IOException(); 113254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 113354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 1134bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank resp.close(); 11354471a6960d352242cc65bddf7888cc5335840c74Marc Blank } 11364471a6960d352242cc65bddf7888cc5335840c74Marc Blank } 11374471a6960d352242cc65bddf7888cc5335840c74Marc Blank 11384471a6960d352242cc65bddf7888cc5335840c74Marc Blank /** 11397531be7774769c84b499b1de5dc46da3a9468316Marc Blank * Responds to a meeting request. The MeetingResponseRequest is basically our 11407531be7774769c84b499b1de5dc46da3a9468316Marc Blank * wrapper for the meetingResponse service call 11417531be7774769c84b499b1de5dc46da3a9468316Marc Blank * @param req the request (message id and response code) 11427531be7774769c84b499b1de5dc46da3a9468316Marc Blank * @throws IOException 11437531be7774769c84b499b1de5dc46da3a9468316Marc Blank */ 11447531be7774769c84b499b1de5dc46da3a9468316Marc Blank protected void sendMeetingResponse(MeetingResponseRequest req) throws IOException { 11454f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank // Retrieve the message and mailbox; punt if either are null 11467531be7774769c84b499b1de5dc46da3a9468316Marc Blank Message msg = Message.restoreMessageWithId(mContext, req.mMessageId); 11474f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank if (msg == null) return; 11484f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, msg.mMailboxKey); 11494f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank if (mailbox == null) return; 11507531be7774769c84b499b1de5dc46da3a9468316Marc Blank Serializer s = new Serializer(); 11517531be7774769c84b499b1de5dc46da3a9468316Marc Blank s.start(Tags.MREQ_MEETING_RESPONSE).start(Tags.MREQ_REQUEST); 11527531be7774769c84b499b1de5dc46da3a9468316Marc Blank s.data(Tags.MREQ_USER_RESPONSE, Integer.toString(req.mResponse)); 11534f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank s.data(Tags.MREQ_COLLECTION_ID, mailbox.mServerId); 11547531be7774769c84b499b1de5dc46da3a9468316Marc Blank s.data(Tags.MREQ_REQ_ID, msg.mServerId); 11557531be7774769c84b499b1de5dc46da3a9468316Marc Blank s.end().end().done(); 1156bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank EasResponse resp = sendHttpClientPost("MeetingResponse", s.toByteArray()); 115754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 1158bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int status = resp.getStatus(); 115954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (status == HttpStatus.SC_OK) { 1160bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank if (!resp.isEmpty()) { 1161bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank InputStream is = resp.getInputStream(); 116262c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Hu new MeetingResponseParser(is).parse(); 116354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank String meetingInfo = msg.mMeetingInfo; 116454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (meetingInfo != null) { 116554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank String responseRequested = new PackedString(meetingInfo).get( 116654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank MeetingInfo.MEETING_RESPONSE_REQUESTED); 116754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // If there's no tag, or a non-zero tag, we send the response mail 116854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if ("0".equals(responseRequested)) { 116954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank return; 117054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 117139fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank } 117254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank sendMeetingResponseMail(msg, req.mResponse); 117339fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank } 1174ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } else if (resp.isAuthError()) { 117554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank throw new EasAuthenticationException(); 117654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } else { 117754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Meeting response request failed, code: " + status); 117854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank throw new IOException(); 11797531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 118054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 1181bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank resp.close(); 118254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 11837531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 11847531be7774769c84b499b1de5dc46da3a9468316Marc Blank 118583ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler /** 11866dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank * Using mUserName and mPassword, lazily create the strings that are commonly used in our HTTP 11876dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank * POSTs, including the authentication header string, the base URI we use to communicate with 11886dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank * EAS, and the user information string (user, deviceId, and deviceType) 118983ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler */ 11906dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank private void cacheAuthUserAndBaseUriStrings() { 11916dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank if (mAuthString == null || mUserString == null || mBaseUriString == null) { 11926dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank String safeUserName = Uri.encode(mUserName); 11936dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank String cs = mUserName + ':' + mPassword; 11946dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank mAuthString = "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP); 11956dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank mUserString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId + 11966dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank "&DeviceType=" + DEVICE_TYPE; 11976dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank String scheme = 11986dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank EmailClientConnectionManager.makeScheme(mSsl, mTrustSsl, mClientCertAlias); 11996dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank mBaseUriString = scheme + "://" + mHostAddress + "/Microsoft-Server-ActiveSync"; 12006dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank } 120183ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler } 120283ffeefe6055b946692ff36ff17eea40ba6c423fAndrew Stadler 1203e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo @VisibleForTesting 1204e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo String makeUriString(String cmd, String extra) { 12056dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank cacheAuthUserAndBaseUriStrings(); 12066dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank String uriString = mBaseUriString; 1207ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (cmd != null) { 12086dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank uriString += "?Cmd=" + cmd + mUserString; 1209ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1210ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (extra != null) { 1211e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo uriString += extra; 1212ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1213e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo return uriString; 1214ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1215ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 121620da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 121720da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Set standard HTTP headers, using a policy key if required 121820da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @param method the method we are going to send 121920da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @param usePolicyKey whether or not a policy key should be sent in the headers 122020da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 12217dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank /*package*/ void setHeaders(HttpRequestBase method, boolean usePolicyKey) { 12228047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("Authorization", mAuthString); 12238047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("MS-ASProtocolVersion", mProtocolVersion); 12240209d8248a237f68b4ae6ed1917a6ea28d30eb32Marc Blank method.setHeader("User-Agent", USER_AGENT); 1225bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank method.setHeader("Accept-Encoding", "gzip"); 12267dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank if (usePolicyKey) { 12277dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank // If there's an account in existence, use its key; otherwise (we're creating the 12287dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank // account), send "0". The server will respond with code 449 if there are policies 12297dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank // to be enforced 12307dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank String key = "0"; 12317dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank if (mAccount != null) { 12327dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank String accountKey = mAccount.mSecuritySyncKey; 12337dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank if (!TextUtils.isEmpty(accountKey)) { 12347dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank key = accountKey; 12357dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank } 123695fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank } 123720da011530088036d2bf45d3836d6986a4b5d423Marc Blank method.setHeader("X-MS-PolicyKey", key); 123820da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 12398047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank } 1240ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1241e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank protected void setConnectionParameters(HostAuth hostAuth) throws CertificateException { 12423fedf8da5e2bd8c5b5a128ffe27e6bb42ebc611dMarc Blank mHostAuth = hostAuth; 1243e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank mSsl = hostAuth.shouldUseSsl(); 1244e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank mTrustSsl = hostAuth.shouldTrustAllServerCerts(); 1245e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank mClientCertAlias = hostAuth.mClientCertAlias; 1246e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo 1247e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo // Register the new alias, if needed. 1248e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo if (mClientCertAlias != null) { 1249e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo // Ensure that the connection manager knows to use the proper client certificate 1250e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo // when establishing connections for this service. 1251e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank EmailClientConnectionManager connManager = getClientConnectionManager(); 1252e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank connManager.registerClientCert(mContext, hostAuth); 1253e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo } 1254e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo } 1255e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo 1256e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo private EmailClientConnectionManager getClientConnectionManager() { 12573fedf8da5e2bd8c5b5a128ffe27e6bb42ebc611dMarc Blank return ExchangeService.getClientConnectionManager(mContext, mHostAuth); 1258e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank } 1259e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank 12608047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank private HttpClient getHttpClient(int timeout) { 12618047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpParams params = new BasicHttpParams(); 126216b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT); 12638047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpConnectionParams.setSoTimeout(params, timeout); 1264e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank HttpConnectionParams.setSocketBufferSize(params, 8192); 12656ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert return new DefaultHttpClient(getClientConnectionManager(), params) { 12666ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert protected BasicHttpProcessor createHttpProcessor() { 12676ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert final BasicHttpProcessor processor = super.createHttpProcessor(); 12686ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert processor.addRequestInterceptor(new CurlLogger()); 12696ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert return processor; 12706ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert } 12716ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert }; 12728047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank } 1273ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1274bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank public EasResponse sendHttpClientPost(String cmd, byte[] bytes) throws IOException { 12751b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank return sendHttpClientPost(cmd, new ByteArrayEntity(bytes), COMMAND_TIMEOUT); 12769e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank } 12779e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank 1278adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank /** 1279adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * Convenience method for executePostWithTimeout for use other than with the Ping command 1280adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank */ 1281bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank protected EasResponse executePostWithTimeout(HttpClient client, HttpPost method, int timeout) 1282adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank throws IOException { 1283adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank return executePostWithTimeout(client, method, timeout, false); 1284adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1285adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank 1286adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank /** 1287adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * Handle executing an HTTP POST command with proper timeout, watchdog, and ping behavior 1288adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @param client the HttpClient 1289adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @param method the HttpPost 1290adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @param timeout the timeout before failure, in ms 1291adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @param isPingCommand whether the POST is for the Ping command (requires wakelock logic) 1292adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @return the HttpResponse 1293adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @throws IOException 1294adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank */ 1295c716a538cffe7f5cea0d55683c696bc25e3568c5Yu Ping Hu protected EasResponse executePostWithTimeout(HttpClient client, HttpPost method, 1296c716a538cffe7f5cea0d55683c696bc25e3568c5Yu Ping Hu final int timeout, final boolean isPingCommand) throws IOException { 1297c716a538cffe7f5cea0d55683c696bc25e3568c5Yu Ping Hu final boolean hasWakeLock; 1298adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank synchronized(getSynchronizer()) { 1299c716a538cffe7f5cea0d55683c696bc25e3568c5Yu Ping Hu hasWakeLock = ExchangeService.isHoldingWakeLock(mMailboxId); 1300c716a538cffe7f5cea0d55683c696bc25e3568c5Yu Ping Hu if (isPingCommand && !hasWakeLock) { 1301942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.e(TAG, "executePostWithTimeout (ping) without holding wakelock"); 1302c716a538cffe7f5cea0d55683c696bc25e3568c5Yu Ping Hu } 1303adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank mPendingPost = method; 130416b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank long alarmTime = timeout + WATCHDOG_TIMEOUT_ALLOWANCE; 1305c716a538cffe7f5cea0d55683c696bc25e3568c5Yu Ping Hu if (isPingCommand && hasWakeLock) { 1306385a0be662509754e687bcfa9813208b050bf951Marc Blank ExchangeService.runAsleep(mMailboxId, alarmTime); 1307adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } else { 1308385a0be662509754e687bcfa9813208b050bf951Marc Blank ExchangeService.setWatchdogAlarm(mMailboxId, alarmTime); 1309adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1310adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1311adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank try { 13123d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo return EasResponse.fromHttpRequest(getClientConnectionManager(), client, method); 1313adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } finally { 1314adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank synchronized(getSynchronizer()) { 1315c716a538cffe7f5cea0d55683c696bc25e3568c5Yu Ping Hu if (isPingCommand && hasWakeLock) { 1316385a0be662509754e687bcfa9813208b050bf951Marc Blank ExchangeService.runAwake(mMailboxId); 1317adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } else { 1318385a0be662509754e687bcfa9813208b050bf951Marc Blank ExchangeService.clearWatchdogAlarm(mMailboxId); 1319adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1320adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank mPendingPost = null; 1321adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1322adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1323adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1324adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank 13250565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public EasResponse sendHttpClientPost(String cmd, HttpEntity entity, int timeout) 13261b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank throws IOException { 13271b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank HttpClient client = getHttpClient(timeout); 1328c4bc56c4057d2d7596b75c60ee792676251804f5Marc Blank boolean isPingCommand = cmd.equals(PING_COMMAND); 132985f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank 133085f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank // Split the mail sending commands 133185f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank String extra = null; 133285f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank boolean msg = false; 133385f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) { 13345843b85178a359446f81770ed7734604a1b2fa7dMarc Blank int cmdLength = cmd.indexOf('&'); 133585f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank extra = cmd.substring(cmdLength); 133685f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank cmd = cmd.substring(0, cmdLength); 133785f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank msg = true; 133885f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank } else if (cmd.startsWith("SendMail&")) { 133985f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank msg = true; 134085f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank } 134185f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank 134285f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank String us = makeUriString(cmd, extra); 13438047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpPost method = new HttpPost(URI.create(us)); 1344c171a2362e6db78385463e3b7b1bc66585fdcdfcMarc Blank // Send the proper Content-Type header; it's always wbxml except for messages when 1345c171a2362e6db78385463e3b7b1bc66585fdcdfcMarc Blank // the EAS protocol version is < 14.0 134642f47790b84483b15fdf4c7f53b283dd1d56d3faMarc Blank // If entity is null (e.g. for attachments), don't set this header 1347c171a2362e6db78385463e3b7b1bc66585fdcdfcMarc Blank if (msg && (mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE)) { 13488047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("Content-Type", "message/rfc822"); 134942f47790b84483b15fdf4c7f53b283dd1d56d3faMarc Blank } else if (entity != null) { 13508047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml"); 1351ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1352b7e8db5257203ce221885cbdff256092a073e1daMarc Blank setHeaders(method, !isPingCommand); 1353b7e8db5257203ce221885cbdff256092a073e1daMarc Blank // NOTE 1354b7e8db5257203ce221885cbdff256092a073e1daMarc Blank // The next lines are added at the insistence of $VENDOR, who is seeing inappropriate 1355b7e8db5257203ce221885cbdff256092a073e1daMarc Blank // network activity related to the Ping command on some networks with some servers. 1356b7e8db5257203ce221885cbdff256092a073e1daMarc Blank // This code should be removed when the underlying issue is resolved 1357b7e8db5257203ce221885cbdff256092a073e1daMarc Blank if (isPingCommand) { 1358b7e8db5257203ce221885cbdff256092a073e1daMarc Blank method.setHeader("Connection", "close"); 1359b7e8db5257203ce221885cbdff256092a073e1daMarc Blank } 13609e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank method.setEntity(entity); 1361adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank return executePostWithTimeout(client, method, timeout, isPingCommand); 13628047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank } 13638047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank 1364bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank protected EasResponse sendHttpClientOptions() throws IOException { 13656dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank cacheAuthUserAndBaseUriStrings(); 13666dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank // For OPTIONS, just use the base string and the single header 13676dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank String uriString = mBaseUriString; 13686dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank HttpOptions method = new HttpOptions(URI.create(uriString)); 13696dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank method.setHeader("Authorization", mAuthString); 13706dd267921d03b252b1d74ba6e75516263dfdf080Marc Blank method.setHeader("User-Agent", USER_AGENT); 1371e9353616056991511d4c3a707e97ca0468c0ef42Marc Blank HttpClient client = getHttpClient(COMMAND_TIMEOUT); 13723d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo return EasResponse.fromHttpRequest(getClientConnectionManager(), client, method); 1373ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1374ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1375e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String getTargetCollectionClassFromCursor(Cursor c) { 1376ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN); 1377ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (type == Mailbox.TYPE_CONTACTS) { 1378ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Contacts"; 1379ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else if (type == Mailbox.TYPE_CALENDAR) { 1380ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Calendar"; 1381ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 1382ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Email"; 1383ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1384ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1385ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 138620da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 138720da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Negotiate provisioning with the server. First, get policies form the server and see if 138820da011530088036d2bf45d3836d6986a4b5d423Marc Blank * the policies are supported by the device. Then, write the policies to the account and 138920da011530088036d2bf45d3836d6986a4b5d423Marc Blank * tell SecurityPolicy that we have policies in effect. Finally, see if those policies are 139020da011530088036d2bf45d3836d6986a4b5d423Marc Blank * active; if so, acknowledge the policies to the server and get a final policy key that we 139120da011530088036d2bf45d3836d6986a4b5d423Marc Blank * use in future EAS commands and write this key to the account. 139220da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @return whether or not provisioning has been successful 139320da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @throws IOException 139420da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 1395e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank public static boolean tryProvision(EasSyncService svc) throws IOException { 139620da011530088036d2bf45d3836d6986a4b5d423Marc Blank // First, see if provisioning is even possible, i.e. do we support the policies required 139720da011530088036d2bf45d3836d6986a4b5d423Marc Blank // by the server 1398e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ProvisionParser pp = canProvision(svc); 1399e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (pp == null) return false; 1400e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Context context = svc.mContext; 1401e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Account account = svc.mAccount; 1402e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Get the policies from ProvisionParser 1403e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Policy policy = pp.getPolicy(); 1404e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Policy oldPolicy = null; 1405e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Grab the old policy (if any) 1406e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (svc.mAccount.mPolicyKey > 0) { 1407e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank oldPolicy = Policy.restorePolicyWithId(context, account.mPolicyKey); 1408e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 1409e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Update the account with a null policyKey (the key we've gotten is 1410e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // temporary and cannot be used for syncing) 1411e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank PolicyServiceProxy.setAccountPolicy(context, account.mId, policy, null); 1412e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Make sure mAccount is current (with latest policy key) 1413e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank account.refresh(context); 1414e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (pp.getRemoteWipe()) { 1415e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // We've gotten a remote wipe command 1416e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ExchangeService.alwaysLog("!!! Remote wipe request received"); 1417e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Start by setting the account to security hold 1418e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank PolicyServiceProxy.setAccountHoldFlag(context, account, true); 1419e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Force a stop to any running syncs for this account (except this one) 1420e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ExchangeService.stopNonAccountMailboxSyncsForAccount(account.mId); 1421e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 1422e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // First, we've got to acknowledge it, but wrap the wipe in try/catch so that 1423e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // we wipe the device regardless of any errors in acknowledgment 1424e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank try { 1425e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ExchangeService.alwaysLog("!!! Acknowledging remote wipe to server"); 1426e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank acknowledgeRemoteWipe(svc, pp.getSecuritySyncKey()); 1427e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } catch (Exception e) { 1428e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Because remote wipe is such a high priority task, we don't want to 1429e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // circumvent it if there's an exception in acknowledgment 1430c55d6dfd1ade376f080568b4f307f76d4984a761Marc Blank } 1431e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Then, tell SecurityPolicy to wipe the device 1432e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ExchangeService.alwaysLog("!!! Executing remote wipe"); 1433e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank PolicyServiceProxy.remoteWipe(context); 1434e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank return false; 1435e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } else if (pp.hasSupportablePolicySet() && PolicyServiceProxy.isActive(context, policy)) { 1436e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // See if the required policies are in force; if they are, acknowledge the policies 1437e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // to the server and get the final policy key 1438e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // NOTE: For EAS 14.0, we already have the acknowledgment in the ProvisionParser 1439e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String securitySyncKey; 1440e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (svc.mProtocolVersionDouble == Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) { 1441e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank securitySyncKey = pp.getSecuritySyncKey(); 1442e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } else { 1443e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank securitySyncKey = acknowledgeProvision(svc, pp.getSecuritySyncKey(), 1444e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank PROVISION_STATUS_OK); 1445e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 1446e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (securitySyncKey != null) { 1447e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // If attachment policies have changed, fix up any affected attachment records 1448e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (oldPolicy != null) { 1449e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if ((oldPolicy.mDontAllowAttachments != policy.mDontAllowAttachments) || 1450e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank (oldPolicy.mMaxAttachmentSize != policy.mMaxAttachmentSize)) { 1451e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Policy.setAttachmentFlagsForNewPolicy(context, account, policy); 1452d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 1453bbc1811956084987767779e31525c5013dab59d8Marc Blank } 1454e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Write the final policy key to the Account and say we've been successful 1455e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank PolicyServiceProxy.setAccountPolicy(context, account.mId, policy, securitySyncKey); 1456e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Release any mailboxes that might be in a security hold 1457e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ExchangeService.releaseSecurityHold(account); 1458e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank return true; 145920da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 146020da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 146120da011530088036d2bf45d3836d6986a4b5d423Marc Blank return false; 146220da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 146320da011530088036d2bf45d3836d6986a4b5d423Marc Blank 1464e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank private static String getPolicyType(Double protocolVersion) { 1465e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank return (protocolVersion >= 1466d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) ? EAS_12_POLICY_TYPE : EAS_2_POLICY_TYPE; 1467b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank } 1468b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank 146920da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 147020da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Obtain a set of policies from the server and determine whether those policies are supported 147120da011530088036d2bf45d3836d6986a4b5d423Marc Blank * by the device. 147232c569e053c3159956e13b2e5b47eb9d167186ceMarc Blank * @return the ProvisionParser (holds policies and key) if we receive policies; null otherwise 147320da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @throws IOException 147420da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 1475e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank public static ProvisionParser canProvision(EasSyncService svc) throws IOException { 14768692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank Serializer s = new Serializer(); 1477e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Double protocolVersion = svc.mProtocolVersionDouble; 147877186bb1a174432ef272584374942d8b9228e39cMarc Blank s.start(Tags.PROVISION_PROVISION); 1479e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (svc.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2010_SP1_DOUBLE) { 14808b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank // Send settings information in 14.1 and greater 148177186bb1a174432ef272584374942d8b9228e39cMarc Blank s.start(Tags.SETTINGS_DEVICE_INFORMATION).start(Tags.SETTINGS_SET); 148277186bb1a174432ef272584374942d8b9228e39cMarc Blank s.data(Tags.SETTINGS_MODEL, Build.MODEL); 148377186bb1a174432ef272584374942d8b9228e39cMarc Blank //s.data(Tags.SETTINGS_IMEI, ""); 148477186bb1a174432ef272584374942d8b9228e39cMarc Blank //s.data(Tags.SETTINGS_FRIENDLY_NAME, "Friendly Name"); 148577186bb1a174432ef272584374942d8b9228e39cMarc Blank s.data(Tags.SETTINGS_OS, "Android " + Build.VERSION.RELEASE); 148677186bb1a174432ef272584374942d8b9228e39cMarc Blank //s.data(Tags.SETTINGS_OS_LANGUAGE, ""); 148777186bb1a174432ef272584374942d8b9228e39cMarc Blank //s.data(Tags.SETTINGS_PHONE_NUMBER, ""); 148877186bb1a174432ef272584374942d8b9228e39cMarc Blank //s.data(Tags.SETTINGS_MOBILE_OPERATOR, ""); 1489e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank s.data(Tags.SETTINGS_USER_AGENT, EasSyncService.USER_AGENT); 149077186bb1a174432ef272584374942d8b9228e39cMarc Blank s.end().end(); // SETTINGS_SET, SETTINGS_DEVICE_INFORMATION 149177186bb1a174432ef272584374942d8b9228e39cMarc Blank } 149277186bb1a174432ef272584374942d8b9228e39cMarc Blank s.start(Tags.PROVISION_POLICIES); 1493e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank s.start(Tags.PROVISION_POLICY); 1494e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank s.data(Tags.PROVISION_POLICY_TYPE, getPolicyType(protocolVersion)); 1495e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank s.end().end().end().done(); // PROVISION_POLICY, PROVISION_POLICIES, PROVISION_PROVISION 1496e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank EasResponse resp = svc.sendHttpClientPost("Provision", s.toByteArray()); 149754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 1498bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int code = resp.getStatus(); 149954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (code == HttpStatus.SC_OK) { 1500bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank InputStream is = resp.getInputStream(); 1501bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu ProvisionParser pp = new ProvisionParser(svc.mContext, is); 150254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (pp.parse()) { 150354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // The PolicySet in the ProvisionParser will have the requirements for all KNOWN 150454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // policies. If others are required, hasSupportablePolicySet will be false 15058b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank if (pp.hasSupportablePolicySet() && 1506e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank svc.mProtocolVersionDouble == Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) { 15078b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank // In EAS 14.0, we need the final security key in order to use the settings 15088b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank // command 1509e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String policyKey = acknowledgeProvision(svc, pp.getSecuritySyncKey(), 15108b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank PROVISION_STATUS_OK); 15118b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank if (policyKey != null) { 15128b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank pp.setSecuritySyncKey(policyKey); 15138b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } 15148b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } else if (!pp.hasSupportablePolicySet()) { 151554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // Try to acknowledge using the "partial" status (i.e. we can partially 151654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // accommodate the required policies). The server will agree to this if the 151754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // "allow non-provisionable devices" setting is enabled on the server 1518afbb79493033d7108fa8dfaa88e0427db84de09fMarc Blank ExchangeService.log("PolicySet is NOT fully supportable"); 1519e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (acknowledgeProvision(svc, pp.getSecuritySyncKey(), 1520e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank PROVISION_STATUS_PARTIAL) != null) { 1521e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // The server's ok with our inability to support policies, so we'll 1522e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // clear them 1523e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank pp.clearUnsupportablePolicies(); 1524883a5c77034a3b839127167bc20d05830c8350fbAndy Stadler } 152554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 152632c569e053c3159956e13b2e5b47eb9d167186ceMarc Blank return pp; 15278692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 15288692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 152954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 1530bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank resp.close(); 15318692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 15328b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank 153320da011530088036d2bf45d3836d6986a4b5d423Marc Blank // On failures, simply return null 153420da011530088036d2bf45d3836d6986a4b5d423Marc Blank return null; 153520da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 153620da011530088036d2bf45d3836d6986a4b5d423Marc Blank 153720da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 153820da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Acknowledge that we support the policies provided by the server, and that these policies 153920da011530088036d2bf45d3836d6986a4b5d423Marc Blank * are in force. 154020da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @param tempKey the initial (temporary) policy key sent by the server 154120da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @throws IOException 154220da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 1543e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank private static void acknowledgeRemoteWipe(EasSyncService svc, String tempKey) 1544e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank throws IOException { 1545e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank acknowledgeProvisionImpl(svc, tempKey, PROVISION_STATUS_OK, true); 15462a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank } 15472a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank 1548e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank private static String acknowledgeProvision(EasSyncService svc, String tempKey, String result) 1549e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank throws IOException { 1550e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank return acknowledgeProvisionImpl(svc, tempKey, result, false); 15512a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank } 15522a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank 1553e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank private static String acknowledgeProvisionImpl(EasSyncService svc, String tempKey, 1554e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String status, boolean remoteWipe) throws IOException { 155520da011530088036d2bf45d3836d6986a4b5d423Marc Blank Serializer s = new Serializer(); 155620da011530088036d2bf45d3836d6986a4b5d423Marc Blank s.start(Tags.PROVISION_PROVISION).start(Tags.PROVISION_POLICIES); 155720da011530088036d2bf45d3836d6986a4b5d423Marc Blank s.start(Tags.PROVISION_POLICY); 1558b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank 1559b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank // Use the proper policy type, depending on EAS version 1560e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank s.data(Tags.PROVISION_POLICY_TYPE, getPolicyType(svc.mProtocolVersionDouble)); 1561b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank 156220da011530088036d2bf45d3836d6986a4b5d423Marc Blank s.data(Tags.PROVISION_POLICY_KEY, tempKey); 15637dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank s.data(Tags.PROVISION_STATUS, status); 15642ca2e1caef16fde39df2c795ac4c9477f9a8c6d9Marc Blank s.end().end(); // PROVISION_POLICY, PROVISION_POLICIES 15652a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank if (remoteWipe) { 15662a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank s.start(Tags.PROVISION_REMOTE_WIPE); 15677dde8276e9e168d933ebfdb86b4f2d8ec5d7b872Marc Blank s.data(Tags.PROVISION_STATUS, PROVISION_STATUS_OK); 15682a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank s.end(); 15692a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank } 15702ca2e1caef16fde39df2c795ac4c9477f9a8c6d9Marc Blank s.end().done(); // PROVISION_PROVISION 1571e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank EasResponse resp = svc.sendHttpClientPost("Provision", s.toByteArray()); 157254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 1573bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int code = resp.getStatus(); 157454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (code == HttpStatus.SC_OK) { 1575bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank InputStream is = resp.getInputStream(); 1576bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu ProvisionParser pp = new ProvisionParser(svc.mContext, is); 157754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (pp.parse()) { 157854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // Return the final policy key from the ProvisionParser 1579e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String result = (pp.getSecuritySyncKey() == null) ? "failed" : "confirmed"; 1580e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ExchangeService.log("Provision " + result + " for " + 1581afbb79493033d7108fa8dfaa88e0427db84de09fMarc Blank (PROVISION_STATUS_PARTIAL.equals(status) ? "PART" : "FULL") + " set"); 15823ba50d433ee89b252cd1dd9fd3eee1f2baa3945bMarc Blank return pp.getSecuritySyncKey(); 158354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 158454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 158554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 1586bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank resp.close(); 158720da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 1588afbb79493033d7108fa8dfaa88e0427db84de09fMarc Blank // On failures, log issue and return null 1589e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ExchangeService.log("Provisioning failed for" + 1590afbb79493033d7108fa8dfaa88e0427db84de09fMarc Blank (PROVISION_STATUS_PARTIAL.equals(status) ? "PART" : "FULL") + " set"); 159120da011530088036d2bf45d3836d6986a4b5d423Marc Blank return null; 15928692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 15938692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank 15948b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank private boolean sendSettings() throws IOException { 15958b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank Serializer s = new Serializer(); 15968b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank s.start(Tags.SETTINGS_SETTINGS); 15978b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank s.start(Tags.SETTINGS_DEVICE_INFORMATION).start(Tags.SETTINGS_SET); 15988b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank s.data(Tags.SETTINGS_MODEL, Build.MODEL); 15998b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank s.data(Tags.SETTINGS_OS, "Android " + Build.VERSION.RELEASE); 16008b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank s.data(Tags.SETTINGS_USER_AGENT, USER_AGENT); 16018b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank s.end().end().end().done(); // SETTINGS_SET, SETTINGS_DEVICE_INFORMATION, SETTINGS_SETTINGS 16028b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank EasResponse resp = sendHttpClientPost("Settings", s.toByteArray()); 16038b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank try { 16048b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank int code = resp.getStatus(); 16058b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank if (code == HttpStatus.SC_OK) { 16068b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank InputStream is = resp.getInputStream(); 1607bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu SettingsParser sp = new SettingsParser(is); 16088b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank return sp.parse(); 16098b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } 16108b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } finally { 16118b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank resp.close(); 16128b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } 16138b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank // On failures, simply return false 16148b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank return false; 16158b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank } 16168b4543adb2e1eda9b0c0d3d7d89e03bff9e9fe44Marc Blank 1617ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 1618ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Common code to sync E+PIM data 1619883a5c77034a3b839127167bc20d05830c8350fbAndy Stadler * @param target an EasMailbox, EasContacts, or EasCalendar object 1620ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 16217c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank public void sync(AbstractSyncAdapter target) throws IOException { 1622ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Mailbox mailbox = target.mMailbox; 1623ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1624ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank boolean moreAvailable = true; 16258efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank int loopingCount = 0; 1626b20a744b01fef0082b106f44612a3d0fec361800Marc Blank while (!mStop && (moreAvailable || hasPendingRequests())) { 1627385a0be662509754e687bcfa9813208b050bf951Marc Blank // If we have no connectivity, just exit cleanly. ExchangeService will start us up again 16281b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // when connectivity has returned 16291b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (!hasConnectivity()) { 16301b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank userLog("No connectivity in sync; finishing sync"); 16311b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mExitStatus = EXIT_DONE; 16321b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank return; 1633aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank } 1634aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank 1635aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank // Every time through the loop we check to see if we're still syncable 1636aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank if (!target.isSyncable()) { 1637aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank mExitStatus = EXIT_DONE; 1638aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank return; 16391b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 1640ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 16417531be7774769c84b499b1de5dc46da3a9468316Marc Blank // Now, handle various requests 1642d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank while (true) { 16437531be7774769c84b499b1de5dc46da3a9468316Marc Blank Request req = null; 1644b20a744b01fef0082b106f44612a3d0fec361800Marc Blank 1645b20a744b01fef0082b106f44612a3d0fec361800Marc Blank if (mRequestQueue.isEmpty()) { 1646b20a744b01fef0082b106f44612a3d0fec361800Marc Blank break; 1647b20a744b01fef0082b106f44612a3d0fec361800Marc Blank } else { 1648b20a744b01fef0082b106f44612a3d0fec361800Marc Blank req = mRequestQueue.peek(); 1649d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 16507531be7774769c84b499b1de5dc46da3a9468316Marc Blank 16517531be7774769c84b499b1de5dc46da3a9468316Marc Blank // Our two request types are PartRequest (loading attachment) and 16527531be7774769c84b499b1de5dc46da3a9468316Marc Blank // MeetingResponseRequest (respond to a meeting request) 16537531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (req instanceof PartRequest) { 1654c0dce2284e50f2b638bad8c1fa6e2028e46ea5d9Marc Blank TrafficStats.setThreadStatsTag( 1655c0dce2284e50f2b638bad8c1fa6e2028e46ea5d9Marc Blank TrafficFlags.getAttachmentFlags(mContext, mAccount)); 1656bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank new AttachmentLoader(this, (PartRequest)req).loadAttachment(); 1657c0dce2284e50f2b638bad8c1fa6e2028e46ea5d9Marc Blank TrafficStats.setThreadStatsTag(TrafficFlags.getSyncFlags(mContext, mAccount)); 16587531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else if (req instanceof MeetingResponseRequest) { 16597531be7774769c84b499b1de5dc46da3a9468316Marc Blank sendMeetingResponse((MeetingResponseRequest)req); 16604471a6960d352242cc65bddf7888cc5335840c74Marc Blank } else if (req instanceof MessageMoveRequest) { 16614471a6960d352242cc65bddf7888cc5335840c74Marc Blank messageMoveRequest((MessageMoveRequest)req); 16627531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 16637531be7774769c84b499b1de5dc46da3a9468316Marc Blank 16647531be7774769c84b499b1de5dc46da3a9468316Marc Blank // If there's an exception handling the request, we'll throw it 16657531be7774769c84b499b1de5dc46da3a9468316Marc Blank // Otherwise, we remove the request 1666b20a744b01fef0082b106f44612a3d0fec361800Marc Blank mRequestQueue.remove(); 1667b20a744b01fef0082b106f44612a3d0fec361800Marc Blank } 1668b20a744b01fef0082b106f44612a3d0fec361800Marc Blank 1669b20a744b01fef0082b106f44612a3d0fec361800Marc Blank // Don't sync if we've got nothing to do 1670b20a744b01fef0082b106f44612a3d0fec361800Marc Blank if (!moreAvailable) { 1671b20a744b01fef0082b106f44612a3d0fec361800Marc Blank continue; 1672d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 1673d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank 16747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank Serializer s = new Serializer(); 1675c5f958fb9eeafb267f23f2e25769d345be5b22adMarc Blank 1676ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String className = target.getCollectionName(); 167748af7392c82262d17700e3fbdccf3a582809d449Marc Blank String syncKey = target.getSyncKey(); 16788d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank userLog("sync, sending ", className, " syncKey: ", syncKey); 16797c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_SYNC) 16807c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .start(Tags.SYNC_COLLECTIONS) 1681e7d9602fce0d4b404d68716da7eb0567da9dad47Marc Blank .start(Tags.SYNC_COLLECTION); 1682e7d9602fce0d4b404d68716da7eb0567da9dad47Marc Blank // The "Class" element is removed in EAS 12.1 and later versions 1683e7d9602fce0d4b404d68716da7eb0567da9dad47Marc Blank if (mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE) { 1684e7d9602fce0d4b404d68716da7eb0567da9dad47Marc Blank s.data(Tags.SYNC_CLASS, className); 1685e7d9602fce0d4b404d68716da7eb0567da9dad47Marc Blank } 1686e7d9602fce0d4b404d68716da7eb0567da9dad47Marc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey) 1687e489e48b50bf8a44ddeccd12efa029f8a7b5f85bMarc Blank .data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId); 1688ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 168916b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // Start with the default timeout 169016b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank int timeout = COMMAND_TIMEOUT; 1691e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank boolean initialSync = syncKey.equals("0"); 1692e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // EAS doesn't allow GetChanges in an initial sync; sending other options 1693e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // appears to cause the server to delay its response in some cases, and this delay 1694e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // can be long enough to result in an IOException and total failure to sync. 1695e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Therefore, we don't send any options with the initial sync. 1696e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Set the truncation amount, body preference, lookback, etc. 1697e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank target.sendSyncOptions(mProtocolVersionDouble, s, initialSync); 1698e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (initialSync) { 1699e489e48b50bf8a44ddeccd12efa029f8a7b5f85bMarc Blank // Use enormous timeout for initial sync, which empirically can take a while longer 1700847322e4b75287643a8381e28401ee5cf4945a44Yu Ping Hu timeout = (int)(120 * DateUtils.SECOND_IN_MILLIS); 1701ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1702ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Send our changes up to the server 170361c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank if (mUpsyncFailed) { 170461c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank if (Eas.USER_LOG) { 1705942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.d(TAG, "Inhibiting upsync this cycle"); 170661c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank } 170761c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank } else { 170861c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank target.sendLocalChanges(s); 170961c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank } 1710ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 17117c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.end().end().end().done(); 1712bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank EasResponse resp = sendHttpClientPost("Sync", new ByteArrayEntity(s.toByteArray()), 171316b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank timeout); 171454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank try { 1715bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank int code = resp.getStatus(); 171654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (code == HttpStatus.SC_OK) { 171754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // In EAS 12.1, we can get "empty" sync responses, which indicate that there are 171854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // no changes in the mailbox; handle that case here 1719bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // There are two cases here; if we get back a compressed stream (GZIP), we won't 1720bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // know until we try to parse it (and generate an EmptyStreamException). If we 1721bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // get uncompressed data, the response will be empty (i.e. have zero length) 1722bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank boolean emptyStream = false; 1723bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank if (!resp.isEmpty()) { 1724bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank InputStream is = resp.getInputStream(); 1725bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank try { 1726bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank moreAvailable = target.parse(is); 172761c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank // If we inhibited upsync, we need yet another sync 172861c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank if (mUpsyncFailed) { 172961c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank moreAvailable = true; 173061c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank } 173161c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank 1732bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank if (target.isLooping()) { 1733bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank loopingCount++; 1734bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank userLog("** Looping: " + loopingCount); 1735bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // After the maximum number of loops, we'll set moreAvailable to 1736bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // false and allow the sync loop to terminate 1737bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank if (moreAvailable && (loopingCount > MAX_LOOPING_COUNT)) { 1738bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank userLog("** Looping force stopped"); 1739bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank moreAvailable = false; 1740bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 1741bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } else { 1742bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank loopingCount = 0; 1743bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 174461c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank 174561c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank // Cleanup clears out the updated/deleted tables, and we don't want to 174661c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank // do that if our upsync failed; clear the flag otherwise 174761c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank if (!mUpsyncFailed) { 174861c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank target.cleanup(); 174961c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank } else { 175061c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank mUpsyncFailed = false; 175161c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank } 1752bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } catch (EmptyStreamException e) { 1753bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank userLog("Empty stream detected in GZIP response"); 1754bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank emptyStream = true; 175577186bb1a174432ef272584374942d8b9228e39cMarc Blank } catch (CommandStatusException e) { 175677186bb1a174432ef272584374942d8b9228e39cMarc Blank // TODO 14.1 175777186bb1a174432ef272584374942d8b9228e39cMarc Blank int status = e.mStatus; 175877186bb1a174432ef272584374942d8b9228e39cMarc Blank if (CommandStatus.isNeedsProvisioning(status)) { 1759c379d9df9eb8a5ee3ce0a308a146e8d2c73ef8e3Marc Blank mExitStatus = EXIT_SECURITY_FAILURE; 176077186bb1a174432ef272584374942d8b9228e39cMarc Blank } else if (CommandStatus.isDeniedAccess(status)) { 176177186bb1a174432ef272584374942d8b9228e39cMarc Blank mExitStatus = EXIT_ACCESS_DENIED; 176277186bb1a174432ef272584374942d8b9228e39cMarc Blank } else if (CommandStatus.isTransientError(status)) { 176377186bb1a174432ef272584374942d8b9228e39cMarc Blank mExitStatus = EXIT_IO_ERROR; 176477186bb1a174432ef272584374942d8b9228e39cMarc Blank } else { 176577186bb1a174432ef272584374942d8b9228e39cMarc Blank mExitStatus = EXIT_EXCEPTION; 176677186bb1a174432ef272584374942d8b9228e39cMarc Blank } 176777186bb1a174432ef272584374942d8b9228e39cMarc Blank return; 1768bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 1769bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } else { 1770bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank emptyStream = true; 1771bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 1772bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank 1773bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank if (emptyStream) { 1774fd7aac1fbda199ec62f8ea7f1d1edaf15018ba37Marc Blank // Make sure we get rid of updates/deletes 1775fd7aac1fbda199ec62f8ea7f1d1edaf15018ba37Marc Blank target.cleanup(); 177654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // If this happens, exit cleanly, and change the interval from push to ping 177754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank // if necessary 177854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Empty sync response; finishing"); 177954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) { 178054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Changing mailbox from push to ping"); 178154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank ContentValues cv = new ContentValues(); 178254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING); 178354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank mContentResolver.update( 178454ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId), 178554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank cv, null, null); 178654ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 178754ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank if (mRequestQueue.isEmpty()) { 178854ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank mExitStatus = EXIT_DONE; 178954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank return; 179054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } else { 179154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank continue; 179254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 1793b20a744b01fef0082b106f44612a3d0fec361800Marc Blank } 17948d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank } else { 179554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank userLog("Sync response error: ", code); 1796ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu if (resp.isProvisionError()) { 1797c379d9df9eb8a5ee3ce0a308a146e8d2c73ef8e3Marc Blank mExitStatus = EXIT_SECURITY_FAILURE; 1798ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu } else if (resp.isAuthError()) { 179954ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank mExitStatus = EXIT_LOGIN_FAILURE; 180054ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } else { 180154ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank mExitStatus = EXIT_IO_ERROR; 180254ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } 180354ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank return; 1804ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 180554ca4d97c7c0955b902bea9eb2aa1cd54d435878Marc Blank } finally { 1806bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank resp.close(); 1807ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1808ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 18091b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mExitStatus = EXIT_DONE; 1810ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1811ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 181222e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank protected boolean setupService() { 1813d0bb59f5d1c7e1d8b6c2350d3ad769a2073a0230Marc Blank synchronized(getSynchronizer()) { 1814d0bb59f5d1c7e1d8b6c2350d3ad769a2073a0230Marc Blank mThread = Thread.currentThread(); 1815d0bb59f5d1c7e1d8b6c2350d3ad769a2073a0230Marc Blank android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); 1816d0bb59f5d1c7e1d8b6c2350d3ad769a2073a0230Marc Blank TAG = mThread.getName(); 1817d0bb59f5d1c7e1d8b6c2350d3ad769a2073a0230Marc Blank } 18187ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank // Make sure account and mailbox are always the latest from the database 18197ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank mAccount = Account.restoreAccountWithId(mContext, mAccount.mId); 182022e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank if (mAccount == null) return false; 18217ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId); 182222e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank if (mMailbox == null) return false; 1823ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank HostAuth ha = HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv); 182422e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank if (ha == null) return false; 1825ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mHostAddress = ha.mAddress; 1826ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUserName = ha.mLogin; 1827ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mPassword = ha.mPassword; 182836a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo 182936a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo try { 1830e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank setConnectionParameters(ha); 183136a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo } catch (CertificateException e) { 183236a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo userLog("Couldn't retrieve certificate for connection"); 183336a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo return false; 183436a3e5257d0bb0be44479cea75a20c46b078db17Ben Komalo } 18353852792f1c79dcfca6dc9be67f11e4a53788a462Marc Blank 1836fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank // Set up our protocol version from the Account 18373852792f1c79dcfca6dc9be67f11e4a53788a462Marc Blank mProtocolVersion = mAccount.mProtocolVersion; 1838fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank // If it hasn't been set up, start with default version 1839fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank if (mProtocolVersion == null) { 1840d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank mProtocolVersion = Eas.DEFAULT_PROTOCOL_VERSION; 184154250c7c23b43b644961a8f241a3a3edcc9c796eMarc Blank } 1842cb5c48824628e93e98ca24edec46f05c54851af1Marc Blank mProtocolVersionDouble = Eas.getProtocolVersionDouble(mProtocolVersion); 18435a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo 18445a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo // Do checks to address historical policy sets. 18455a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo Policy policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 184655c8432b7d7e183cffdcf441b71a04c2d301d96cBen Komalo if ((policy != null) && policy.mRequireEncryptionExternal) { 18475a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo // External storage encryption is not supported at this time. In a previous release, 18485a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo // prior to the system supporting true removable storage on Honeycomb, we accepted 18495a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo // this since we emulated external storage on partitions that could be encrypted. 18505a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo // If that was set before, we must clear it out now that the system supports true 18515a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo // removable storage (which can't be encrypted). 18525a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo resetSecurityPolicies(); 18535a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo } 185422e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank return true; 18557ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank } 18567ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank 18575a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo /** 18585a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo * Clears out the security policies associated with the account, forcing a provision error 18595a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo * and a re-sync of the policy information for the account. 18605a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo */ 1861e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank @SuppressWarnings("deprecation") 1862e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank void resetSecurityPolicies() { 18635a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo ContentValues cv = new ContentValues(); 18645a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo cv.put(AccountColumns.SECURITY_FLAGS, 0); 18655a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo cv.putNull(AccountColumns.SECURITY_SYNC_KEY); 18665a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo long accountId = mAccount.mId; 18675a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo mContentResolver.update(ContentUris.withAppendedId( 18685a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo Account.CONTENT_URI, accountId), cv, null, null); 18695a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo } 18705a4d4bc3a4c5f03c0cd628ac000a9e8ac094d944Ben Komalo 1871e0ac5d26481e68c25b976136a6d376c39977e779Ben Komalo @Override 18727ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank public void run() { 18730164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank try { 18740164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank // Make sure account and mailbox are still valid 18750164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank if (!setupService()) return; 18760164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank // If we've been stopped, we're done 18770164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank if (mStop) return; 1878fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank 18790164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank // Whether or not we're the account mailbox 18800164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank try { 18810164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank mDeviceId = ExchangeService.getDeviceId(mContext); 18820164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank int trafficFlags = TrafficFlags.getSyncFlags(mContext, mAccount); 18830164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank if ((mMailbox == null) || (mAccount == null)) { 18840164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank return; 18850164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } else { 18860164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank AbstractSyncAdapter target; 18870164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank if (mMailbox.mType == Mailbox.TYPE_CONTACTS) { 1888ff9fed5ccdd57d5408e759f0ca6c41e79e09e999Martin Hibdon // ContactsSyncAdapter is gone, and this class is deprecated. 1889ff9fed5ccdd57d5408e759f0ca6c41e79e09e999Martin Hibdon // Just leaving this commented out here for reference. 1890ff9fed5ccdd57d5408e759f0ca6c41e79e09e999Martin Hibdon// TrafficStats.setThreadStatsTag(trafficFlags | TrafficFlags.DATA_CONTACTS); 1891ff9fed5ccdd57d5408e759f0ca6c41e79e09e999Martin Hibdon// target = new ContactsSyncAdapter(this); 1892ff9fed5ccdd57d5408e759f0ca6c41e79e09e999Martin Hibdon target = null; 18930164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } else if (mMailbox.mType == Mailbox.TYPE_CALENDAR) { 1894de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // CalendarSyncAdapter is gone, and this class is deprecated. 1895de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Just leaving this commented out here for reference. 1896de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// TrafficStats.setThreadStatsTag(trafficFlags | TrafficFlags.DATA_CALENDAR); 1897de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// target = new CalendarSyncAdapter(this); 1898de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon target = null; 18990164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } else { 19000164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank TrafficStats.setThreadStatsTag(trafficFlags | TrafficFlags.DATA_EMAIL); 19010164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank target = new EmailSyncAdapter(this); 19020164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } 19030164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank // We loop because someone might have put a request in while we were syncing 19040164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank // and we've missed that opportunity... 19050164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank do { 19060164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank if (mRequestTime != 0) { 19070164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank userLog("Looping for user request..."); 19080164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank mRequestTime = 0; 19090164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } 19100164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank sync(target); 19110164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } while (mRequestTime != 0); 1912ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 19130164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } catch (EasAuthenticationException e) { 19140164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank userLog("Caught authentication error"); 19150164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank mExitStatus = EXIT_LOGIN_FAILURE; 19160164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } catch (IOException e) { 19170164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank String message = e.getMessage(); 19180164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank userLog("Caught IOException: ", (message == null) ? "No message" : message); 19190164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank mExitStatus = EXIT_IO_ERROR; 19200164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } catch (Exception e) { 19210164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank userLog("Uncaught exception in EasSyncService", e); 19220164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } finally { 19230164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank int status; 1924ac7a749800da2d51a55845344e9c91f6332913b8Marc Blank ExchangeService.done(this); 19250164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank if (!mStop) { 19260164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank userLog("Sync finished"); 19270164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank switch (mExitStatus) { 19280164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank case EXIT_IO_ERROR: 19290164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank status = EmailServiceStatus.CONNECTION_ERROR; 19300164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank break; 19310164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank case EXIT_DONE: 19320164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank status = EmailServiceStatus.SUCCESS; 19330164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank ContentValues cv = new ContentValues(); 19340164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis()); 19350164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank String s = "S" + mSyncReason + ':' + status + ':' + mChangeCount; 19360164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank cv.put(Mailbox.SYNC_STATUS, s); 19370164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, 19380164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank mMailboxId), cv, null, null); 19390164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank break; 19400164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank case EXIT_LOGIN_FAILURE: 19410164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank status = EmailServiceStatus.LOGIN_FAILED; 19420164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank break; 19430164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank case EXIT_SECURITY_FAILURE: 19440164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank status = EmailServiceStatus.SECURITY_FAILURE; 19450164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank // Ask for a new folder list. This should wake up the account mailbox; a 19460164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank // security error in account mailbox should start provisioning 19470164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank ExchangeService.reloadFolderList(mContext, mAccount.mId, true); 19480164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank break; 19490164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank case EXIT_ACCESS_DENIED: 19500164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank status = EmailServiceStatus.ACCESS_DENIED; 19510164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank break; 19520164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank default: 19530164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank status = EmailServiceStatus.REMOTE_EXCEPTION; 19540164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank errorLog("Sync ended due to an exception."); 19550164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank break; 1956ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 19570164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } else { 19580164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank userLog("Stopped sync finished."); 19590164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank status = EmailServiceStatus.SUCCESS; 19605c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank } 19612fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank 19620164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank // Make sure ExchangeService knows about this 19630164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank ExchangeService.kick("sync finished"); 19640164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } 19650164f9da9a96fd8146c4a61e6de01734206baf4cMarc Blank } catch (ProviderUnavailableException e) { 1966942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.e(TAG, "EmailProvider unavailable; sync ended prematurely"); 19670b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo } 1968ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1969ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank} 1970