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