Bug 686992 - Draw to Android window/surface directly r=blassey
authorJames Willcox <jwillcox@mozilla.com>
Wed, 21 Sep 2011 12:46:00 -0400
changeset 77375 0ec8974f09172e85dc883a84e808193b80ad88b8
parent 77374 cbc62f5e79e8e33b47ff089b762caadb86399ec7
child 77376 583b4c9b3d4eb73466b57ac4587432c7df7b1561
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersblassey
bugs686992
milestone9.0a1
Bug 686992 - Draw to Android window/surface directly r=blassey
embedding/android/GeckoSurfaceView.java
widget/src/android/AndroidBridge.cpp
widget/src/android/AndroidBridge.h
widget/src/android/AndroidJavaWrappers.cpp
widget/src/android/AndroidJavaWrappers.h
widget/src/android/nsWindow.cpp
--- a/embedding/android/GeckoSurfaceView.java
+++ b/embedding/android/GeckoSurfaceView.java
@@ -243,85 +243,75 @@ class GeckoSurfaceView
             mAbortDraw = false;
         }
 
         if (mShowingSplashScreen)
             drawSplashScreen(holder, width, height);
 
         mSurfaceLock.lock();
 
-        try {
-            if (mInDrawing) {
-                Log.w(LOG_FILE_NAME, "surfaceChanged while mInDrawing is true!");
-            }
+        if (mInDrawing) {
+            Log.w(LOG_FILE_NAME, "surfaceChanged while mInDrawing is true!");
+        }
+
+        boolean invalidSize;
 
-            boolean invalidSize;
+        if (width == 0 || height == 0) {
+            mSoftwareBitmap = null;
+            mSoftwareBuffer = null;
+            mSoftwareBufferCopy = null;
+            invalidSize = true;
+        } else {
+            invalidSize = false;
+        }
 
-            if (width == 0 || height == 0) {
-                mSoftwareBitmap = null;
-                mSoftwareBuffer = null;
-                mSoftwareBufferCopy = null;
-                invalidSize = true;
-            } else {
-                invalidSize = false;
-            }
+        boolean doSyncDraw =
+            mDrawMode == DRAW_2D &&
+            !invalidSize &&
+            GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning);
+        mSyncDraw = doSyncDraw;
 
-            boolean doSyncDraw =
-                mDrawMode == DRAW_2D &&
-                !invalidSize &&
-                GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning);
-            mSyncDraw = doSyncDraw;
+        mFormat = format;
+        mWidth = width;
+        mHeight = height;
+        mSurfaceValid = true;
 
-            mFormat = format;
-            mWidth = width;
-            mHeight = height;
-            mSurfaceValid = true;
+        Log.i(LOG_FILE_NAME, "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
 
-            Log.i(LOG_FILE_NAME, "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
-
+        try {
             DisplayMetrics metrics = new DisplayMetrics();
             GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
 
             GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height,
                                           metrics.widthPixels, metrics.heightPixels);
             GeckoAppShell.sendEventToGecko(e);
-
-            if (!doSyncDraw) {
-                if (mDrawMode == DRAW_GLES_2 || mShowingSplashScreen)
-                    return;
-                Canvas c = holder.lockCanvas();
-                c.drawARGB(255, 255, 255, 255);
-                holder.unlockCanvasAndPost(c);
-                return;
-            } else {
-                GeckoAppShell.scheduleRedraw();
-            }
         } finally {
             mSurfaceLock.unlock();
-            if (mDrawMode == DRAW_GLES_2) {
-                // Force a frame to be drawn before the surfaceChange returns,
-                // otherwise we get artifacts.
-                GeckoAppShell.scheduleRedraw();
-                GeckoAppShell.geckoEventSync();
-            }
         }
 
-        Object syncDrawObject = null;
-        try {
-            syncDrawObject = mSyncDraws.take();
-        } catch (InterruptedException ie) {
-            Log.e(LOG_FILE_NAME, "Threw exception while getting sync draw bitmap/buffer: ", ie);
-        }
-        if (syncDrawObject != null) {
-            if (syncDrawObject instanceof Bitmap)
-                draw(holder, (Bitmap)syncDrawObject);
-            else
-                draw(holder, (ByteBuffer)syncDrawObject);
-        } else {
-            Log.e("GeckoSurfaceViewJava", "Synchronised draw object is null");
+        if (doSyncDraw) {
+            Object syncDrawObject = null;
+            try {
+                syncDrawObject = mSyncDraws.take();
+            } catch (InterruptedException ie) {
+                Log.e(LOG_FILE_NAME, "Threw exception while getting sync draw bitmap/buffer: ", ie);
+            }
+            if (syncDrawObject != null) {
+                if (syncDrawObject instanceof Bitmap)
+                    draw(holder, (Bitmap)syncDrawObject);
+                else
+                    draw(holder, (ByteBuffer)syncDrawObject);
+            } else {
+                Log.e("GeckoSurfaceViewJava", "Synchronised draw object is null");
+            }
+        } else if (!mShowingSplashScreen) {
+            // Make sure a frame is drawn before we return
+            // otherwise we see artifacts or a black screen
+            GeckoAppShell.scheduleRedraw();
+            GeckoAppShell.geckoEventSync();
         }
     }
 
     public void surfaceCreated(SurfaceHolder holder) {
         Log.i(LOG_FILE_NAME, "surface created");
         GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_CREATED);
         GeckoAppShell.sendEventToGecko(e);
         if (mShowingSplashScreen)
@@ -367,16 +357,20 @@ class GeckoSurfaceView
             mSoftwareBufferCopy.getWidth() != mWidth) {
             mSoftwareBufferCopy = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
         }
 
         mDrawMode = DRAW_2D;
         return mSoftwareBuffer;
     }
 
+    public Surface getSurface() {
+        return getHolder().getSurface();
+    }
+
     /*
      * Called on Gecko thread
      */
 
     public static final int DRAW_ERROR = 0;
     public static final int DRAW_GLES_2 = 1;
     public static final int DRAW_2D = 2;
     // Drawing is disable when the surface buffer
--- a/widget/src/android/AndroidBridge.cpp
+++ b/widget/src/android/AndroidBridge.cpp
@@ -101,18 +101,19 @@ PRBool
 AndroidBridge::Init(JNIEnv *jEnv,
                     jclass jGeckoAppShellClass)
 {
     ALOG_BRIDGE("AndroidBridge::Init");
     jEnv->GetJavaVM(&mJavaVM);
 
     mJNIEnv = nsnull;
     mThread = nsnull;
-    mOpenedBitmapLibrary = false;
+    mOpenedGraphicsLibraries = false;
     mHasNativeBitmapAccess = false;
+    mHasNativeWindowAccess = false;
 
     mGeckoAppShellClass = (jclass) jEnv->NewGlobalRef(jGeckoAppShellClass);
 
     jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)V");
     jNotifyIMEEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEEnabled", "(ILjava/lang/String;Ljava/lang/String;Z)V");
     jNotifyIMEChange = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEChange", "(Ljava/lang/String;III)V");
     jAcknowledgeEventSync = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "acknowledgeEventSync", "()V");
 
@@ -972,43 +973,56 @@ AndroidBridge::ExecuteNextRunnable()
     if (mRunnableQueue.Count() > 0) {
         nsIRunnable* r = mRunnableQueue[0];
         __android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "going to run %p", r);
         r->Run();
         mRunnableQueue.RemoveObjectAt(0);
     }
     __android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "leaving %s", __PRETTY_FUNCTION__);
 }
+
+void
+AndroidBridge::OpenGraphicsLibraries()
+{
+    if (!mOpenedGraphicsLibraries) {
+        // Try to dlopen libjnigraphics.so for direct bitmap access on
+        // Android 2.2+ (API level 8)
+        mOpenedGraphicsLibraries = true;
+
+        void *handle = dlopen("/system/lib/libjnigraphics.so", RTLD_LAZY | RTLD_LOCAL);
+        if (handle) {
+            AndroidBitmap_getInfo = (int (*)(JNIEnv *, jobject, void *))dlsym(handle, "AndroidBitmap_getInfo");
+            AndroidBitmap_lockPixels = (int (*)(JNIEnv *, jobject, void **))dlsym(handle, "AndroidBitmap_lockPixels");
+            AndroidBitmap_unlockPixels = (int (*)(JNIEnv *, jobject))dlsym(handle, "AndroidBitmap_unlockPixels");
+
+            ALOG_BRIDGE("Successfully opened libjnigraphics.so");
+        }
+
+        // Try to dlopen libandroid.so for and native window access on
+        // Android 2.3+ (API level 9)
+        handle = dlopen("/system/lib/libandroid.so", RTLD_LAZY | RTLD_LOCAL);
+        if (handle) {
+            ANativeWindow_fromSurface = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurface");
+            ANativeWindow_release = (void (*)(void*))dlsym(handle, "ANativeWindow_release");
+            ANativeWindow_setBuffersGeometry = (int (*)(void*, int, int, int)) dlsym(handle, "ANativeWindow_setBuffersGeometry");
+            ANativeWindow_lock = (int (*)(void*, void*, void*)) dlsym(handle, "ANativeWindow_lock");
+            ANativeWindow_unlockAndPost = (int (*)(void*))dlsym(handle, "ANativeWindow_unlockAndPost");
+ 
+            ALOG_BRIDGE("Successfully opened libandroid.so");
+        }
+
+        mHasNativeBitmapAccess = AndroidBitmap_getInfo && AndroidBitmap_lockPixels && AndroidBitmap_unlockPixels;
+        mHasNativeWindowAccess = ANativeWindow_fromSurface && ANativeWindow_release && ANativeWindow_lock && ANativeWindow_unlockAndPost;
+    }
+}
+
 bool
 AndroidBridge::HasNativeBitmapAccess()
 {
-    if (!mOpenedBitmapLibrary) {
-        // Try to dlopen libjnigraphics.so for direct bitmap access on
-        // Android 2.2+ (API level 8)
-        mOpenedBitmapLibrary = true;
-
-        void *handle = dlopen("/system/lib/libjnigraphics.so", RTLD_LAZY | RTLD_LOCAL);
-        if (handle == nsnull)
-            return false;
-
-        AndroidBitmap_getInfo = (int (*)(JNIEnv *, jobject, void *))dlsym(handle, "AndroidBitmap_getInfo");
-        if (AndroidBitmap_getInfo == nsnull)
-            return false;
-
-        AndroidBitmap_lockPixels = (int (*)(JNIEnv *, jobject, void **))dlsym(handle, "AndroidBitmap_lockPixels");
-        if (AndroidBitmap_lockPixels == nsnull)
-            return false;
-
-        AndroidBitmap_unlockPixels = (int (*)(JNIEnv *, jobject))dlsym(handle, "AndroidBitmap_unlockPixels");
-        if (AndroidBitmap_unlockPixels == nsnull)
-            return false;
-
-        ALOG_BRIDGE("Successfully opened libjnigraphics.so");
-        mHasNativeBitmapAccess = true;
-    }
+    OpenGraphicsLibraries();
 
     return mHasNativeBitmapAccess;
 }
 
 bool
 AndroidBridge::ValidateBitmap(jobject bitmap, int width, int height)
 {
     // This structure is defined in Android API level 8's <android/bitmap.h>
@@ -1052,8 +1066,97 @@ AndroidBridge::LockBitmap(jobject bitmap
 
 void
 AndroidBridge::UnlockBitmap(jobject bitmap)
 {
     int err;
     if ((err = AndroidBitmap_unlockPixels(JNI(), bitmap)) != 0)
         ALOG_BRIDGE("AndroidBitmap_unlockPixels failed! (error %d)", err);
 }
+
+
+bool
+AndroidBridge::HasNativeWindowAccess()
+{
+    OpenGraphicsLibraries();
+
+    return mHasNativeWindowAccess;
+}
+
+void*
+AndroidBridge::AcquireNativeWindow(jobject surface)
+{
+    if (!HasNativeWindowAccess())
+        return nsnull;
+
+    return ANativeWindow_fromSurface(JNI(), surface);
+}
+
+void
+AndroidBridge::ReleaseNativeWindow(void *window)
+{
+    if (!window)
+        return;
+
+    ANativeWindow_release(window);
+}
+
+bool
+AndroidBridge::SetNativeWindowFormat(void *window, int format)
+{
+    return ANativeWindow_setBuffersGeometry(window, 0, 0, format) == 0;
+}
+
+bool
+AndroidBridge::LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride)
+{
+    /* Copied from native_window.h in Android NDK (platform-9) */
+    typedef struct ANativeWindow_Buffer {
+        // The number of pixels that are show horizontally.
+        int32_t width;
+
+        // The number of pixels that are shown vertically.
+        int32_t height;
+
+        // The number of *pixels* that a line in the buffer takes in
+        // memory.  This may be >= width.
+        int32_t stride;
+
+        // The format of the buffer.  One of WINDOW_FORMAT_*
+        int32_t format;
+
+        // The actual bits.
+        void* bits;
+
+        // Do not touch.
+        uint32_t reserved[6];
+    } ANativeWindow_Buffer;
+
+    int err;
+    ANativeWindow_Buffer buffer;
+
+    *bits = NULL;
+    *width = *height = *format = 0;
+    if ((err = ANativeWindow_lock(window, (void*)&buffer, NULL)) != 0) {
+        ALOG_BRIDGE("ANativeWindow_lock failed! (error %d)", err);
+        return false;
+    }
+
+    *bits = (unsigned char*)buffer.bits;
+    *width = buffer.width;
+    *height = buffer.height;
+    *format = buffer.format;
+    *stride = buffer.stride;
+
+    return true;
+}
+
+bool
+AndroidBridge::UnlockWindow(void* window)
+{
+    int err;
+    if ((err = ANativeWindow_unlockAndPost(window)) != 0) {
+        ALOG_BRIDGE("ANativeWindow_unlockAndPost failed! (error %d)", err);
+        return false;
+    }
+
+    return true;
+}
--- a/widget/src/android/AndroidBridge.h
+++ b/widget/src/android/AndroidBridge.h
@@ -262,16 +262,32 @@ public:
     void *LockBitmap(jobject bitmap);
 
     void UnlockBitmap(jobject bitmap);
 
     void PostToJavaThread(nsIRunnable* aRunnable, PRBool aMainThread = PR_FALSE);
 
     void ExecuteNextRunnable();
 
+    /* Copied from Android's native_window.h in newer (platform 9) NDK */
+    enum {
+        WINDOW_FORMAT_RGBA_8888          = 1,
+        WINDOW_FORMAT_RGBX_8888          = 2,
+        WINDOW_FORMAT_RGB_565            = 4,
+    };
+
+    bool HasNativeWindowAccess();
+
+    void *AcquireNativeWindow(jobject surface);
+    void ReleaseNativeWindow(void *window);
+    bool SetNativeWindowFormat(void *window, int format);
+
+    bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride);
+    bool UnlockWindow(void *window);
+
 protected:
     static AndroidBridge *sBridge;
 
     // the global JavaVM
     JavaVM *mJavaVM;
 
     // the JNIEnv for the main thread
     JNIEnv *mJNIEnv;
@@ -283,18 +299,21 @@ protected:
     // the GeckoAppShell java class
     jclass mGeckoAppShellClass;
 
     AndroidBridge() { }
     PRBool Init(JNIEnv *jEnv, jclass jGeckoApp);
 
     void EnsureJNIThread();
 
-    bool mOpenedBitmapLibrary;
+    bool mOpenedGraphicsLibraries;
+    void OpenGraphicsLibraries();
+
     bool mHasNativeBitmapAccess;
+    bool mHasNativeWindowAccess;
 
     nsCOMArray<nsIRunnable> mRunnableQueue;
 
     // other things
     jmethodID jNotifyIME;
     jmethodID jNotifyIMEEnabled;
     jmethodID jNotifyIMEChange;
     jmethodID jAcknowledgeEventSync;
@@ -341,16 +360,23 @@ protected:
     jclass jEGLDisplayImplClass;
     jclass jEGLContextClass;
     jclass jEGL10Class;
 
     // calls we've dlopened from libjnigraphics.so
     int (* AndroidBitmap_getInfo)(JNIEnv *env, jobject bitmap, void *info);
     int (* AndroidBitmap_lockPixels)(JNIEnv *env, jobject bitmap, void **buffer);
     int (* AndroidBitmap_unlockPixels)(JNIEnv *env, jobject bitmap);
+
+    void* (*ANativeWindow_fromSurface)(JNIEnv *env, jobject surface);
+    void (*ANativeWindow_release)(void *window);
+    int (*ANativeWindow_setBuffersGeometry)(void *window, int width, int height, int format);
+
+    int (* ANativeWindow_lock)(void *window, void *outBuffer, void *inOutDirtyBounds);
+    int (* ANativeWindow_unlockAndPost)(void *window);
 };
 
 }
 
 extern "C" JNIEnv * GetJNIForThread();
 extern PRBool mozilla_AndroidBridge_SetMainThread(void *);
 extern jclass GetGeckoAppShellClass();
 
--- a/widget/src/android/AndroidJavaWrappers.cpp
+++ b/widget/src/android/AndroidJavaWrappers.cpp
@@ -104,16 +104,17 @@ jmethodID AndroidAddress::jGetThoroughfa
 
 jclass AndroidGeckoSurfaceView::jGeckoSurfaceViewClass = 0;
 jmethodID AndroidGeckoSurfaceView::jBeginDrawingMethod = 0;
 jmethodID AndroidGeckoSurfaceView::jEndDrawingMethod = 0;
 jmethodID AndroidGeckoSurfaceView::jDraw2DBitmapMethod = 0;
 jmethodID AndroidGeckoSurfaceView::jDraw2DBufferMethod = 0;
 jmethodID AndroidGeckoSurfaceView::jGetSoftwareDrawBitmapMethod = 0;
 jmethodID AndroidGeckoSurfaceView::jGetSoftwareDrawBufferMethod = 0;
+jmethodID AndroidGeckoSurfaceView::jGetSurfaceMethod = 0;
 jmethodID AndroidGeckoSurfaceView::jGetHolderMethod = 0;
 
 #define JNI()  (AndroidBridge::JNI())
 
 #define initInit() jclass jClass
 
 // note that this also sets jClass
 #define getClassGlobalRef(cname) \
@@ -178,16 +179,17 @@ AndroidGeckoSurfaceView::InitGeckoSurfac
     jGeckoSurfaceViewClass = getClassGlobalRef("org/mozilla/gecko/GeckoSurfaceView");
 
     jBeginDrawingMethod = getMethod("beginDrawing", "()I");
     jGetSoftwareDrawBitmapMethod = getMethod("getSoftwareDrawBitmap", "()Landroid/graphics/Bitmap;");
     jGetSoftwareDrawBufferMethod = getMethod("getSoftwareDrawBuffer", "()Ljava/nio/ByteBuffer;");
     jEndDrawingMethod = getMethod("endDrawing", "()V");
     jDraw2DBitmapMethod = getMethod("draw2D", "(Landroid/graphics/Bitmap;II)V");
     jDraw2DBufferMethod = getMethod("draw2D", "(Ljava/nio/ByteBuffer;I)V");
+    jGetSurfaceMethod = getMethod("getSurface", "()Landroid/view/Surface;");
     jGetHolderMethod = getMethod("getHolder", "()Landroid/view/SurfaceHolder;");
 }
 
 void
 AndroidLocation::InitLocationClass(JNIEnv *jEnv)
 {
     initInit();
 
@@ -512,16 +514,22 @@ AndroidGeckoSurfaceView::GetSoftwareDraw
 
 jobject
 AndroidGeckoSurfaceView::GetSoftwareDrawBuffer()
 {
     return JNI()->CallObjectMethod(wrapped_obj, jGetSoftwareDrawBufferMethod);
 }
 
 jobject
+AndroidGeckoSurfaceView::GetSurface()
+{
+    return JNI()->CallObjectMethod(wrapped_obj, jGetSurfaceMethod);
+}
+
+jobject
 AndroidGeckoSurfaceView::GetSurfaceHolder()
 {
     return JNI()->CallObjectMethod(wrapped_obj, jGetHolderMethod);
 }
 
 void
 AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
 {
--- a/widget/src/android/AndroidJavaWrappers.h
+++ b/widget/src/android/AndroidJavaWrappers.h
@@ -170,28 +170,31 @@ public:
 
     int BeginDrawing();
     jobject GetSoftwareDrawBitmap();
     jobject GetSoftwareDrawBuffer();
     void EndDrawing();
     void Draw2D(jobject bitmap, int width, int height);
     void Draw2D(jobject buffer, int stride);
 
+    jobject GetSurface();
+
     // must have a JNI local frame when calling this,
     // and you'd better know what you're doing
     jobject GetSurfaceHolder();
 
 protected:
     static jclass jGeckoSurfaceViewClass;
     static jmethodID jBeginDrawingMethod;
     static jmethodID jEndDrawingMethod;
     static jmethodID jDraw2DBitmapMethod;
     static jmethodID jDraw2DBufferMethod;
     static jmethodID jGetSoftwareDrawBitmapMethod;
     static jmethodID jGetSoftwareDrawBufferMethod;
+    static jmethodID jGetSurfaceMethod;
     static jmethodID jGetHolderMethod;
 };
 
 class AndroidKeyEvent
 {
 public:
     enum {
         KEYCODE_UNKNOWN            = 0,
--- a/widget/src/android/nsWindow.cpp
+++ b/widget/src/android/nsWindow.cpp
@@ -124,16 +124,17 @@ static PRBool gMenuConsumed;
 // stacking order, so the window at gAndroidBounds[0] is the topmost
 // one.
 static nsTArray<nsWindow*> gTopLevelWindows;
 
 static nsRefPtr<gl::GLContext> sGLContext;
 static bool sFailedToCreateGLContext = false;
 static bool sValidSurface;
 static bool sSurfaceExists = false;
+static void *sNativeWindow = nsnull;
 
 // Multitouch swipe thresholds in inches
 static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
 static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
 
 static nsWindow*
 TopWindow()
 {
@@ -841,22 +842,37 @@ nsWindow::OnGlobalAndroidEvent(AndroidGe
             } else {
                 NS_WARNING("Sending unexpected IME event to top window");
                 win->OnIMEEvent(ae);
             }
             break;
 
         case AndroidGeckoEvent::SURFACE_CREATED:
             sSurfaceExists = true;
+
+            if (AndroidBridge::Bridge()->HasNativeWindowAccess()) {
+                AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
+                jobject surface = sview.GetSurface();
+                if (surface) {
+                    sNativeWindow = AndroidBridge::Bridge()->AcquireNativeWindow(surface);
+                    if (sNativeWindow) {
+                        AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, AndroidBridge::WINDOW_FORMAT_RGB_565);
+                    }
+                }
+            }
             break;
 
         case AndroidGeckoEvent::SURFACE_DESTROYED:
             if (sGLContext && sValidSurface) {
                 sGLContext->ReleaseSurface();
             }
+            if (sNativeWindow) {
+                AndroidBridge::Bridge()->ReleaseNativeWindow(sNativeWindow);
+                sNativeWindow = nsnull;
+            }
             sSurfaceExists = false;
             sValidSurface = false;
             break;
 
         case AndroidGeckoEvent::GECKO_EVENT_SYNC:
             AndroidBridge::Bridge()->AcknowledgeEventSync();
             break;
 
@@ -997,17 +1013,45 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
 
     AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
 
     NS_ASSERTION(!sview.isNull(), "SurfaceView is null!");
 
     AndroidBridge::Bridge()->HideProgressDialogOnce();
 
     if (GetLayerManager(nsnull)->GetBackendType() == LayerManager::LAYERS_BASIC) {
-        if (AndroidBridge::Bridge()->HasNativeBitmapAccess()) {
+        if (sNativeWindow) {
+            unsigned char *bits;
+            int width, height, format, stride;
+            if (!AndroidBridge::Bridge()->LockWindow(sNativeWindow, &bits, &width, &height, &format, &stride)) {
+                ALOG("failed to lock buffer - skipping draw");
+                return;
+            }
+
+            if (!bits || format != AndroidBridge::WINDOW_FORMAT_RGB_565 ||
+                width != mBounds.width || height != mBounds.height) {
+
+                ALOG("surface is not expected dimensions or format - skipping draw");
+                AndroidBridge::Bridge()->UnlockWindow(sNativeWindow);
+                return;
+            }
+
+            nsRefPtr<gfxImageSurface> targetSurface =
+                new gfxImageSurface(bits,
+                                    gfxIntSize(mBounds.width, mBounds.height),
+                                    stride * 2,
+                                    gfxASurface::ImageFormatRGB16_565);
+            if (targetSurface->CairoStatus()) {
+                ALOG("### Failed to create a valid surface from the bitmap");
+            } else {
+                DrawTo(targetSurface);
+            }
+
+            AndroidBridge::Bridge()->UnlockWindow(sNativeWindow);
+        } else if (AndroidBridge::Bridge()->HasNativeBitmapAccess()) {
             jobject bitmap = sview.GetSoftwareDrawBitmap();
             if (!bitmap) {
                 ALOG("no bitmap to draw into - skipping draw");
                 return;
             }
 
             if (!AndroidBridge::Bridge()->ValidateBitmap(bitmap, mBounds.width, mBounds.height))
                 return;