JavaBridge.cpp revision 2df3aefb377b3f3c4af3b548b1980d8c8ae56844
1/*
2 * Copyright 2006, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_TAG "webcoreglue"
27
28#include "config.h"
29
30#include "MemoryCache.h"
31#include "Connection.h"
32#include "CookieClient.h"
33#include "FileSystemClient.h"
34#include "JavaSharedClient.h"
35#include "KeyGeneratorClient.h"
36#include "KURL.h"
37#include "NetworkStateNotifier.h"
38#include "PackageNotifier.h"
39#include "Page.h"
40#include "PluginClient.h"
41#include "PluginDatabase.h"
42#include "Timer.h"
43#include "TimerClient.h"
44#ifdef ANDROID_INSTRUMENT
45#include "TimeCounter.h"
46#endif
47#include "WebCache.h"
48#include "WebCoreJni.h"
49
50#include <JNIHelp.h>
51#include <JNIUtility.h>
52#include <SkUtils.h>
53#include <jni.h>
54#include <utils/misc.h>
55#include <wtf/Platform.h>
56#include <wtf/StdLibExtras.h>
57#include <wtf/text/AtomicString.h>
58
59namespace android {
60
61// ----------------------------------------------------------------------------
62
63static jfieldID gJavaBridge_ObjectID;
64
65// ----------------------------------------------------------------------------
66
67class JavaBridge : public TimerClient, public CookieClient, public PluginClient, public KeyGeneratorClient, public FileSystemClient
68{
69public:
70    JavaBridge(JNIEnv* env, jobject obj);
71    virtual ~JavaBridge();
72
73    /*
74     * WebCore -> Java API
75     */
76    virtual void setSharedTimer(long long timemillis);
77    virtual void stopSharedTimer();
78
79    virtual void setCookies(WebCore::KURL const& url, WTF::String const& value);
80    virtual WTF::String cookies(WebCore::KURL const& url);
81    virtual bool cookiesEnabled();
82
83    virtual WTF::Vector<WTF::String> getPluginDirectories();
84    virtual WTF::String getPluginSharedDataDirectory();
85
86    virtual WTF::Vector<String> getSupportedKeyStrengthList();
87    virtual WTF::String getSignedPublicKeyAndChallengeString(unsigned index,
88            const WTF::String& challenge, const WebCore::KURL& url);
89    virtual WTF::String resolveFilePathForContentUri(const WTF::String& uri);
90
91    ////////////////////////////////////////////
92
93    virtual void setSharedTimerCallback(void (*f)());
94
95    ////////////////////////////////////////////
96
97    virtual void signalServiceFuncPtrQueue();
98
99    // jni functions
100    static void Constructor(JNIEnv* env, jobject obj);
101    static void Finalize(JNIEnv* env, jobject obj);
102    static void SharedTimerFired(JNIEnv* env, jobject);
103    static void SetCacheSize(JNIEnv* env, jobject obj, jint bytes);
104    static void SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online);
105    static void SetNetworkType(JNIEnv* env, jobject obj, jstring type, jstring subtype);
106    static void SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer);
107    static void ServiceFuncPtrQueue(JNIEnv*);
108    static void UpdatePluginDirectories(JNIEnv* env, jobject obj, jobjectArray array, jboolean reload);
109    static void AddPackageNames(JNIEnv* env, jobject obj, jobject packageNames);
110    static void AddPackageName(JNIEnv* env, jobject obj, jstring packageName);
111    static void RemovePackageName(JNIEnv* env, jobject obj, jstring packageName);
112    static void UpdateProxy(JNIEnv* env, jobject obj, jstring newProxy, jstring newExList);
113
114
115private:
116    jweak       mJavaObject;
117    jmethodID   mSetSharedTimer;
118    jmethodID   mStopSharedTimer;
119    jmethodID   mSetCookies;
120    jmethodID   mCookies;
121    jmethodID   mCookiesEnabled;
122    jmethodID   mGetPluginDirectories;
123    jmethodID   mGetPluginSharedDataDirectory;
124    jmethodID   mSignalFuncPtrQueue;
125    jmethodID   mGetKeyStrengthList;
126    jmethodID   mGetSignedPublicKey;
127    jmethodID   mResolveFilePathForContentUri;
128    AutoJObject javaObject(JNIEnv* env) { return getRealObject(env, mJavaObject); }
129};
130
131static void (*sSharedTimerFiredCallback)();
132
133JavaBridge::JavaBridge(JNIEnv* env, jobject obj)
134{
135    mJavaObject = env->NewWeakGlobalRef(obj);
136    jclass clazz = env->GetObjectClass(obj);
137
138    mSetSharedTimer = env->GetMethodID(clazz, "setSharedTimer", "(J)V");
139    mStopSharedTimer = env->GetMethodID(clazz, "stopSharedTimer", "()V");
140    mSetCookies = env->GetMethodID(clazz, "setCookies", "(Ljava/lang/String;Ljava/lang/String;)V");
141    mCookies = env->GetMethodID(clazz, "cookies", "(Ljava/lang/String;)Ljava/lang/String;");
142    mCookiesEnabled = env->GetMethodID(clazz, "cookiesEnabled", "()Z");
143    mGetPluginDirectories = env->GetMethodID(clazz, "getPluginDirectories", "()[Ljava/lang/String;");
144    mGetPluginSharedDataDirectory = env->GetMethodID(clazz, "getPluginSharedDataDirectory", "()Ljava/lang/String;");
145    mSignalFuncPtrQueue = env->GetMethodID(clazz, "signalServiceFuncPtrQueue", "()V");
146    mGetKeyStrengthList = env->GetMethodID(clazz, "getKeyStrengthList", "()[Ljava/lang/String;");
147    mGetSignedPublicKey = env->GetMethodID(clazz, "getSignedPublicKey", "(ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
148    mResolveFilePathForContentUri = env->GetMethodID(clazz, "resolveFilePathForContentUri", "(Ljava/lang/String;)Ljava/lang/String;");
149    env->DeleteLocalRef(clazz);
150
151    LOG_ASSERT(mSetSharedTimer, "Could not find method setSharedTimer");
152    LOG_ASSERT(mStopSharedTimer, "Could not find method stopSharedTimer");
153    LOG_ASSERT(mSetCookies, "Could not find method setCookies");
154    LOG_ASSERT(mCookies, "Could not find method cookies");
155    LOG_ASSERT(mCookiesEnabled, "Could not find method cookiesEnabled");
156    LOG_ASSERT(mGetPluginDirectories, "Could not find method getPluginDirectories");
157    LOG_ASSERT(mGetPluginSharedDataDirectory, "Could not find method getPluginSharedDataDirectory");
158    LOG_ASSERT(mGetKeyStrengthList, "Could not find method getKeyStrengthList");
159    LOG_ASSERT(mGetSignedPublicKey, "Could not find method getSignedPublicKey");
160
161    JavaSharedClient::SetTimerClient(this);
162    JavaSharedClient::SetCookieClient(this);
163    JavaSharedClient::SetPluginClient(this);
164    JavaSharedClient::SetKeyGeneratorClient(this);
165    JavaSharedClient::SetFileSystemClient(this);
166}
167
168JavaBridge::~JavaBridge()
169{
170    if (mJavaObject) {
171        JNIEnv* env = JSC::Bindings::getJNIEnv();
172        env->DeleteWeakGlobalRef(mJavaObject);
173        mJavaObject = 0;
174    }
175
176    JavaSharedClient::SetTimerClient(NULL);
177    JavaSharedClient::SetCookieClient(NULL);
178    JavaSharedClient::SetPluginClient(NULL);
179    JavaSharedClient::SetKeyGeneratorClient(NULL);
180    JavaSharedClient::SetFileSystemClient(NULL);
181}
182
183void
184JavaBridge::setSharedTimer(long long timemillis)
185{
186    JNIEnv* env = JSC::Bindings::getJNIEnv();
187    AutoJObject obj = javaObject(env);
188    if (!obj.get())
189        return;
190    env->CallVoidMethod(obj.get(), mSetSharedTimer, timemillis);
191}
192
193void
194JavaBridge::stopSharedTimer()
195{
196    JNIEnv* env = JSC::Bindings::getJNIEnv();
197    AutoJObject obj = javaObject(env);
198    if (!obj.get())
199        return;
200    env->CallVoidMethod(obj.get(), mStopSharedTimer);
201}
202
203void
204JavaBridge::setCookies(WebCore::KURL const& url, WTF::String const& value)
205{
206    JNIEnv* env = JSC::Bindings::getJNIEnv();
207    AutoJObject obj = javaObject(env);
208    if (!obj.get())
209        return;
210
211    const WTF::String& urlStr = url.string();
212    jstring jUrlStr = wtfStringToJstring(env, urlStr);
213    jstring jValueStr = wtfStringToJstring(env, value);
214
215    env->CallVoidMethod(obj.get(), mSetCookies, jUrlStr, jValueStr);
216    env->DeleteLocalRef(jUrlStr);
217    env->DeleteLocalRef(jValueStr);
218}
219
220WTF::String
221JavaBridge::cookies(WebCore::KURL const& url)
222{
223    JNIEnv* env = JSC::Bindings::getJNIEnv();
224    AutoJObject obj = javaObject(env);
225    if (!obj.get())
226        return String();
227
228    const WTF::String& urlStr = url.string();
229    jstring jUrlStr = wtfStringToJstring(env, urlStr);
230    jstring string = (jstring)(env->CallObjectMethod(obj.get(), mCookies, jUrlStr));
231
232    WTF::String ret = jstringToWtfString(env, string);
233    env->DeleteLocalRef(jUrlStr);
234    env->DeleteLocalRef(string);
235    return ret;
236}
237
238bool
239JavaBridge::cookiesEnabled()
240{
241    JNIEnv* env = JSC::Bindings::getJNIEnv();
242    AutoJObject obj = javaObject(env);
243    if (!obj.get())
244        return false;
245    jboolean ret = env->CallBooleanMethod(obj.get(), mCookiesEnabled);
246    return (ret != 0);
247}
248
249WTF::Vector<WTF::String>
250JavaBridge::getPluginDirectories()
251{
252    WTF::Vector<WTF::String> directories;
253    JNIEnv* env = JSC::Bindings::getJNIEnv();
254    AutoJObject obj = javaObject(env);
255    if (!obj.get())
256        return directories;
257    jobjectArray array = (jobjectArray)
258            env->CallObjectMethod(obj.get(), mGetPluginDirectories);
259    int count = env->GetArrayLength(array);
260    for (int i = 0; i < count; i++) {
261        jstring dir = (jstring) env->GetObjectArrayElement(array, i);
262        directories.append(jstringToWtfString(env, dir));
263        env->DeleteLocalRef(dir);
264    }
265    env->DeleteLocalRef(array);
266    checkException(env);
267    return directories;
268}
269
270WTF::String
271JavaBridge::getPluginSharedDataDirectory()
272{
273    JNIEnv* env = JSC::Bindings::getJNIEnv();
274    AutoJObject obj = javaObject(env);
275    if (!obj.get())
276        return String();
277    jstring ret = (jstring)env->CallObjectMethod(obj.get(), mGetPluginSharedDataDirectory);
278    WTF::String path = jstringToWtfString(env, ret);
279    checkException(env);
280    return path;
281}
282
283void
284JavaBridge::setSharedTimerCallback(void (*f)())
285{
286    LOG_ASSERT(!sSharedTimerFiredCallback || sSharedTimerFiredCallback==f,
287               "Shared timer callback may already be set or null!");
288
289    sSharedTimerFiredCallback = f;
290}
291
292void JavaBridge::signalServiceFuncPtrQueue()
293{
294    // In order to signal the main thread we must go through JNI. This
295    // is the only usage on most threads, so we need to ensure a JNI
296    // environment is setup.
297    JNIEnv* env = JSC::Bindings::getJNIEnv();
298    AutoJObject obj = javaObject(env);
299    if (!obj.get())
300        return;
301    env->CallVoidMethod(obj.get(), mSignalFuncPtrQueue);
302}
303
304WTF::Vector<WTF::String>JavaBridge::getSupportedKeyStrengthList() {
305    WTF::Vector<WTF::String> list;
306    JNIEnv* env = JSC::Bindings::getJNIEnv();
307    AutoJObject obj = javaObject(env);
308    if (!obj.get())
309        return list;
310    jobjectArray array = (jobjectArray) env->CallObjectMethod(obj.get(),
311            mGetKeyStrengthList);
312    int count = env->GetArrayLength(array);
313    for (int i = 0; i < count; ++i) {
314        jstring keyStrength = (jstring) env->GetObjectArrayElement(array, i);
315        list.append(jstringToWtfString(env, keyStrength));
316        env->DeleteLocalRef(keyStrength);
317    }
318    env->DeleteLocalRef(array);
319    checkException(env);
320    return list;
321}
322
323WTF::String JavaBridge::getSignedPublicKeyAndChallengeString(unsigned index,
324        const WTF::String& challenge, const WebCore::KURL& url) {
325    JNIEnv* env = JSC::Bindings::getJNIEnv();
326    AutoJObject obj = javaObject(env);
327    if (!obj.get())
328        return String();
329    jstring jChallenge = wtfStringToJstring(env, challenge);
330    const WTF::String& urlStr = url.string();
331    jstring jUrl = wtfStringToJstring(env, urlStr);
332    jstring key = (jstring) env->CallObjectMethod(obj.get(),
333            mGetSignedPublicKey, index, jChallenge, jUrl);
334    WTF::String ret = jstringToWtfString(env, key);
335    env->DeleteLocalRef(jChallenge);
336    env->DeleteLocalRef(jUrl);
337    env->DeleteLocalRef(key);
338    return ret;
339}
340
341WTF::String JavaBridge::resolveFilePathForContentUri(const WTF::String& uri) {
342    JNIEnv* env = JSC::Bindings::getJNIEnv();
343    AutoJObject obj = javaObject(env);
344    if (!obj.get())
345        return String();
346    jstring jUri = wtfStringToJstring(env, uri);
347    jstring path = static_cast<jstring>(env->CallObjectMethod(obj.get(), mResolveFilePathForContentUri, jUri));
348    WTF::String ret = jstringToWtfString(env, path);
349    env->DeleteLocalRef(jUri);
350    env->DeleteLocalRef(path);
351    return ret;
352}
353
354// ----------------------------------------------------------------------------
355
356void JavaBridge::Constructor(JNIEnv* env, jobject obj)
357{
358    JavaBridge* javaBridge = new JavaBridge(env, obj);
359    env->SetIntField(obj, gJavaBridge_ObjectID, (jint)javaBridge);
360}
361
362void JavaBridge::Finalize(JNIEnv* env, jobject obj)
363{
364    JavaBridge* javaBridge = (JavaBridge*)
365        (env->GetIntField(obj, gJavaBridge_ObjectID));
366    LOG_ASSERT(javaBridge, "Finalize should not be called twice for the same java bridge!");
367    ALOGV("webcore_javabridge::nativeFinalize(%p)\n", javaBridge);
368    delete javaBridge;
369    env->SetIntField(obj, gJavaBridge_ObjectID, 0);
370}
371
372// we don't use the java bridge object, as we're just looking at a global
373void JavaBridge::SharedTimerFired(JNIEnv* env, jobject)
374{
375    if (sSharedTimerFiredCallback)
376    {
377#ifdef ANDROID_INSTRUMENT
378        TimeCounter::start(TimeCounter::SharedTimerTimeCounter);
379#endif
380        SkAutoMemoryUsageProbe  mup("JavaBridge::sharedTimerFired");
381        sSharedTimerFiredCallback();
382#ifdef ANDROID_INSTRUMENT
383        TimeCounter::record(TimeCounter::SharedTimerTimeCounter, __FUNCTION__);
384#endif
385    }
386}
387
388void JavaBridge::SetCacheSize(JNIEnv* env, jobject obj, jint bytes)
389{
390    WebCore::memoryCache()->setCapacities(0, bytes/2, bytes);
391}
392
393void JavaBridge::SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online)
394{
395	WebCore::networkStateNotifier().networkStateChange(online);
396}
397
398void JavaBridge::SetNetworkType(JNIEnv* env, jobject obj, jstring javatype, jstring javasubtype)
399{
400    DEFINE_STATIC_LOCAL(AtomicString, wifi, ("wifi"));
401    DEFINE_STATIC_LOCAL(AtomicString, mobile, ("mobile"));
402    DEFINE_STATIC_LOCAL(AtomicString, mobileSupl, ("mobile_supl"));
403    DEFINE_STATIC_LOCAL(AtomicString, gprs, ("gprs"));
404    DEFINE_STATIC_LOCAL(AtomicString, edge, ("edge"));
405    DEFINE_STATIC_LOCAL(AtomicString, umts, ("umts"));
406
407    String type = jstringToWtfString(env, javatype);
408    String subtype = jstringToWtfString(env, javasubtype);
409    Connection::ConnectionType connectionType = Connection::UNKNOWN;
410    if (type == wifi)
411        connectionType = Connection::WIFI;
412    else if (type == mobile || type == mobileSupl) {
413        if (subtype == edge || subtype == gprs)
414            connectionType = Connection::CELL_2G;
415        else if (subtype == umts)
416            connectionType = Connection::CELL_3G;
417    }
418    WebCore::networkStateNotifier().networkTypeChange(connectionType);
419}
420
421void JavaBridge::ServiceFuncPtrQueue(JNIEnv*)
422{
423    JavaSharedClient::ServiceFunctionPtrQueue();
424}
425
426void JavaBridge::UpdatePluginDirectories(JNIEnv* env, jobject obj,
427        jobjectArray array, jboolean reload) {
428    WTF::Vector<WTF::String> directories;
429    int count = env->GetArrayLength(array);
430    for (int i = 0; i < count; i++) {
431        jstring dir = (jstring) env->GetObjectArrayElement(array, i);
432        directories.append(jstringToWtfString(env, dir));
433        env->DeleteLocalRef(dir);
434    }
435    checkException(env);
436    WebCore::PluginDatabase *pluginDatabase =
437            WebCore::PluginDatabase::installedPlugins();
438    pluginDatabase->setPluginDirectories(directories);
439    // refreshPlugins() should refresh both PluginDatabase and Page's PluginData
440    WebCore::Page::refreshPlugins(reload);
441}
442
443void JavaBridge::AddPackageNames(JNIEnv* env, jobject obj, jobject packageNames)
444{
445    if (!packageNames)
446        return;
447
448    // dalvikvm will raise exception if any of these fail
449    jclass setClass = env->FindClass("java/util/Set");
450    jmethodID iterator = env->GetMethodID(setClass, "iterator",
451            "()Ljava/util/Iterator;");
452    jobject iter = env->CallObjectMethod(packageNames, iterator);
453
454    jclass iteratorClass = env->FindClass("java/util/Iterator");
455    jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
456    jmethodID next = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
457
458    HashSet<WTF::String> namesSet;
459    while (env->CallBooleanMethod(iter, hasNext)) {
460        jstring name = static_cast<jstring>(env->CallObjectMethod(iter, next));
461        namesSet.add(jstringToWtfString(env, name));
462        env->DeleteLocalRef(name);
463    }
464
465    packageNotifier().addPackageNames(namesSet);
466
467    env->DeleteLocalRef(iteratorClass);
468    env->DeleteLocalRef(iter);
469    env->DeleteLocalRef(setClass);
470}
471
472void JavaBridge::AddPackageName(JNIEnv* env, jobject obj, jstring packageName)
473{
474    packageNotifier().addPackageName(jstringToWtfString(env, packageName));
475}
476
477void JavaBridge::RemovePackageName(JNIEnv* env, jobject obj, jstring packageName)
478{
479    packageNotifier().removePackageName(jstringToWtfString(env, packageName));
480}
481
482void JavaBridge::UpdateProxy(JNIEnv* env, jobject obj, jstring newProxy, jstring newExList)
483{
484#if USE(CHROME_NETWORK_STACK)
485    std::string proxy = jstringToStdString(env, newProxy);
486    std::string exList = jstringToStdString(env, newExList);
487    WebCache::get(false)->proxy()->UpdateProxySettings(proxy, exList);
488    WebCache::get(true)->proxy()->UpdateProxySettings(proxy, exList);
489#endif
490}
491
492
493// ----------------------------------------------------------------------------
494
495/*
496 * JNI registration.
497 */
498static JNINativeMethod gWebCoreJavaBridgeMethods[] = {
499    /* name, signature, funcPtr */
500    { "nativeConstructor", "()V",
501        (void*) JavaBridge::Constructor },
502    { "nativeFinalize", "()V",
503        (void*) JavaBridge::Finalize },
504    { "sharedTimerFired", "()V",
505        (void*) JavaBridge::SharedTimerFired },
506    { "setCacheSize", "(I)V",
507        (void*) JavaBridge::SetCacheSize },
508    { "setNetworkOnLine", "(Z)V",
509        (void*) JavaBridge::SetNetworkOnLine },
510    { "setNetworkType", "(Ljava/lang/String;Ljava/lang/String;)V",
511        (void*) JavaBridge::SetNetworkType },
512    { "nativeServiceFuncPtrQueue", "()V",
513        (void*) JavaBridge::ServiceFuncPtrQueue },
514    { "nativeUpdatePluginDirectories", "([Ljava/lang/String;Z)V",
515        (void*) JavaBridge::UpdatePluginDirectories },
516    { "addPackageNames", "(Ljava/util/Set;)V",
517        (void*) JavaBridge::AddPackageNames },
518    { "addPackageName", "(Ljava/lang/String;)V",
519        (void*) JavaBridge::AddPackageName },
520    { "removePackageName", "(Ljava/lang/String;)V",
521        (void*) JavaBridge::RemovePackageName },
522    { "nativeUpdateProxy", "(Ljava/lang/String;Ljava/lang/String;)V",
523        (void*) JavaBridge::UpdateProxy }
524};
525
526int registerJavaBridge(JNIEnv* env)
527{
528    jclass javaBridge = env->FindClass("android/webkit/JWebCoreJavaBridge");
529    LOG_FATAL_IF(javaBridge == NULL, "Unable to find class android/webkit/JWebCoreJavaBridge");
530    gJavaBridge_ObjectID = env->GetFieldID(javaBridge, "mNativeBridge", "I");
531    LOG_FATAL_IF(gJavaBridge_ObjectID == NULL, "Unable to find android/webkit/JWebCoreJavaBridge.mNativeBridge");
532    env->DeleteLocalRef(javaBridge);
533
534    return jniRegisterNativeMethods(env, "android/webkit/JWebCoreJavaBridge",
535                                    gWebCoreJavaBridgeMethods, NELEM(gWebCoreJavaBridgeMethods));
536}
537
538} /* namespace android */
539