WebStorage.java revision 5647bb3cc21428dd5242255323dff13c71a3343d
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.os.Handler;
20import android.os.Message;
21import android.util.Log;
22
23import java.util.concurrent.locks.Condition;
24import java.util.concurrent.locks.Lock;
25import java.util.concurrent.locks.ReentrantLock;
26import java.util.HashMap;
27import java.util.Vector;
28
29/**
30 * Functionality for manipulating the webstorage databases.
31 */
32public final class WebStorage {
33
34    /**
35     * Encapsulates a callback function to be executed when a new quota is made
36     * available. We primarily want this to allow us to call back the sleeping
37     * WebCore thread from outside the WebViewCore class (as the native call
38     * is private). It is imperative that this the setDatabaseQuota method is
39     * executed once a decision to either allow or deny new quota is made,
40     * otherwise the WebCore thread will remain asleep.
41     */
42    public interface QuotaUpdater {
43        public void updateQuota(long newQuota);
44    };
45
46    // Log tag
47    private static final String TAG = "webstorage";
48
49    // Global instance of a WebStorage
50    private static WebStorage sWebStorage;
51
52    // We keep a copy of the origins, quotas and usages
53    // that we protect via a lock and update in syncValues()
54    private static Lock mLock = new ReentrantLock();
55    private static Condition mCacheUpdated = mLock.newCondition();
56
57    // Message ids
58    static final int UPDATE = 0;
59    static final int SET_QUOTA_ORIGIN = 1;
60    static final int DELETE_ORIGIN = 2;
61    static final int DELETE_ALL = 3;
62
63    private Vector <String> mOrigins;
64    private HashMap <String, Long> mQuotas = new HashMap<String, Long>();
65    private HashMap <String, Long> mUsages = new HashMap<String, Long>();
66
67    private Handler mHandler = null;
68
69    private class Origin {
70        String mOrigin = null;
71        long mQuota = 0;
72
73        public Origin(String origin, long quota) {
74            mOrigin = origin;
75            mQuota = quota;
76        }
77
78        public Origin(String origin) {
79            mOrigin = origin;
80        }
81
82        public String getOrigin() {
83            return mOrigin;
84        }
85
86        public long getQuota() {
87            return mQuota;
88        }
89    }
90
91    /**
92     * @hide
93     * Message handler
94     */
95    public void createHandler() {
96        if (mHandler == null) {
97            mHandler = new Handler() {
98                @Override
99                public void handleMessage(Message msg) {
100                    switch (msg.what) {
101                        case SET_QUOTA_ORIGIN: {
102                            Origin website = (Origin) msg.obj;
103                            nativeSetQuotaForOrigin(website.getOrigin(),
104                                                    website.getQuota());
105                            syncValues();
106                            } break;
107
108                        case DELETE_ORIGIN: {
109                            Origin website = (Origin) msg.obj;
110                            nativeDeleteOrigin(website.getOrigin());
111                            syncValues();
112                            } break;
113
114                        case DELETE_ALL:
115                            nativeDeleteAllDatabases();
116                            syncValues();
117                            break;
118
119                        case UPDATE:
120                            syncValues();
121                            break;
122                    }
123                }
124            };
125        }
126    }
127
128    /**
129     * @hide
130     * Returns a list of origins having a database
131     */
132    public Vector getOrigins() {
133        Vector ret = null;
134        mLock.lock();
135        try {
136            update();
137            mCacheUpdated.await();
138            ret = mOrigins;
139        } catch (InterruptedException e) {
140            Log.e(TAG, "Exception while waiting on the updated origins", e);
141        } finally {
142            mLock.unlock();
143        }
144        return ret;
145    }
146
147    /**
148     * @hide
149     * Returns the use for a given origin
150     */
151    public long getUsageForOrigin(String origin) {
152        long ret = 0;
153        if (origin == null) {
154          return ret;
155        }
156        mLock.lock();
157        try {
158            update();
159            mCacheUpdated.await();
160            Long usage = mUsages.get(origin);
161            if (usage != null) {
162                ret = usage.longValue();
163            }
164        } catch (InterruptedException e) {
165            Log.e(TAG, "Exception while waiting on the updated origins", e);
166        } finally {
167            mLock.unlock();
168        }
169        return ret;
170    }
171
172    /**
173     * @hide
174     * Returns the quota for a given origin
175     */
176    public long getQuotaForOrigin(String origin) {
177        long ret = 0;
178        if (origin == null) {
179          return ret;
180        }
181        mLock.lock();
182        try {
183            update();
184            mCacheUpdated.await();
185            Long quota = mQuotas.get(origin);
186            if (quota != null) {
187                ret = quota.longValue();
188            }
189        } catch (InterruptedException e) {
190            Log.e(TAG, "Exception while waiting on the updated origins", e);
191        } finally {
192            mLock.unlock();
193        }
194        return ret;
195    }
196
197    /**
198     * @hide
199     * Set the quota for a given origin
200     */
201    public void setQuotaForOrigin(String origin, long quota) {
202        if (origin != null) {
203            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
204                nativeSetQuotaForOrigin(origin, quota);
205                syncValues();
206            } else {
207                postMessage(Message.obtain(null, SET_QUOTA_ORIGIN,
208                    new Origin(origin, quota)));
209            }
210        }
211    }
212
213    /**
214     * @hide
215     * Delete a given origin
216     */
217    public void deleteOrigin(String origin) {
218        if (origin != null) {
219            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
220                nativeDeleteOrigin(origin);
221                syncValues();
222            } else {
223                postMessage(Message.obtain(null, DELETE_ORIGIN,
224                    new Origin(origin)));
225            }
226        }
227    }
228
229    /**
230     * @hide
231     * Delete all databases
232     */
233    public void deleteAllDatabases() {
234        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
235            nativeDeleteAllDatabases();
236            syncValues();
237        } else {
238            postMessage(Message.obtain(null, DELETE_ALL));
239        }
240    }
241
242    /**
243     * Utility function to send a message to our handler
244     */
245    private void postMessage(Message msg) {
246        if (mHandler != null) {
247            mHandler.sendMessage(msg);
248        }
249    }
250
251    /**
252     * @hide
253     * Get the global instance of WebStorage.
254     * @return A single instance of WebStorage.
255     */
256    public static WebStorage getInstance() {
257      if (sWebStorage == null) {
258          sWebStorage = new WebStorage();
259      }
260      return sWebStorage;
261    }
262
263    /**
264     * @hide
265     * Post a Sync request
266     */
267    public void update() {
268        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
269            syncValues();
270        } else {
271            postMessage(Message.obtain(null, UPDATE));
272        }
273    }
274
275    /**
276     * Run on the webcore thread
277     * sync the local cached values with the real ones
278     */
279    private void syncValues() {
280        mLock.lock();
281        Vector tmp = nativeGetOrigins();
282        mOrigins = new Vector<String>();
283        mQuotas.clear();
284        mUsages.clear();
285        for (int i = 0; i < tmp.size(); i++) {
286            String origin = (String) tmp.get(i);
287            mOrigins.add(origin);
288            mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin)));
289            mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin)));
290        }
291        mCacheUpdated.signal();
292        mLock.unlock();
293    }
294
295    // Native functions
296    private static native Vector nativeGetOrigins();
297    private static native long nativeGetUsageForOrigin(String origin);
298    private static native long nativeGetQuotaForOrigin(String origin);
299    private static native void nativeSetQuotaForOrigin(String origin, long quota);
300    private static native void nativeDeleteOrigin(String origin);
301    private static native void nativeDeleteAllDatabases();
302}
303