| /* |
| * 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.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| /** |
| * 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 the origins, quotas and usages as member values |
| // that we protect via a lock and update in syncValues(). |
| // This is needed to transfer this data across threads. |
| private static Lock mLock = new ReentrantLock(); |
| private static Condition mUpdateCondition = mLock.newCondition(); |
| private static boolean mUpdateAvailable; |
| |
| // 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 Set <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()); |
| } break; |
| |
| case DELETE_ORIGIN: { |
| Origin website = (Origin) msg.obj; |
| nativeDeleteOrigin(website.getOrigin()); |
| } break; |
| |
| case DELETE_ALL: |
| nativeDeleteAllData(); |
| break; |
| |
| case UPDATE: |
| syncValues(); |
| break; |
| } |
| } |
| }; |
| } |
| } |
| |
| /** |
| * @hide |
| * Returns a list of origins having a database |
| */ |
| public Set getOrigins() { |
| Set ret = null; |
| mLock.lock(); |
| try { |
| mUpdateAvailable = false; |
| update(); |
| while (!mUpdateAvailable) { |
| mUpdateCondition.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 { |
| mUpdateAvailable = false; |
| update(); |
| while (!mUpdateAvailable) { |
| mUpdateCondition.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 { |
| mUpdateAvailable = false; |
| update(); |
| while (!mUpdateAvailable) { |
| mUpdateCondition.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); |
| } 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); |
| } else { |
| postMessage(Message.obtain(null, DELETE_ORIGIN, |
| new Origin(origin))); |
| } |
| } |
| } |
| |
| /** |
| * @hide |
| * Delete all databases |
| */ |
| public void deleteAllData() { |
| if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { |
| nativeDeleteAllData(); |
| } 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 |
| * set the local values with the current ones |
| */ |
| private void syncValues() { |
| mLock.lock(); |
| Set tmp = nativeGetOrigins(); |
| mOrigins = new HashSet<String>(); |
| mQuotas.clear(); |
| mUsages.clear(); |
| Iterator<String> iter = tmp.iterator(); |
| while (iter.hasNext()) { |
| String origin = iter.next(); |
| mOrigins.add(origin); |
| mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin))); |
| mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin))); |
| } |
| mUpdateAvailable = true; |
| mUpdateCondition.signal(); |
| mLock.unlock(); |
| } |
| |
| // Native functions |
| private static native Set 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 nativeDeleteAllData(); |
| } |