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