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 59 namespace android { 60 61 // ---------------------------------------------------------------------------- 62 63 static jfieldID gJavaBridge_ObjectID; 64 65 // ---------------------------------------------------------------------------- 66 67 class JavaBridge : public TimerClient, public CookieClient, public PluginClient, public KeyGeneratorClient, public FileSystemClient 68 { 69 public: 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 115 private: 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 131 static void (*sSharedTimerFiredCallback)(); 132 133 JavaBridge::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 168 JavaBridge::~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 183 void 184 JavaBridge::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 193 void 194 JavaBridge::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 203 void 204 JavaBridge::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 220 WTF::String 221 JavaBridge::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 238 bool 239 JavaBridge::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 249 WTF::Vector<WTF::String> 250 JavaBridge::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 270 WTF::String 271 JavaBridge::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 283 void 284 JavaBridge::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 292 void 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 304 WTF::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 323 WTF::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 341 WTF::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 356 void JavaBridge::Constructor(JNIEnv* env, jobject obj) 357 { 358 JavaBridge* javaBridge = new JavaBridge(env, obj); 359 env->SetIntField(obj, gJavaBridge_ObjectID, (jint)javaBridge); 360 } 361 362 void 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 LOGV("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 373 void 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 388 void JavaBridge::SetCacheSize(JNIEnv* env, jobject obj, jint bytes) 389 { 390 WebCore::memoryCache()->setCapacities(0, bytes/2, bytes); 391 } 392 393 void JavaBridge::SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online) 394 { 395 WebCore::networkStateNotifier().networkStateChange(online); 396 } 397 398 void 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 421 void JavaBridge::ServiceFuncPtrQueue(JNIEnv*) 422 { 423 JavaSharedClient::ServiceFunctionPtrQueue(); 424 } 425 426 void 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 443 void 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 472 void JavaBridge::AddPackageName(JNIEnv* env, jobject obj, jstring packageName) 473 { 474 packageNotifier().addPackageName(jstringToWtfString(env, packageName)); 475 } 476 477 void JavaBridge::RemovePackageName(JNIEnv* env, jobject obj, jstring packageName) 478 { 479 packageNotifier().removePackageName(jstringToWtfString(env, packageName)); 480 } 481 482 void 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 */ 498 static 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 526 int 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