blob: e4ce7b2beb5bce60d63f84b64c71634b9965bc9e [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.exchange.eas;
import android.content.Context;
import android.text.format.DateUtils;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.utility.EmailClientConnectionManager;
import com.android.exchange.Eas;
import com.android.mail.utils.LogUtils;
import org.apache.http.conn.params.ConnManagerPNames;
import org.apache.http.conn.params.ConnPerRoute;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import java.util.HashMap;
/**
* Manage all {@link EmailClientConnectionManager}s used by Exchange operations.
* When making connections for persisted accounts, this class will cache and reuse connections
* as much as possible. All access of connection objects should accordingly go through this class.
*
* We use {@link HostAuth}'s id as the cache key. Multiple calls to {@link #getConnectionManager}
* with {@link HostAuth} objects with the same id will get the same connection object returned,
* i.e. we assume that the rest of the contents of the {@link HostAuth} objects are also the same,
* not just the id. If the {@link HostAuth} changes or is deleted, {@link #uncacheConnectionManager}
* must be called.
*
* This cache is a singleton since the whole point is to not have multiples.
*/
public class EasConnectionCache {
/** The max length of time we want to keep a connection in the cache. */
private static final long MAX_LIFETIME = 10 * DateUtils.MINUTE_IN_MILLIS;
private final HashMap<Long, EmailClientConnectionManager> mConnectionMap;
/** The creation time of connections in mConnectionMap. */
private final HashMap<Long, Long> mConnectionCreationTimes;
private static final ConnPerRoute sConnPerRoute = new ConnPerRoute() {
@Override
public int getMaxForRoute(final HttpRoute route) {
return 8;
}
};
/** The singleton instance of the cache. */
private static EasConnectionCache sCache = null;
/** Accessor for the cache singleton. */
public static EasConnectionCache instance() {
if (sCache == null) {
sCache = new EasConnectionCache();
}
return sCache;
}
private EasConnectionCache() {
mConnectionMap = new HashMap<Long, EmailClientConnectionManager>();
mConnectionCreationTimes = new HashMap<Long, Long>();
}
/**
* Create an {@link EmailClientConnectionManager} for this {@link HostAuth}.
* @param context The {@link Context}.
* @param hostAuth The {@link HostAuth} to which we want to connect.
* @return The {@link EmailClientConnectionManager} for hostAuth.
*/
private EmailClientConnectionManager createConnectionManager(final Context context,
final HostAuth hostAuth) {
LogUtils.d(Eas.LOG_TAG, "Creating new connection manager for HostAuth %d", hostAuth.mId);
final HttpParams params = new BasicHttpParams();
params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute);
return EmailClientConnectionManager.newInstance(context, params, hostAuth);
}
/**
* Get the correct {@link EmailClientConnectionManager} for a {@link HostAuth} from our cache.
* If it's not in the cache, create and add it.
* If the cached connection is old, recreate it.
* @param context The {@link Context}.
* @param hostAuth The {@link HostAuth} to which we want to connect.
* @return The {@link EmailClientConnectionManager} for hostAuth.
*/
private synchronized EmailClientConnectionManager getCachedConnectionManager(
final Context context, final HostAuth hostAuth) {
EmailClientConnectionManager connectionManager = mConnectionMap.get(hostAuth.mId);
final long now = System.currentTimeMillis();
if (connectionManager != null) {
final long lifetime = now - mConnectionCreationTimes.get(hostAuth.mId);
if (lifetime > MAX_LIFETIME) {
LogUtils.d(Eas.LOG_TAG, "Aging out connection manager for HostAuth %d",
hostAuth.mId);
uncacheConnectionManager(hostAuth);
connectionManager = null;
}
}
if (connectionManager == null) {
connectionManager = createConnectionManager(context, hostAuth);
mConnectionMap.put(hostAuth.mId, connectionManager);
mConnectionCreationTimes.put(hostAuth.mId, now);
} else {
LogUtils.d(Eas.LOG_TAG, "Reusing cached connection manager for HostAuth %d",
hostAuth.mId);
}
return connectionManager;
}
/**
* Get the correct {@link EmailClientConnectionManager} for a {@link HostAuth}. If the
* {@link HostAuth} is persistent, then use the cache for this request.
* @param context The {@link Context}.
* @param hostAuth The {@link HostAuth} to which we want to connect.
* @return The {@link EmailClientConnectionManager} for hostAuth.
*/
public EmailClientConnectionManager getConnectionManager(
final Context context, final HostAuth hostAuth) {
final EmailClientConnectionManager connectionManager;
// We only cache the connection manager for persisted HostAuth objects, i.e. objects
// whose ids are permanent and won't get reused by other transient HostAuth objects.
if (hostAuth.isSaved()) {
connectionManager = getCachedConnectionManager(context, hostAuth);
} else {
connectionManager = createConnectionManager(context, hostAuth);
}
return connectionManager;
}
/**
* Remove a connection manager from the cache. This is necessary when a {@link HostAuth} is
* redirected or otherwise altered. It's not strictly necessary but good to also call this
* when a {@link HostAuth} is deleted, i.e. when an account is removed.
* @param hostAuth The {@link HostAuth} whose connection manager should be deleted.
*/
public synchronized void uncacheConnectionManager(final HostAuth hostAuth) {
LogUtils.d(Eas.LOG_TAG, "Uncaching connection manager for HostAuth %d", hostAuth.mId);
EmailClientConnectionManager connectionManager = mConnectionMap.get(hostAuth.mId);
if (connectionManager != null) {
connectionManager.shutdown();
}
mConnectionMap.remove(hostAuth.mId);
mConnectionCreationTimes.remove(hostAuth.mId);
}
}