ANDROID: IMPLEMENT PERSISTENT COOKIE STORE for HTTPCLIENT

Rajat Kumar
4 min readApr 16, 2020

Persistent cookie store class implementation using cookieStore interface for HttpClient.

PersistentCookieStore(Context context) is used to instantiate by passingapplication context.  Once it is initialised then, it should be passedto httpClient instance via setCookieStore(CookieStore) method.Every cookie in http/https calls will automatically written and readin this cookie store. This cookie information can also be synced upwith android app webview using CookieManager class as below .
CookieSyncManager.createInstance(mContext);CookieManager.getInstance().setAcceptCookie(true);
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import org.apache.http.client.CookieStore;

import org.apache.http.cookie.Cookie;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;



public class PersistentCookieStore implements CookieStore {

private static final String LOG_TAG = "CookieStore";
private static final String COOKIE_PREFS = "CookiePrefsFile";

private static final String COOKIE_NAME_STORE = "names";

private static final String COOKIE_NAME_PREFIX = "cookie_";

private final ConcurrentHashMap< String, Cookie> cookies;

private final SharedPreferences cookiePrefs;

private boolean omitNonPersistentCookies = false;


/** * Construct a persistent cookie store.

*

* @param ctx Context to attach cookie store to */



public PersistentCookieStore(Context ctx) {
cookiePrefs = ctx.getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE);

cookies = new ConcurrentHashMap < String, Cookie > ();

// Load any previously stored cookies into the store

String storedCookieNames = cookiePrefs.getString(COOKIE_NAME_STORE, null);

if (storedCookieNames != null) {
String[] cookieNames = TextUtils.split(storedCookieNames, ",");

for (String name: cookieNames) {
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
if (encodedCookie != null) {
Cookie decodedCookie = decodeCookie(encodedCookie);

if (decodedCookie != null) {
cookies.put(name, decodedCookie);

}
}
}
// Clear out expired cookies

clearExpired(new Date());

}
}

@Override
public void addCookie(Cookie cookie) {
if (omitNonPersistentCookies && !cookie.isPersistent())
return;

if (!cookie.getDomain().equalsIgnoreCase(Settings.COOKIE_DOMAIN))
return;

String name = cookie.getName() + cookie.getDomain();

// Save cookie into local store, or remove if expired

if (!cookie.isExpired(new Date())) {
cookies.put(name, cookie);

} else {
cookies.remove(name);

}
// Save cookie into persistent store

SharedPreferences.Editor prefsWriter = cookiePrefs.edit();

prefsWriter.putString(COOKIE_NAME_STORE, TextUtils.join(",", cookies.keySet()));

prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableCookie(cookie)));

prefsWriter.apply();

}

@Override
public void clear() {
// Clear cookies from persistent store

SharedPreferences.Editor prefsWriter = cookiePrefs.edit();

for (String name: cookies.keySet()) {
prefsWriter.remove(COOKIE_NAME_PREFIX + name);

}
prefsWriter.remove(COOKIE_NAME_STORE);

prefsWriter.apply();

// Clear cookies from local store

cookies.clear();

}

@Override
public boolean clearExpired(Date date) {
boolean clearedAny = false;

SharedPreferences.Editor prefsWriter = cookiePrefs.edit();

for (ConcurrentHashMap.Entry < String, Cookie > entry: cookies.entrySet()) {
String name = entry.getKey();

Cookie cookie = entry.getValue();

if (cookie.isExpired(date) || cookie.getExpiryDate() == null) {
// Clear cookies from local store

cookies.remove(name);

// Clear cookies from persistent store

prefsWriter.remove(COOKIE_NAME_PREFIX + name);

// We've cleared at least one

clearedAny = true; }
}
// Update names in persistent store

if (clearedAny) {
prefsWriter.putString(COOKIE_NAME_STORE, TextUtils.join(",", cookies.keySet()));

}
prefsWriter.apply();

return clearedAny;

}

@Override
public List < Cookie > getCookies() {
return new ArrayList < Cookie > (cookies.values());

}

/**
* Will make PersistentCookieStore instance ignore Cookies,which are non-persistent by* signature (`Cookie.isPersistent`)
*
* @param omitNonPersistentCookies true if non-persistent cookiesshould be omited
*/

public void setOmitNonPersistentCookies(boolean omitNonPersistentCookies) {
this.omitNonPersistentCookies = omitNonPersistentCookies;

}

/** * Non-standard helper method, to delete cookie

*

* @param cookie cookie to be removed

*/

public void deleteCookie(Cookie cookie) {
String name = cookie.getName() + cookie.getDomain();

cookies.remove(name);

SharedPreferences.Editor prefsWriter = cookiePrefs.edit();

prefsWriter.remove(COOKIE_NAME_PREFIX + name);

prefsWriter.apply();

}

/** * Serializes Cookie object into String *
* @param cookie cookie to be encoded, can be null* @return cookie encoded as String */


protected String encodeCookie(SerializableCookie cookie) {
if (cookie == null)
return null;

ByteArrayOutputStream os = new ByteArrayOutputStream();

try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);

outputStream.writeObject(cookie);
} catch (IOException e) {
Log.d(LOG_TAG, "IOException in encodeCookie", e);

return null;

}
return byteArrayToHexString(os.toByteArray());

}

/** * Returns cookie decoded from cookie string *
* @param cookieString string of cookie as returned from http request* @return decoded cookie or null if exception occured

*/


protected Cookie decodeCookie(String cookieString) {
byte[] bytes = hexStringToByteArray(cookieString);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);

Cookie cookie = null;

try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); cookie = ((SerializableCookie) objectInputStream.readObject()).getCookie();

} catch (IOException e) {
Log.d(LOG_TAG, "IOException in decodeCookie", e)

} catch (ClassNotFoundException e) {
Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e); }
return cookie; }

/** * Using some super basic byte array <-> hex conversions so we
don't have to rely on any * large Base64 libraries. Can be overriddenif you like!

*

* @param bytes byte array to be converted

* @return string containing hex values */


protected String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);

for (byte element: bytes) {
int v = element & 0xff;

if (v < 16) {
sb.append('0'); }
sb.append(Integer.toHexString(v));

}
return sb.toString().toUpperCase(Locale.US);

}

/** * Converts hex values from strings to byte array

*

* @param hexString string of hex-encoded values

* @return decoded byte array

*/


protected byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();

byte[] data = new byte[len / 2];

for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte)((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));

}
return data;

}
}

--

--