Fields Summary |
---|
private static CookieManager | sRef |
private static final String | LOGTAG |
private static final String | DOMAIN |
private static final String | PATH |
private static final String | EXPIRES |
private static final String | SECURE |
private static final String | MAX_AGE |
private static final String | HTTP_ONLY |
private static final String | HTTPS |
private static final char | PERIOD |
private static final char | COMMA |
private static final char | SEMICOLON |
private static final char | EQUAL |
private static final char | PATH_DELIM |
private static final char | QUESTION_MARK |
private static final char | WHITE_SPACE |
private static final char | QUOTATION |
private static final int | SECURE_LENGTH |
private static final int | HTTP_ONLY_LENGTH |
private static final int | MAX_COOKIE_LENGTH |
private static final int | MAX_COOKIE_COUNT_PER_BASE_DOMAIN |
private static final int | MAX_DOMAIN_COUNT |
private static final int | MAX_RAM_COOKIES_COUNT |
private static final int | MAX_RAM_DOMAIN_COUNT |
private Map | mCookieMap |
private boolean | mAcceptCookie |
private static final String[] | BAD_COUNTRY_2LDSThis contains a list of 2nd-level domains that aren't allowed to have
wildcards when combined with country-codes. For example: [.co.uk]. |
Methods Summary |
---|
public synchronized boolean | acceptCookie()Return whether cookie is enabled
return mAcceptCookie;
|
protected java.lang.Object | clone()
throw new CloneNotSupportedException("doesn't implement Cloneable");
|
synchronized void | deleteACookie(android.webkit.CookieManager$Cookie cookie)Package level api, called from CookieSyncManager
Delete a Cookie in the RAM
if (cookie.mode == Cookie.MODE_DELETED) {
String baseDomain = getBaseDomain(cookie.domain);
ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain);
if (cookieList != null) {
cookieList.remove(cookie);
if (cookieList.isEmpty()) {
mCookieMap.remove(baseDomain);
}
}
}
|
synchronized java.util.ArrayList | deleteLRUDomain()Package level api, called from CookieSyncManager
Delete the least recent used domains if the total cookie count in RAM
exceeds the limit
int count = 0;
int byteCount = 0;
int mapSize = mCookieMap.size();
if (mapSize < MAX_RAM_DOMAIN_COUNT) {
Collection<ArrayList<Cookie>> cookieLists = mCookieMap.values();
Iterator<ArrayList<Cookie>> listIter = cookieLists.iterator();
while (listIter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
ArrayList<Cookie> list = listIter.next();
if (Config.DEBUG) {
Iterator<Cookie> iter = list.iterator();
while (iter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
Cookie cookie = iter.next();
// 14 is 3 * sizeof(long) + sizeof(boolean)
// + sizeof(byte)
byteCount += cookie.domain.length()
+ cookie.path.length()
+ cookie.name.length()
+ cookie.value.length() + 14;
count++;
}
} else {
count += list.size();
}
}
}
ArrayList<Cookie> retlist = new ArrayList<Cookie>();
if (mapSize >= MAX_RAM_DOMAIN_COUNT || count >= MAX_RAM_COOKIES_COUNT) {
if (Config.DEBUG) {
Log.v(LOGTAG, count + " cookies used " + byteCount
+ " bytes with " + mapSize + " domains");
}
Object[] domains = mCookieMap.keySet().toArray();
int toGo = mapSize / 10 + 1;
while (toGo-- > 0){
String domain = domains[toGo].toString();
if (Config.LOGV) {
Log.v(LOGTAG, "delete domain: " + domain
+ " from RAM cache");
}
retlist.addAll(mCookieMap.get(domain));
mCookieMap.remove(domain);
}
}
return retlist;
|
private java.lang.String | getBaseDomain(java.lang.String host)Get the base domain for a give host. E.g. mail.google.com will return
google.com
int startIndex = 0;
int nextIndex = host.indexOf(PERIOD);
int lastIndex = host.lastIndexOf(PERIOD);
while (nextIndex < lastIndex) {
startIndex = nextIndex + 1;
nextIndex = host.indexOf(PERIOD, startIndex);
}
if (startIndex > 0) {
return host.substring(startIndex);
} else {
return host;
}
|
public java.lang.String | getCookie(java.lang.String url)Get cookie(s) for a given url so that it can be set to "cookie:" in http
request header.
WebAddress uri;
try {
uri = new WebAddress(url);
} catch (ParseException ex) {
Log.e(LOGTAG, "Bad address: " + url);
return null;
}
return getCookie(uri);
|
public synchronized java.lang.String | getCookie(android.net.WebAddress uri)Get cookie(s) for a given uri so that it can be set to "cookie:" in http
request header.
if (!mAcceptCookie || uri == null) {
return null;
}
String[] hostAndPath = getHostAndPath(uri);
if (hostAndPath == null) {
return null;
}
String baseDomain = getBaseDomain(hostAndPath[0]);
ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain);
if (cookieList == null) {
cookieList = CookieSyncManager.getInstance()
.getCookiesForDomain(baseDomain);
mCookieMap.put(baseDomain, cookieList);
}
long now = System.currentTimeMillis();
boolean secure = HTTPS.equals(uri.mScheme);
Iterator<Cookie> iter = cookieList.iterator();
StringBuilder ret = new StringBuilder(256);
while (iter.hasNext()) {
Cookie cookie = iter.next();
if (cookie.domainMatch(hostAndPath[0]) &&
cookie.pathMatch(hostAndPath[1])
// expires == -1 means no expires defined. Otherwise
// negative means far future
&& (cookie.expires < 0 || cookie.expires > now)
&& (!cookie.secure || secure)
&& cookie.mode != Cookie.MODE_DELETED) {
cookie.lastAcessTime = now;
if (ret.length() > 0) {
ret.append(SEMICOLON);
// according to RC2109, SEMICOLON is office separator,
// but when log in yahoo.com, it needs WHITE_SPACE too.
ret.append(WHITE_SPACE);
}
ret.append(cookie.name);
ret.append(EQUAL);
ret.append(cookie.value);
}
}
if (ret.length() > 0) {
if (Config.LOGV) {
Log.v(LOGTAG, "getCookie: uri: " + uri + " value: " + ret);
}
return ret.toString();
} else {
if (Config.LOGV) {
Log.v(LOGTAG, "getCookie: uri: " + uri
+ " But can't find cookie.");
}
return null;
}
|
private java.lang.String[] | getHostAndPath(android.net.WebAddress uri)Extract the host and path out of a uri
if (uri.mHost != null && uri.mPath != null) {
String[] ret = new String[2];
ret[0] = uri.mHost;
ret[1] = uri.mPath;
int index = ret[0].indexOf(PERIOD);
if (index == -1) {
if (uri.mScheme.equalsIgnoreCase("file")) {
// There is a potential bug where a local file path matches
// another file in the local web server directory. Still
// "localhost" is the best pseudo domain name.
ret[0] = "localhost";
} else if (!ret[0].equals("localhost")) {
return null;
}
} else if (index == ret[0].lastIndexOf(PERIOD)) {
// cookie host must have at least two periods
ret[0] = PERIOD + ret[0];
}
if (ret[1].charAt(0) != PATH_DELIM) {
return null;
}
/*
* find cookie path, e.g. for http://www.google.com, the path is "/"
* for http://www.google.com/lab/, the path is "/lab"
* for http://www.google.com/lab/foo, the path is "/lab/foo"
* for http://www.google.com/lab?hl=en, the path is "/lab"
* for http://www.google.com/lab.asp?hl=en, the path is "/lab.asp"
* Note: the path from URI has at least one "/"
* See:
* http://www.unix.com.ua/rfc/rfc2109.html
*/
index = ret[1].indexOf(QUESTION_MARK);
if (index != -1) {
ret[1] = ret[1].substring(0, index);
}
return ret;
} else
return null;
|
public static synchronized android.webkit.CookieManager | getInstance()Get a singleton CookieManager. If this is called before any
{@link WebView} is created or outside of {@link WebView} context, the
caller needs to call {@link CookieSyncManager#createInstance(Context)}
first.
if (sRef == null) {
sRef = new CookieManager();
}
return sRef;
|
synchronized java.util.ArrayList | getUpdatedCookiesSince(long last)Package level api, called from CookieSyncManager
Get a list of cookies which are updated since a given time.
ArrayList<Cookie> cookies = new ArrayList<Cookie>();
Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
while (listIter.hasNext()) {
ArrayList<Cookie> list = listIter.next();
Iterator<Cookie> iter = list.iterator();
while (iter.hasNext()) {
Cookie cookie = iter.next();
if (cookie.lastUpdateTime > last) {
cookies.add(cookie);
}
}
}
return cookies;
|
public synchronized boolean | hasCookies()Return true if there are stored cookies.
return CookieSyncManager.getInstance().hasCookies();
|
private java.util.ArrayList | parseCookie(java.lang.String host, java.lang.String path, java.lang.String cookieString)parseCookie() parses the cookieString which is a comma-separated list of
one or more cookies in the format of "NAME=VALUE; expires=DATE;
path=PATH; domain=DOMAIN_NAME; secure httponly" to a list of Cookies.
Here is a sample: IGDND=1, IGPC=ET=UB8TSNwtDmQ:AF=0; expires=Sun,
17-Jan-2038 19:14:07 GMT; path=/ig; domain=.google.com, =,
PREF=ID=408909b1b304593d:TM=1156459854:LM=1156459854:S=V-vCAU6Sh-gobCfO;
expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com which
contains 3 cookies IGDND, IGPC, PREF and an empty cookie
ArrayList<Cookie> ret = new ArrayList<Cookie>();
int index = 0;
int length = cookieString.length();
while (true) {
Cookie cookie = null;
// done
if (index < 0 || index >= length) {
break;
}
// skip white space
if (cookieString.charAt(index) == WHITE_SPACE) {
index++;
continue;
}
/*
* get NAME=VALUE; pair. detecting the end of a pair is tricky, it
* can be the end of a string, like "foo=bluh", it can be semicolon
* like "foo=bluh;path=/"; or it can be enclosed by \", like
* "foo=\"bluh bluh\";path=/"
*
* Note: in the case of "foo=bluh, bar=bluh;path=/", we interpret
* it as one cookie instead of two cookies.
*/
int semicolonIndex = cookieString.indexOf(SEMICOLON, index);
int equalIndex = cookieString.indexOf(EQUAL, index);
if (equalIndex == -1) {
// bad format, force return
break;
}
if (semicolonIndex > -1 && semicolonIndex < equalIndex) {
// empty cookie, like "; path=/", return
break;
}
cookie = new Cookie(host, path);
cookie.name = cookieString.substring(index, equalIndex);
if (cookieString.charAt(equalIndex + 1) == QUOTATION) {
index = cookieString.indexOf(QUOTATION, equalIndex + 2);
if (index == -1) {
// bad format, force return
break;
}
}
semicolonIndex = cookieString.indexOf(SEMICOLON, index);
if (semicolonIndex == -1) {
semicolonIndex = length;
}
if (semicolonIndex - equalIndex > MAX_COOKIE_LENGTH) {
// cookie is too big, trim it
cookie.value = cookieString.substring(equalIndex + 1,
equalIndex + MAX_COOKIE_LENGTH);
} else if (equalIndex + 1 == semicolonIndex
|| semicolonIndex < equalIndex) {
// these are unusual case like foo=; and foo; path=/
cookie.value = "";
} else {
cookie.value = cookieString.substring(equalIndex + 1,
semicolonIndex);
}
// get attributes
index = semicolonIndex;
while (true) {
// done
if (index < 0 || index >= length) {
break;
}
// skip white space and semicolon
if (cookieString.charAt(index) == WHITE_SPACE
|| cookieString.charAt(index) == SEMICOLON) {
index++;
continue;
}
// comma means next cookie
if (cookieString.charAt(index) == COMMA) {
index++;
break;
}
// "secure" is a known attribute doesn't use "=";
// while sites like live.com uses "secure="
if (length - index > SECURE_LENGTH
&& cookieString.substring(index, index + SECURE_LENGTH).
equalsIgnoreCase(SECURE)) {
index += SECURE_LENGTH;
cookie.secure = true;
if (cookieString.charAt(index) == EQUAL) index++;
continue;
}
// "httponly" is a known attribute doesn't use "=";
// while sites like live.com uses "httponly="
if (length - index > HTTP_ONLY_LENGTH
&& cookieString.substring(index,
index + HTTP_ONLY_LENGTH).
equalsIgnoreCase(HTTP_ONLY)) {
index += HTTP_ONLY_LENGTH;
if (cookieString.charAt(index) == EQUAL) index++;
// FIXME: currently only parse the attribute
continue;
}
equalIndex = cookieString.indexOf(EQUAL, index);
if (equalIndex > 0) {
String name = cookieString.substring(index, equalIndex)
.toLowerCase();
if (name.equals(EXPIRES)) {
int comaIndex = cookieString.indexOf(COMMA, equalIndex);
// skip ',' in (Wdy, DD-Mon-YYYY HH:MM:SS GMT) or
// (Weekday, DD-Mon-YY HH:MM:SS GMT) if it applies.
// "Wednesday" is the longest Weekday which has length 9
if ((comaIndex != -1) &&
(comaIndex - equalIndex <= 10)) {
index = comaIndex + 1;
}
}
semicolonIndex = cookieString.indexOf(SEMICOLON, index);
int commaIndex = cookieString.indexOf(COMMA, index);
if (semicolonIndex == -1 && commaIndex == -1) {
index = length;
} else if (semicolonIndex == -1) {
index = commaIndex;
} else if (commaIndex == -1) {
index = semicolonIndex;
} else {
index = Math.min(semicolonIndex, commaIndex);
}
String value =
cookieString.substring(equalIndex + 1, index);
// Strip quotes if they exist
if (value.length() > 2 && value.charAt(0) == QUOTATION) {
int endQuote = value.indexOf(QUOTATION, 1);
if (endQuote > 0) {
value = value.substring(1, endQuote);
}
}
if (name.equals(EXPIRES)) {
try {
cookie.expires = HttpDateTime.parse(value);
} catch (IllegalArgumentException ex) {
Log.e(LOGTAG,
"illegal format for expires: " + value);
}
} else if (name.equals(MAX_AGE)) {
try {
cookie.expires = System.currentTimeMillis() + 1000
* Long.parseLong(value);
} catch (NumberFormatException ex) {
Log.e(LOGTAG,
"illegal format for max-age: " + value);
}
} else if (name.equals(PATH)) {
// only allow non-empty path value
if (value.length() > 0) {
cookie.path = value;
}
} else if (name.equals(DOMAIN)) {
int lastPeriod = value.lastIndexOf(PERIOD);
if (lastPeriod == 0) {
// disallow cookies set for TLDs like [.com]
cookie.domain = null;
continue;
}
try {
Integer.parseInt(value.substring(lastPeriod + 1));
// no wildcard for ip address match
if (!value.equals(host)) {
// no cross-site cookie
cookie.domain = null;
}
continue;
} catch (NumberFormatException ex) {
// ignore the exception, value is a host name
}
value = value.toLowerCase();
if (value.charAt(0) != PERIOD) {
// pre-pended dot to make it as a domain cookie
value = PERIOD + value;
lastPeriod++;
}
if (host.endsWith(value.substring(1))) {
int len = value.length();
int hostLen = host.length();
if (hostLen > (len - 1)
&& host.charAt(hostLen - len) != PERIOD) {
// make sure the bar.com doesn't match .ar.com
cookie.domain = null;
continue;
}
// disallow cookies set on ccTLDs like [.co.uk]
if ((len == lastPeriod + 3)
&& (len >= 6 && len <= 8)) {
String s = value.substring(1, lastPeriod);
if (Arrays.binarySearch(BAD_COUNTRY_2LDS, s) >= 0) {
cookie.domain = null;
continue;
}
}
cookie.domain = value;
} else {
// no cross-site or more specific sub-domain cookie
cookie.domain = null;
}
}
} else {
// bad format, force return
index = length;
}
}
if (cookie != null && cookie.domain != null) {
ret.add(cookie);
}
}
return ret;
|
public void | removeAllCookie()Remove all cookies
final Runnable clearCache = new Runnable() {
public void run() {
synchronized(CookieManager.this) {
mCookieMap = new LinkedHashMap<String, ArrayList<Cookie>>(
MAX_DOMAIN_COUNT, 0.75f, true);
CookieSyncManager.getInstance().clearAllCookies();
}
}
};
new Thread(clearCache).start();
|
public void | removeExpiredCookie()Remove all expired cookies
final Runnable clearCache = new Runnable() {
public void run() {
synchronized(CookieManager.this) {
long now = System.currentTimeMillis();
Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
while (listIter.hasNext()) {
ArrayList<Cookie> list = listIter.next();
Iterator<Cookie> iter = list.iterator();
while (iter.hasNext()) {
Cookie cookie = iter.next();
// expires == -1 means no expires defined. Otherwise
// negative means far future
if (cookie.expires > 0 && cookie.expires < now) {
iter.remove();
}
}
}
CookieSyncManager.getInstance().clearExpiredCookies(now);
}
}
};
new Thread(clearCache).start();
|
public void | removeSessionCookie()Remove all session cookies, which are cookies without expiration date
final Runnable clearCache = new Runnable() {
public void run() {
synchronized(CookieManager.this) {
Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
while (listIter.hasNext()) {
ArrayList<Cookie> list = listIter.next();
Iterator<Cookie> iter = list.iterator();
while (iter.hasNext()) {
Cookie cookie = iter.next();
if (cookie.expires == -1) {
iter.remove();
}
}
}
CookieSyncManager.getInstance().clearSessionCookies();
}
}
};
new Thread(clearCache).start();
|
public synchronized void | setAcceptCookie(boolean accept)Control whether cookie is enabled or disabled
mAcceptCookie = accept;
|
public void | setCookie(java.lang.String url, java.lang.String value)Set cookie for a given url. The old cookie with same host/path/name will
be removed. The new cookie will be added if it is not expired or it does
not have expiration which implies it is session cookie.
WebAddress uri;
try {
uri = new WebAddress(url);
} catch (ParseException ex) {
Log.e(LOGTAG, "Bad address: " + url);
return;
}
setCookie(uri, value);
|
public synchronized void | setCookie(android.net.WebAddress uri, java.lang.String value)Set cookie for a given uri. The old cookie with same host/path/name will
be removed. The new cookie will be added if it is not expired or it does
not have expiration which implies it is session cookie.
if (value != null && value.length() > MAX_COOKIE_LENGTH) {
return;
}
if (!mAcceptCookie || uri == null) {
return;
}
if (Config.LOGV) {
Log.v(LOGTAG, "setCookie: uri: " + uri + " value: " + value);
}
String[] hostAndPath = getHostAndPath(uri);
if (hostAndPath == null) {
return;
}
// For default path, when setting a cookie, the spec says:
//Path: Defaults to the path of the request URL that generated the
// Set-Cookie response, up to, but not including, the
// right-most /.
if (hostAndPath[1].length() > 1) {
int index = hostAndPath[1].lastIndexOf(PATH_DELIM);
hostAndPath[1] = hostAndPath[1].substring(0,
index > 0 ? index : index + 1);
}
ArrayList<Cookie> cookies = null;
try {
cookies = parseCookie(hostAndPath[0], hostAndPath[1], value);
} catch (RuntimeException ex) {
Log.e(LOGTAG, "parse cookie failed for: " + value);
}
if (cookies == null || cookies.size() == 0) {
return;
}
String baseDomain = getBaseDomain(hostAndPath[0]);
ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain);
if (cookieList == null) {
cookieList = CookieSyncManager.getInstance()
.getCookiesForDomain(baseDomain);
mCookieMap.put(baseDomain, cookieList);
}
long now = System.currentTimeMillis();
int size = cookies.size();
for (int i = 0; i < size; i++) {
Cookie cookie = cookies.get(i);
boolean done = false;
Iterator<Cookie> iter = cookieList.iterator();
while (iter.hasNext()) {
Cookie cookieEntry = iter.next();
if (cookie.exactMatch(cookieEntry)) {
// expires == -1 means no expires defined. Otherwise
// negative means far future
if (cookie.expires < 0 || cookie.expires > now) {
// secure cookies can't be overwritten by non-HTTPS url
if (!cookieEntry.secure || HTTPS.equals(uri.mScheme)) {
cookieEntry.value = cookie.value;
cookieEntry.expires = cookie.expires;
cookieEntry.secure = cookie.secure;
cookieEntry.lastAcessTime = now;
cookieEntry.lastUpdateTime = now;
cookieEntry.mode = Cookie.MODE_REPLACED;
}
} else {
cookieEntry.lastUpdateTime = now;
cookieEntry.mode = Cookie.MODE_DELETED;
}
done = true;
break;
}
}
// expires == -1 means no expires defined. Otherwise negative means
// far future
if (!done && (cookie.expires < 0 || cookie.expires > now)) {
cookie.lastAcessTime = now;
cookie.lastUpdateTime = now;
cookie.mode = Cookie.MODE_NEW;
if (cookieList.size() > MAX_COOKIE_COUNT_PER_BASE_DOMAIN) {
Cookie toDelete = new Cookie();
toDelete.lastAcessTime = now;
Iterator<Cookie> iter2 = cookieList.iterator();
while (iter2.hasNext()) {
Cookie cookieEntry2 = iter2.next();
if ((cookieEntry2.lastAcessTime < toDelete.lastAcessTime)
&& cookieEntry2.mode != Cookie.MODE_DELETED) {
toDelete = cookieEntry2;
}
}
toDelete.mode = Cookie.MODE_DELETED;
}
cookieList.add(cookie);
}
}
|
synchronized void | syncedACookie(android.webkit.CookieManager$Cookie cookie)Package level api, called from CookieSyncManager
Called after a cookie is synced to FLASH
cookie.mode = Cookie.MODE_NORMAL;
|