1ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu/* 2ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Copyright (C) 2013 The Android Open Source Project 3ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * 4ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Licensed under the Apache License, Version 2.0 (the "License"); 5ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * you may not use this file except in compliance with the License. 6ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * You may obtain a copy of the License at 7ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * 8ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * http://www.apache.org/licenses/LICENSE-2.0 9ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * 10ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * Unless required by applicable law or agreed to in writing, software 11ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * distributed under the License is distributed on an "AS IS" BASIS, 12ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * See the License for the specific language governing permissions and 14ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu * limitations under the License. 15ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu */ 16ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 179383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hupackage com.android.exchange.eas; 189383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 199383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport android.content.Context; 20dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Huimport android.text.format.DateUtils; 219383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 229383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport com.android.emailcommon.provider.HostAuth; 239383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport com.android.emailcommon.utility.EmailClientConnectionManager; 249383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport com.android.exchange.Eas; 259383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport com.android.mail.utils.LogUtils; 269383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 279383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport org.apache.http.conn.params.ConnManagerPNames; 289383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport org.apache.http.conn.params.ConnPerRoute; 299383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport org.apache.http.conn.routing.HttpRoute; 309383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport org.apache.http.params.BasicHttpParams; 319383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport org.apache.http.params.HttpParams; 329383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 33d92a75c707461188e8743149476e8f49ef191b42Tony Mantlerimport java.security.cert.CertificateException; 349383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport java.util.HashMap; 359383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 369383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu/** 379383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * Manage all {@link EmailClientConnectionManager}s used by Exchange operations. 389383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * When making connections for persisted accounts, this class will cache and reuse connections 399383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * as much as possible. All access of connection objects should accordingly go through this class. 409383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * 419383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * We use {@link HostAuth}'s id as the cache key. Multiple calls to {@link #getConnectionManager} 429383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * with {@link HostAuth} objects with the same id will get the same connection object returned, 439383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * i.e. we assume that the rest of the contents of the {@link HostAuth} objects are also the same, 449383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * not just the id. If the {@link HostAuth} changes or is deleted, {@link #uncacheConnectionManager} 459383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * must be called. 469383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * 479383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * This cache is a singleton since the whole point is to not have multiples. 489383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu */ 499383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hupublic class EasConnectionCache { 509383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 51dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu /** The max length of time we want to keep a connection in the cache. */ 52dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu private static final long MAX_LIFETIME = 10 * DateUtils.MINUTE_IN_MILLIS; 53dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu 549383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu private final HashMap<Long, EmailClientConnectionManager> mConnectionMap; 55dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu /** The creation time of connections in mConnectionMap. */ 56dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu private final HashMap<Long, Long> mConnectionCreationTimes; 579383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 589383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu private static final ConnPerRoute sConnPerRoute = new ConnPerRoute() { 599383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu @Override 609383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu public int getMaxForRoute(final HttpRoute route) { 619383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu return 8; 629383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 639383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu }; 649383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 659383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu /** The singleton instance of the cache. */ 669383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu private static EasConnectionCache sCache = null; 679383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 689383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu /** Accessor for the cache singleton. */ 699383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu public static EasConnectionCache instance() { 709383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu if (sCache == null) { 719383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu sCache = new EasConnectionCache(); 729383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 739383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu return sCache; 749383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 759383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 769383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu private EasConnectionCache() { 779383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu mConnectionMap = new HashMap<Long, EmailClientConnectionManager>(); 78dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu mConnectionCreationTimes = new HashMap<Long, Long>(); 799383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 809383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 819383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu /** 829383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * Create an {@link EmailClientConnectionManager} for this {@link HostAuth}. 839383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @param context The {@link Context}. 849383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @param hostAuth The {@link HostAuth} to which we want to connect. 859383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @return The {@link EmailClientConnectionManager} for hostAuth. 869383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu */ 879383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu private EmailClientConnectionManager createConnectionManager(final Context context, 88d92a75c707461188e8743149476e8f49ef191b42Tony Mantler final HostAuth hostAuth) 89d92a75c707461188e8743149476e8f49ef191b42Tony Mantler throws CertificateException { 903054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu LogUtils.d(Eas.LOG_TAG, "Creating new connection manager for HostAuth %d", hostAuth.mId); 919383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu final HttpParams params = new BasicHttpParams(); 929383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25); 939383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute); 94d92a75c707461188e8743149476e8f49ef191b42Tony Mantler final EmailClientConnectionManager mgr = 95d92a75c707461188e8743149476e8f49ef191b42Tony Mantler EmailClientConnectionManager.newInstance(context, params, hostAuth); 96d92a75c707461188e8743149476e8f49ef191b42Tony Mantler 97d92a75c707461188e8743149476e8f49ef191b42Tony Mantler mgr.registerClientCert(context, hostAuth); 98d92a75c707461188e8743149476e8f49ef191b42Tony Mantler 99d92a75c707461188e8743149476e8f49ef191b42Tony Mantler return mgr; 1009383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 1019383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 1029383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu /** 1039383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * Get the correct {@link EmailClientConnectionManager} for a {@link HostAuth} from our cache. 1049383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * If it's not in the cache, create and add it. 105dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu * If the cached connection is old, recreate it. 1069383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @param context The {@link Context}. 1079383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @param hostAuth The {@link HostAuth} to which we want to connect. 1089383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @return The {@link EmailClientConnectionManager} for hostAuth. 1099383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu */ 1109383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu private synchronized EmailClientConnectionManager getCachedConnectionManager( 111d92a75c707461188e8743149476e8f49ef191b42Tony Mantler final Context context, final HostAuth hostAuth) 112d92a75c707461188e8743149476e8f49ef191b42Tony Mantler throws CertificateException { 1139383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu EmailClientConnectionManager connectionManager = mConnectionMap.get(hostAuth.mId); 114dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu final long now = System.currentTimeMillis(); 115dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu if (connectionManager != null) { 116dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu final long lifetime = now - mConnectionCreationTimes.get(hostAuth.mId); 117dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu if (lifetime > MAX_LIFETIME) { 1183054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu LogUtils.d(Eas.LOG_TAG, "Aging out connection manager for HostAuth %d", 1193054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu hostAuth.mId); 120dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu uncacheConnectionManager(hostAuth); 121dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu connectionManager = null; 122dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu } 123dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu } 1249383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu if (connectionManager == null) { 1259383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu connectionManager = createConnectionManager(context, hostAuth); 1269383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu mConnectionMap.put(hostAuth.mId, connectionManager); 127dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu mConnectionCreationTimes.put(hostAuth.mId, now); 128dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu } else { 1293054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu LogUtils.d(Eas.LOG_TAG, "Reusing cached connection manager for HostAuth %d", 1303054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu hostAuth.mId); 1319383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 1329383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu return connectionManager; 1339383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 1349383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 1359383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu /** 1369383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * Get the correct {@link EmailClientConnectionManager} for a {@link HostAuth}. If the 1379383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * {@link HostAuth} is persistent, then use the cache for this request. 1389383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @param context The {@link Context}. 1399383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @param hostAuth The {@link HostAuth} to which we want to connect. 1409383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @return The {@link EmailClientConnectionManager} for hostAuth. 1419383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu */ 1429383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu public EmailClientConnectionManager getConnectionManager( 143d92a75c707461188e8743149476e8f49ef191b42Tony Mantler final Context context, final HostAuth hostAuth) 144d92a75c707461188e8743149476e8f49ef191b42Tony Mantler throws CertificateException { 1459383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu final EmailClientConnectionManager connectionManager; 1469383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu // We only cache the connection manager for persisted HostAuth objects, i.e. objects 1479383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu // whose ids are permanent and won't get reused by other transient HostAuth objects. 1489383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu if (hostAuth.isSaved()) { 1499383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu connectionManager = getCachedConnectionManager(context, hostAuth); 1509383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } else { 1519383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu connectionManager = createConnectionManager(context, hostAuth); 1529383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 1539383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu return connectionManager; 1549383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 1559383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu 1569383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu /** 1579383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * Remove a connection manager from the cache. This is necessary when a {@link HostAuth} is 1589383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * redirected or otherwise altered. It's not strictly necessary but good to also call this 1599383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * when a {@link HostAuth} is deleted, i.e. when an account is removed. 1609383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu * @param hostAuth The {@link HostAuth} whose connection manager should be deleted. 1619383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu */ 1629383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu public synchronized void uncacheConnectionManager(final HostAuth hostAuth) { 1633054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu LogUtils.d(Eas.LOG_TAG, "Uncaching connection manager for HostAuth %d", hostAuth.mId); 164dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu EmailClientConnectionManager connectionManager = mConnectionMap.get(hostAuth.mId); 165dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu if (connectionManager != null) { 166dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu connectionManager.shutdown(); 167dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu } 1689383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu mConnectionMap.remove(hostAuth.mId); 169dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu mConnectionCreationTimes.remove(hostAuth.mId); 1709383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu } 1719383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu} 172