Graphics.cpp revision 1a9c27c312ba20b2ceafcde18ce451724782d2a5
1#define LOG_TAG "GraphicsJNI" 2 3#include "jni.h" 4#include "GraphicsJNI.h" 5#include "NIOBuffer.h" 6#include "SkPicture.h" 7#include "SkRegion.h" 8#include <android_runtime/AndroidRuntime.h> 9 10//#define REPORT_SIZE_TO_JVM 11//#define TRACK_LOCK_COUNT 12 13void doThrow(JNIEnv* env, const char* exc, const char* msg) { 14 // don't throw a new exception if we already have one pending 15 if (env->ExceptionCheck() == JNI_FALSE) { 16 jclass npeClazz; 17 18 npeClazz = env->FindClass(exc); 19 LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc); 20 21 env->ThrowNew(npeClazz, msg); 22 } 23} 24 25void doThrowNPE(JNIEnv* env) { 26 doThrow(env, "java/lang/NullPointerException"); 27} 28 29void doThrowAIOOBE(JNIEnv* env) { 30 doThrow(env, "java/lang/ArrayIndexOutOfBoundsException"); 31} 32 33void doThrowRE(JNIEnv* env, const char* msg) { 34 doThrow(env, "java/lang/RuntimeException", msg); 35} 36 37void doThrowIAE(JNIEnv* env, const char* msg) { 38 doThrow(env, "java/lang/IllegalArgumentException", msg); 39} 40 41void doThrowISE(JNIEnv* env, const char* msg) { 42 doThrow(env, "java/lang/IllegalStateException", msg); 43} 44 45void doThrowOOME(JNIEnv* env, const char* msg) { 46 doThrow(env, "java/lang/OutOfMemoryError", msg); 47} 48 49bool GraphicsJNI::hasException(JNIEnv *env) { 50 if (env->ExceptionCheck() != 0) { 51 LOGE("*** Uncaught exception returned from Java call!\n"); 52 env->ExceptionDescribe(); 53 return true; 54 } 55 return false; 56} 57 58/////////////////////////////////////////////////////////////////////////////// 59 60AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array, 61 int minLength, JNIAccess access) 62: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 63 SkASSERT(env); 64 if (array) { 65 fLen = env->GetArrayLength(array); 66 if (fLen < minLength) { 67 sk_throw(); 68 } 69 fPtr = env->GetFloatArrayElements(array, NULL); 70 } 71 fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0; 72} 73 74AutoJavaFloatArray::~AutoJavaFloatArray() { 75 if (fPtr) { 76 fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode); 77 } 78} 79 80AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array, 81 int minLength) 82: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 83 SkASSERT(env); 84 if (array) { 85 fLen = env->GetArrayLength(array); 86 if (fLen < minLength) { 87 sk_throw(); 88 } 89 fPtr = env->GetIntArrayElements(array, NULL); 90 } 91} 92 93AutoJavaIntArray::~AutoJavaIntArray() { 94 if (fPtr) { 95 fEnv->ReleaseIntArrayElements(fArray, fPtr, 0); 96 } 97} 98 99AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array, 100 int minLength, JNIAccess access) 101: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 102 SkASSERT(env); 103 if (array) { 104 fLen = env->GetArrayLength(array); 105 if (fLen < minLength) { 106 sk_throw(); 107 } 108 fPtr = env->GetShortArrayElements(array, NULL); 109 } 110 fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0; 111} 112 113AutoJavaShortArray::~AutoJavaShortArray() { 114 if (fPtr) { 115 fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode); 116 } 117} 118 119AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array, 120 int minLength) 121: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 122 SkASSERT(env); 123 if (array) { 124 fLen = env->GetArrayLength(array); 125 if (fLen < minLength) { 126 sk_throw(); 127 } 128 fPtr = env->GetByteArrayElements(array, NULL); 129 } 130} 131 132AutoJavaByteArray::~AutoJavaByteArray() { 133 if (fPtr) { 134 fEnv->ReleaseByteArrayElements(fArray, fPtr, 0); 135 } 136} 137 138/////////////////////////////////////////////////////////////////////////////// 139 140static jclass gRect_class; 141static jfieldID gRect_leftFieldID; 142static jfieldID gRect_topFieldID; 143static jfieldID gRect_rightFieldID; 144static jfieldID gRect_bottomFieldID; 145 146static jclass gRectF_class; 147static jfieldID gRectF_leftFieldID; 148static jfieldID gRectF_topFieldID; 149static jfieldID gRectF_rightFieldID; 150static jfieldID gRectF_bottomFieldID; 151 152static jclass gPoint_class; 153static jfieldID gPoint_xFieldID; 154static jfieldID gPoint_yFieldID; 155 156static jclass gPointF_class; 157static jfieldID gPointF_xFieldID; 158static jfieldID gPointF_yFieldID; 159 160static jclass gBitmap_class; 161static jfieldID gBitmap_nativeInstanceID; 162static jmethodID gBitmap_constructorMethodID; 163static jmethodID gBitmap_allocBufferMethodID; 164 165static jclass gBitmapConfig_class; 166static jfieldID gBitmapConfig_nativeInstanceID; 167 168static jclass gCanvas_class; 169static jfieldID gCanvas_nativeInstanceID; 170 171static jclass gPaint_class; 172static jfieldID gPaint_nativeInstanceID; 173 174static jclass gPicture_class; 175static jfieldID gPicture_nativeInstanceID; 176 177static jclass gRegion_class; 178static jfieldID gRegion_nativeInstanceID; 179static jmethodID gRegion_constructorMethodID; 180 181static jobject gVMRuntime_singleton; 182static jmethodID gVMRuntime_trackExternalAllocationMethodID; 183static jmethodID gVMRuntime_trackExternalFreeMethodID; 184 185/////////////////////////////////////////////////////////////////////////////// 186 187void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B) 188{ 189 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 190 191 *L = env->GetIntField(obj, gRect_leftFieldID); 192 *T = env->GetIntField(obj, gRect_topFieldID); 193 *R = env->GetIntField(obj, gRect_rightFieldID); 194 *B = env->GetIntField(obj, gRect_bottomFieldID); 195} 196 197void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B) 198{ 199 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 200 201 env->SetIntField(obj, gRect_leftFieldID, L); 202 env->SetIntField(obj, gRect_topFieldID, T); 203 env->SetIntField(obj, gRect_rightFieldID, R); 204 env->SetIntField(obj, gRect_bottomFieldID, B); 205} 206 207SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir) 208{ 209 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 210 211 ir->set(env->GetIntField(obj, gRect_leftFieldID), 212 env->GetIntField(obj, gRect_topFieldID), 213 env->GetIntField(obj, gRect_rightFieldID), 214 env->GetIntField(obj, gRect_bottomFieldID)); 215 return ir; 216} 217 218void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj) 219{ 220 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 221 222 env->SetIntField(obj, gRect_leftFieldID, ir.fLeft); 223 env->SetIntField(obj, gRect_topFieldID, ir.fTop); 224 env->SetIntField(obj, gRect_rightFieldID, ir.fRight); 225 env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom); 226} 227 228SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r) 229{ 230 SkASSERT(env->IsInstanceOf(obj, gRectF_class)); 231 232 r->set(SkFloatToScalar(env->GetFloatField(obj, gRectF_leftFieldID)), 233 SkFloatToScalar(env->GetFloatField(obj, gRectF_topFieldID)), 234 SkFloatToScalar(env->GetFloatField(obj, gRectF_rightFieldID)), 235 SkFloatToScalar(env->GetFloatField(obj, gRectF_bottomFieldID))); 236 return r; 237} 238 239SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r) 240{ 241 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 242 243 r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)), 244 SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)), 245 SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)), 246 SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID))); 247 return r; 248} 249 250void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj) 251{ 252 SkASSERT(env->IsInstanceOf(obj, gRectF_class)); 253 254 env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft)); 255 env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop)); 256 env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight)); 257 env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom)); 258} 259 260SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point) 261{ 262 SkASSERT(env->IsInstanceOf(obj, gPoint_class)); 263 264 point->set(env->GetIntField(obj, gPoint_xFieldID), 265 env->GetIntField(obj, gPoint_yFieldID)); 266 return point; 267} 268 269void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj) 270{ 271 SkASSERT(env->IsInstanceOf(obj, gPoint_class)); 272 273 env->SetIntField(obj, gPoint_xFieldID, ir.fX); 274 env->SetIntField(obj, gPoint_yFieldID, ir.fY); 275} 276 277SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point) 278{ 279 SkASSERT(env->IsInstanceOf(obj, gPointF_class)); 280 281 point->set(SkFloatToScalar(env->GetIntField(obj, gPointF_xFieldID)), 282 SkFloatToScalar(env->GetIntField(obj, gPointF_yFieldID))); 283 return point; 284} 285 286void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj) 287{ 288 SkASSERT(env->IsInstanceOf(obj, gPointF_class)); 289 290 env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX)); 291 env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY)); 292} 293 294SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) { 295 SkASSERT(env); 296 SkASSERT(bitmap); 297 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); 298 SkBitmap* b = (SkBitmap*)env->GetIntField(bitmap, gBitmap_nativeInstanceID); 299 SkASSERT(b); 300 return b; 301} 302 303SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env, 304 jobject jconfig) { 305 SkASSERT(env); 306 if (NULL == jconfig) { 307 return SkBitmap::kNo_Config; 308 } 309 SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class)); 310 int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); 311 if (c < 0 || c >= SkBitmap::kConfigCount) { 312 c = SkBitmap::kNo_Config; 313 } 314 return static_cast<SkBitmap::Config>(c); 315} 316 317SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { 318 SkASSERT(env); 319 SkASSERT(canvas); 320 SkASSERT(env->IsInstanceOf(canvas, gCanvas_class)); 321 SkCanvas* c = (SkCanvas*)env->GetIntField(canvas, gCanvas_nativeInstanceID); 322 SkASSERT(c); 323 return c; 324} 325 326SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) { 327 SkASSERT(env); 328 SkASSERT(paint); 329 SkASSERT(env->IsInstanceOf(paint, gPaint_class)); 330 SkPaint* p = (SkPaint*)env->GetIntField(paint, gPaint_nativeInstanceID); 331 SkASSERT(p); 332 return p; 333} 334 335SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) 336{ 337 SkASSERT(env); 338 SkASSERT(picture); 339 SkASSERT(env->IsInstanceOf(picture, gPicture_class)); 340 SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID); 341 SkASSERT(p); 342 return p; 343} 344 345SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) 346{ 347 SkASSERT(env); 348 SkASSERT(region); 349 SkASSERT(env->IsInstanceOf(region, gRegion_class)); 350 SkRegion* r = (SkRegion*)env->GetIntField(region, gRegion_nativeInstanceID); 351 SkASSERT(r); 352 return r; 353} 354 355/////////////////////////////////////////////////////////////////////////////////////////// 356 357jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, 358 jbyteArray ninepatch, int density) 359{ 360 SkASSERT(bitmap != NULL); 361 SkASSERT(NULL != bitmap->pixelRef()); 362 363 jobject obj = env->AllocObject(gBitmap_class); 364 if (obj) { 365 env->CallVoidMethod(obj, gBitmap_constructorMethodID, 366 (jint)bitmap, isMutable, ninepatch, density); 367 if (hasException(env)) { 368 obj = NULL; 369 } 370 } 371 return obj; 372} 373 374jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) 375{ 376 SkASSERT(region != NULL); 377 jobject obj = env->AllocObject(gRegion_class); 378 if (obj) { 379 env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0); 380 if (hasException(env)) { 381 obj = NULL; 382 } 383 } 384 return obj; 385} 386 387#include "SkPixelRef.h" 388 389static JNIEnv* vm2env(JavaVM* vm) 390{ 391 JNIEnv* env = NULL; 392 if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env) 393 { 394 SkDebugf("------- [%p] vm->GetEnv() failed\n", vm); 395 sk_throw(); 396 } 397 return env; 398} 399 400#ifdef TRACK_LOCK_COUNT 401 static int gLockCount; 402#endif 403 404/////////////////////////////////////////////////////////////////////////////// 405 406#include "SkMallocPixelRef.h" 407 408/* Extend SkMallocPixelRef to inform the VM when we free up the storage 409*/ 410class AndroidPixelRef : public SkMallocPixelRef { 411public: 412 /** Allocate the specified buffer for pixels. The memory is freed when the 413 last owner of this pixelref is gone. Our caller has already informed 414 the VM of our allocation. 415 */ 416 AndroidPixelRef(JNIEnv* env, void* storage, size_t size, 417 SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { 418 SkASSERT(storage); 419 SkASSERT(env); 420 421 if (env->GetJavaVM(&fVM) != JNI_OK) { 422 SkDebugf("------ [%p] env->GetJavaVM failed\n", env); 423 sk_throw(); 424 } 425 } 426 427 virtual ~AndroidPixelRef() { 428 JNIEnv* env = vm2env(fVM); 429// SkDebugf("-------------- inform VM we're releasing %d bytes\n", this->getSize()); 430 jlong jsize = this->getSize(); // the VM wants longs for the size 431 env->CallVoidMethod(gVMRuntime_singleton, 432 gVMRuntime_trackExternalFreeMethodID, 433 jsize); 434 if (GraphicsJNI::hasException(env)) { 435 env->ExceptionClear(); 436 } 437 } 438 439private: 440 JavaVM* fVM; 441}; 442 443bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, 444 SkColorTable* ctable, bool reportSizeToVM) { 445 Sk64 size64 = bitmap->getSize64(); 446 if (size64.isNeg() || !size64.is32()) { 447 doThrow(env, "java/lang/IllegalArgumentException", 448 "bitmap size exceeds 32bits"); 449 return false; 450 } 451 452 size_t size = size64.get32(); 453 jlong jsize = size; // the VM wants longs for the size 454 if (reportSizeToVM) { 455 // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); 456 bool r = env->CallBooleanMethod(gVMRuntime_singleton, 457 gVMRuntime_trackExternalAllocationMethodID, 458 jsize); 459 if (GraphicsJNI::hasException(env)) { 460 return false; 461 } 462 if (!r) { 463 LOGE("VM won't let us allocate %zd bytes\n", size); 464 doThrowOOME(env, "bitmap size exceeds VM budget"); 465 return false; 466 } 467 } 468 // call the version of malloc that returns null on failure 469 void* addr = sk_malloc_flags(size, 0); 470 if (NULL == addr) { 471 if (reportSizeToVM) { 472 // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); 473 // we didn't actually allocate it, so inform the VM 474 env->CallVoidMethod(gVMRuntime_singleton, 475 gVMRuntime_trackExternalFreeMethodID, 476 jsize); 477 if (!GraphicsJNI::hasException(env)) { 478 doThrowOOME(env, "bitmap size too large for malloc"); 479 } 480 } 481 return false; 482 } 483 484 SkPixelRef* pr = reportSizeToVM ? 485 new AndroidPixelRef(env, addr, size, ctable) : 486 new SkMallocPixelRef(addr, size, ctable); 487 bitmap->setPixelRef(pr)->unref(); 488 // since we're already allocated, we lockPixels right away 489 // HeapAllocator behaves this way too 490 bitmap->lockPixels(); 491 return true; 492} 493 494/////////////////////////////////////////////////////////////////////////////// 495 496JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) 497 : fEnv(env), fReportSizeToVM(reportSizeToVM) {} 498 499bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 500 return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM); 501} 502 503//////////////////////////////////////////////////////////////////////////////// 504 505static jclass make_globalref(JNIEnv* env, const char classname[]) 506{ 507 jclass c = env->FindClass(classname); 508 SkASSERT(c); 509 return (jclass)env->NewGlobalRef(c); 510} 511 512static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 513 const char fieldname[], const char type[]) 514{ 515 jfieldID id = env->GetFieldID(clazz, fieldname, type); 516 SkASSERT(id); 517 return id; 518} 519 520int register_android_graphics_Graphics(JNIEnv* env) 521{ 522 jmethodID m; 523 jclass c; 524 525 gRect_class = make_globalref(env, "android/graphics/Rect"); 526 gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I"); 527 gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I"); 528 gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I"); 529 gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I"); 530 531 gRectF_class = make_globalref(env, "android/graphics/RectF"); 532 gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F"); 533 gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F"); 534 gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F"); 535 gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F"); 536 537 gPoint_class = make_globalref(env, "android/graphics/Point"); 538 gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I"); 539 gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I"); 540 541 gPointF_class = make_globalref(env, "android/graphics/PointF"); 542 gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F"); 543 gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F"); 544 545 gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); 546 gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); 547 gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", 548 "(IZ[BI)V"); 549 550 gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config"); 551 gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class, 552 "nativeInt", "I"); 553 554 gCanvas_class = make_globalref(env, "android/graphics/Canvas"); 555 gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I"); 556 557 gPaint_class = make_globalref(env, "android/graphics/Paint"); 558 gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I"); 559 560 gPicture_class = make_globalref(env, "android/graphics/Picture"); 561 gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I"); 562 563 gRegion_class = make_globalref(env, "android/graphics/Region"); 564 gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I"); 565 gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>", 566 "(II)V"); 567 568 // Get the VMRuntime class. 569 c = env->FindClass("dalvik/system/VMRuntime"); 570 SkASSERT(c); 571 // Look up VMRuntime.getRuntime(). 572 m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;"); 573 SkASSERT(m); 574 // Call VMRuntime.getRuntime() and hold onto its result. 575 gVMRuntime_singleton = env->CallStaticObjectMethod(c, m); 576 SkASSERT(gVMRuntime_singleton); 577 gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton); 578 // Look up the VMRuntime methods we'll be using. 579 gVMRuntime_trackExternalAllocationMethodID = 580 env->GetMethodID(c, "trackExternalAllocation", "(J)Z"); 581 gVMRuntime_trackExternalFreeMethodID = 582 env->GetMethodID(c, "trackExternalFree", "(J)V"); 583 584 NIOBuffer::RegisterJNI(env); 585 586 return 0; 587} 588 589