WebStorage.java revision 11e8fe5a7208c9cd6afc2a0373761ae506d7707f
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            postMessage(Message.obtain(null, SET_QUOTA_ORIGIN,
204                new Origin(origin, quota)));
205        }
206    }
207
208    /**
209     * @hide
210     * Delete a given origin
211     */
212    public void deleteOrigin(String origin) {
213        if (origin != null) {
214            postMessage(Message.obtain(null, DELETE_ORIGIN,
215                new Origin(origin)));
216        }
217    }
218
219    /**
220     * @hide
221     * Delete all databases
222     */
223    public void deleteAllDatabases() {
224        postMessage(Message.obtain(null, DELETE_ALL));
225    }
226
227    /**
228     * Utility function to send a message to our handler
229     */
230    private void postMessage(Message msg) {
231        if (mHandler != null) {
232            mHandler.sendMessage(msg);
233        }
234    }
235
236    /**
237     * @hide
238     * Get the global instance of WebStorage.
239     * @return A single instance of WebStorage.
240     */
241    public static WebStorage getInstance() {
242      if (sWebStorage == null) {
243          sWebStorage = new WebStorage();
244      }
245      return sWebStorage;
246    }
247
248    /**
249     * @hide
250     * Post a Sync request
251     */
252    public void update() {
253        postMessage(Message.obtain(null, UPDATE));
254    }
255
256    /**
257     * Run on the webcore thread
258     * sync the local cached values with the real ones
259     */
260    private void syncValues() {
261        mLock.lock();
262        Vector tmp = nativeGetOrigins();
263        mOrigins = new Vector<String>();
264        mQuotas.clear();
265        mUsages.clear();
266        for (int i = 0; i < tmp.size(); i++) {
267            String origin = (String) tmp.get(i);
268            mOrigins.add(origin);
269            mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin)));
270            mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin)));
271        }
272        mCacheUpdated.signal();
273        mLock.unlock();
274    }
275
276    // Native functions
277    private static native Vector nativeGetOrigins();
278    private static native long nativeGetUsageForOrigin(String origin);
279    private static native long nativeGetQuotaForOrigin(String origin);
280    private static native void nativeSetQuotaForOrigin(String origin, long quota);
281    private static native void nativeDeleteOrigin(String origin);
282    private static native void nativeDeleteAllDatabases();
283}
284