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