blob: 048869132dd4ade18aee519852f11e98d9d7d865 [file] [log] [blame]
/*
* Copyright (C) 2009 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 android.webkit;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.HashMap;
import java.util.Vector;
/**
* Functionality for manipulating the webstorage databases.
*/
public final class WebStorage {
/**
* Encapsulates a callback function to be executed when a new quota is made
* available. We primarily want this to allow us to call back the sleeping
* WebCore thread from outside the WebViewCore class (as the native call
* is private). It is imperative that this the setDatabaseQuota method is
* executed once a decision to either allow or deny new quota is made,
* otherwise the WebCore thread will remain asleep.
*/
public interface QuotaUpdater {
public void updateQuota(long newQuota);
};
// Log tag
private static final String TAG = "webstorage";
// Global instance of a WebStorage
private static WebStorage sWebStorage;
// We keep a copy of the origins, quotas and usages
// that we protect via a lock and update in syncValues()
private static Lock mLock = new ReentrantLock();
private static Condition mCacheUpdated = mLock.newCondition();
// Message ids
static final int UPDATE = 0;
static final int SET_QUOTA_ORIGIN = 1;
static final int DELETE_ORIGIN = 2;
static final int DELETE_ALL = 3;
private Vector <String> mOrigins;
private HashMap <String, Long> mQuotas = new HashMap<String, Long>();
private HashMap <String, Long> mUsages = new HashMap<String, Long>();
private Handler mHandler = null;
private class Origin {
String mOrigin = null;
long mQuota = 0;
public Origin(String origin, long quota) {
mOrigin = origin;
mQuota = quota;
}
public Origin(String origin) {
mOrigin = origin;
}
public String getOrigin() {
return mOrigin;
}
public long getQuota() {
return mQuota;
}
}
/**
* @hide
* Message handler
*/
public void createHandler() {
if (mHandler == null) {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SET_QUOTA_ORIGIN: {
Origin website = (Origin) msg.obj;
nativeSetQuotaForOrigin(website.getOrigin(),
website.getQuota());
syncValues();
} break;
case DELETE_ORIGIN: {
Origin website = (Origin) msg.obj;
nativeDeleteOrigin(website.getOrigin());
syncValues();
} break;
case DELETE_ALL:
nativeDeleteAllDatabases();
syncValues();
break;
case UPDATE:
syncValues();
break;
}
}
};
}
}
/**
* @hide
* Returns a list of origins having a database
*/
public Vector getOrigins() {
Vector ret = null;
mLock.lock();
try {
update();
mCacheUpdated.await();
ret = mOrigins;
} catch (InterruptedException e) {
Log.e(TAG, "Exception while waiting on the updated origins", e);
} finally {
mLock.unlock();
}
return ret;
}
/**
* @hide
* Returns the use for a given origin
*/
public long getUsageForOrigin(String origin) {
long ret = 0;
if (origin == null) {
return ret;
}
mLock.lock();
try {
update();
mCacheUpdated.await();
Long usage = mUsages.get(origin);
if (usage != null) {
ret = usage.longValue();
}
} catch (InterruptedException e) {
Log.e(TAG, "Exception while waiting on the updated origins", e);
} finally {
mLock.unlock();
}
return ret;
}
/**
* @hide
* Returns the quota for a given origin
*/
public long getQuotaForOrigin(String origin) {
long ret = 0;
if (origin == null) {
return ret;
}
mLock.lock();
try {
update();
mCacheUpdated.await();
Long quota = mQuotas.get(origin);
if (quota != null) {
ret = quota.longValue();
}
} catch (InterruptedException e) {
Log.e(TAG, "Exception while waiting on the updated origins", e);
} finally {
mLock.unlock();
}
return ret;
}
/**
* @hide
* Set the quota for a given origin
*/
public void setQuotaForOrigin(String origin, long quota) {
if (origin != null) {
if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
nativeSetQuotaForOrigin(origin, quota);
syncValues();
} else {
postMessage(Message.obtain(null, SET_QUOTA_ORIGIN,
new Origin(origin, quota)));
}
}
}
/**
* @hide
* Delete a given origin
*/
public void deleteOrigin(String origin) {
if (origin != null) {
if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
nativeDeleteOrigin(origin);
syncValues();
} else {
postMessage(Message.obtain(null, DELETE_ORIGIN,
new Origin(origin)));
}
}
}
/**
* @hide
* Delete all databases
*/
public void deleteAllDatabases() {
if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
nativeDeleteAllDatabases();
syncValues();
} else {
postMessage(Message.obtain(null, DELETE_ALL));
}
}
/**
* Utility function to send a message to our handler
*/
private void postMessage(Message msg) {
if (mHandler != null) {
mHandler.sendMessage(msg);
}
}
/**
* @hide
* Get the global instance of WebStorage.
* @return A single instance of WebStorage.
*/
public static WebStorage getInstance() {
if (sWebStorage == null) {
sWebStorage = new WebStorage();
}
return sWebStorage;
}
/**
* @hide
* Post a Sync request
*/
public void update() {
if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
syncValues();
} else {
postMessage(Message.obtain(null, UPDATE));
}
}
/**
* Run on the webcore thread
* sync the local cached values with the real ones
*/
private void syncValues() {
mLock.lock();
Vector tmp = nativeGetOrigins();
mOrigins = new Vector<String>();
mQuotas.clear();
mUsages.clear();
for (int i = 0; i < tmp.size(); i++) {
String origin = (String) tmp.get(i);
mOrigins.add(origin);
mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin)));
mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin)));
}
mCacheUpdated.signal();
mLock.unlock();
}
// Native functions
private static native Vector nativeGetOrigins();
private static native long nativeGetUsageForOrigin(String origin);
private static native long nativeGetQuotaForOrigin(String origin);
private static native void nativeSetQuotaForOrigin(String origin, long quota);
private static native void nativeDeleteOrigin(String origin);
private static native void nativeDeleteAllDatabases();
}