Extract GLRoot interface. Fix some synchronization problems.

Change-Id: I4f7a65393fb6cd364afd24ff7b844012e28fcf28
diff --git a/new3d/src/com/android/gallery3d/app/Gallery.java b/new3d/src/com/android/gallery3d/app/Gallery.java
index b5e08c1..f88aad4 100644
--- a/new3d/src/com/android/gallery3d/app/Gallery.java
+++ b/new3d/src/com/android/gallery3d/app/Gallery.java
@@ -78,8 +78,11 @@
     public void onBackPressed() {
         StateView stateView = getStateManager().peekState();
         if (stateView != null) {
-            synchronized (mGLRootView) {
+            mGLRootView.lockRenderThread();
+            try {
                 stateView.onBackPressed();
+            } finally {
+                mGLRootView.unlockRenderThread();
             }
         } else {
             finish();
@@ -110,9 +113,4 @@
         }
         return mStateManager;
     }
-
-    public Object getUiMonitor() {
-        return mGLRootView;
-    }
-
 }
diff --git a/new3d/src/com/android/gallery3d/app/GalleryContext.java b/new3d/src/com/android/gallery3d/app/GalleryContext.java
index 2b8ce87..48bb1b7 100644
--- a/new3d/src/com/android/gallery3d/app/GalleryContext.java
+++ b/new3d/src/com/android/gallery3d/app/GalleryContext.java
@@ -12,7 +12,6 @@
 import com.android.gallery3d.ui.StateManager;
 
 public interface GalleryContext {
-    public Object getUiMonitor();
     public ImageService getImageService();
     public StateManager getStateManager();
     public DataManager getDataManager();
diff --git a/new3d/src/com/android/gallery3d/app/ViewImage.java b/new3d/src/com/android/gallery3d/app/ViewImage.java
index 937b8e8..e55a6ed 100644
--- a/new3d/src/com/android/gallery3d/app/ViewImage.java
+++ b/new3d/src/com/android/gallery3d/app/ViewImage.java
@@ -89,8 +89,11 @@
     @Override
     protected void onPause() {
         super.onPause();
-        synchronized (mGLRootView) {
+        mGLRootView.lockRenderThread();
+        try {
             mImageViewer.close();
+        } finally {
+            mGLRootView.unlockRenderThread();
         }
         mGLRootView.onPause();
     }
@@ -155,9 +158,4 @@
     public StateManager getStateManager() {
         throw new UnsupportedOperationException();
     }
-
-    public Object getUiMonitor() {
-        return mGLRootView;
-    }
-
 }
diff --git a/new3d/src/com/android/gallery3d/data/DatabaseMediaSet.java b/new3d/src/com/android/gallery3d/data/DatabaseMediaSet.java
index 4bdbad2..a837b30 100644
--- a/new3d/src/com/android/gallery3d/data/DatabaseMediaSet.java
+++ b/new3d/src/com/android/gallery3d/data/DatabaseMediaSet.java
@@ -29,8 +29,7 @@
     protected DatabaseMediaSet(GalleryContext context) {
         mContext = context;
 
-        mMainHandler = new SynchronizedHandler(
-                context.getUiMonitor(), context.getMainLooper()) {
+        mMainHandler = new Handler() {
             @Override
             public void handleMessage(Message message) {
                 Util.Assert(message.what == MSG_UPDATE_CONTENT);
diff --git a/new3d/src/com/android/gallery3d/ui/AlbumView.java b/new3d/src/com/android/gallery3d/ui/AlbumView.java
index 0575f92..22a8f1e 100644
--- a/new3d/src/com/android/gallery3d/ui/AlbumView.java
+++ b/new3d/src/com/android/gallery3d/ui/AlbumView.java
@@ -47,7 +47,7 @@
         initializeViews();
         intializeData(data);
 
-        mHandler = new SynchronizedHandler(getGLRootView()) {
+        mHandler = new SynchronizedHandler(getGLRoot()) {
             @Override
             public void handleMessage(Message message) {
                 switch (message.what) {
diff --git a/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java b/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java
index a27d0d0..1b972b7 100644
--- a/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java
+++ b/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java
@@ -49,6 +49,16 @@
      */
     public void putDisplayItem(
             DisplayItem item, float x, float y, float theata) {
+        lockRendering();
+        try {
+            putDisplayItemLocked(item, x, y, theata);
+        } finally {
+            unlockRendering();
+        }
+    }
+
+    private void putDisplayItemLocked(
+            DisplayItem item, float x, float y, float theata) {
         if (item.mPanel != this && item.mPanel != null) {
             throw new IllegalArgumentException();
         }
@@ -71,34 +81,54 @@
     }
 
     public void removeDisplayItem(DisplayItem item) {
-        if (item.mPanel != this) throw new IllegalArgumentException();
-        mItems.remove(item);
-        item.mPanel = null;
+        lockRendering();
+        try {
+            if (item.mPanel != this) throw new IllegalArgumentException();
+            mItems.remove(item);
+            item.mPanel = null;
+        } finally {
+            unlockRendering();
+        }
     }
 
     public void prepareTransition() {
-        mPrepareTransition = true;
-        for (DisplayItem item : mItems) {
-            item.mState = STATE_REMOVED;
+        lockRendering();
+        try {
+            mPrepareTransition = true;
+            for (DisplayItem item : mItems) {
+                item.mState = STATE_REMOVED;
+            }
+        } finally {
+            unlockRendering();
         }
     }
 
     public void startTransition() {
-        mPrepareTransition = false;
-        mAnimationStartTime = START_ANIMATION;
-        invalidate();
+        lockRendering();
+        try {
+            mPrepareTransition = false;
+            mAnimationStartTime = START_ANIMATION;
+            invalidate();
+        } finally {
+            unlockRendering();
+        }
     }
 
     private void onTransitionComplete() {
-        ArrayList<DisplayItem> list = new ArrayList<DisplayItem>();
-        for (DisplayItem item: mItems) {
-            if (item.mState == STATE_REMOVED) {
-                item.mPanel = null;
-            } else {
-                list.add(item);
+        lockRendering();
+        try {
+            ArrayList<DisplayItem> list = new ArrayList<DisplayItem>();
+            for (DisplayItem item: mItems) {
+                if (item.mState == STATE_REMOVED) {
+                    item.mPanel = null;
+                } else {
+                    list.add(item);
+                }
             }
+            mItems = list;
+        } finally {
+            unlockRendering();
         }
-        mItems = list;
     }
 
     @Override
diff --git a/new3d/src/com/android/gallery3d/ui/GLListView.java b/new3d/src/com/android/gallery3d/ui/GLListView.java
index 58c6797..98bf6b0 100644
--- a/new3d/src/com/android/gallery3d/ui/GLListView.java
+++ b/new3d/src/com/android/gallery3d/ui/GLListView.java
@@ -75,13 +75,11 @@
         mHandler = new Handler() {
             @Override
             public void handleMessage(Message msg) {
-                GLRootView root = getGLRootView();
-                if (root != null) {
-                    synchronized (root) {
-                        handleMessageLocked(msg);
-                    }
-                } else {
+                lockRendering();
+                try {
                     handleMessageLocked(msg);
+                } finally {
+                    unlockRendering();
                 }
             }
 
diff --git a/new3d/src/com/android/gallery3d/ui/GLRoot.java b/new3d/src/com/android/gallery3d/ui/GLRoot.java
new file mode 100644
index 0000000..68e4e45
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/GLRoot.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.ui;
+
+import com.android.gallery3d.anim.CanvasAnimation;
+
+public interface GLRoot {
+
+    public static interface OnGLIdleListener {
+        public boolean onGLIdle(GLRoot root, GLCanvas canvas);
+    }
+
+    public void addOnGLIdleListener(OnGLIdleListener listener);
+    public void registerLaunchedAnimation(CanvasAnimation animation);
+    public void requestRender();
+    public void requestLayoutContentPane();
+    public boolean hasStencil();
+
+    public void lockRenderThread();
+    public void unlockRenderThread();
+}
diff --git a/new3d/src/com/android/gallery3d/ui/GLRootView.java b/new3d/src/com/android/gallery3d/ui/GLRootView.java
index e6e3bb9..10361a6 100644
--- a/new3d/src/com/android/gallery3d/ui/GLRootView.java
+++ b/new3d/src/com/android/gallery3d/ui/GLRootView.java
@@ -30,6 +30,7 @@
 
 import java.util.ArrayList;
 import java.util.LinkedList;
+import java.util.concurrent.locks.ReentrantLock;
 
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
@@ -44,7 +45,7 @@
 // (2) The public methods of CameraHeadUpDisplay
 // (3) The overridden methods in GLRootView.
 public class GLRootView extends GLSurfaceView
-        implements GLSurfaceView.Renderer {
+        implements GLSurfaceView.Renderer, GLRoot {
     private static final String TAG = "GLRootView";
 
     private final boolean DEBUG_FPS = false;
@@ -80,9 +81,7 @@
 
     private final IdleRunner mIdleRunner = new IdleRunner();
 
-    public static interface OnGLIdleListener {
-        public boolean onGLIdle(GLRootView root);
-    }
+    private ReentrantLock mRenderLock = new ReentrantLock();
 
     public GLRootView(Context context) {
         this(context, null);
@@ -103,7 +102,11 @@
         return mEglConfigChooser;
     }
 
-    void registerLaunchedAnimation(CanvasAnimation animation) {
+    public boolean hasStencil() {
+        return getEGLConfigChooser().getStencilBits() > 0;
+    }
+
+    public void registerLaunchedAnimation(CanvasAnimation animation) {
         // Register the newly launched animation so that we can set the start
         // time more precisely. (Usually, it takes much longer for first
         // rendering, so we set the animation start time as the time we
@@ -111,11 +114,16 @@
         mAnimations.add(animation);
     }
 
-    public synchronized void addOnGLIdleListener(OnGLIdleListener listener) {
-        mIdleListeners.addLast(listener);
-        if (!mRenderRequested && !mIdleRunner.mActive) {
-            mIdleRunner.mActive = true;
-            queueEvent(mIdleRunner);
+    public void addOnGLIdleListener(OnGLIdleListener listener) {
+        mRenderLock.lock();
+        try {
+            mIdleListeners.addLast(listener);
+            if (!mRenderRequested && !mIdleRunner.mActive) {
+                mIdleRunner.mActive = true;
+                queueEvent(mIdleRunner);
+            }
+        } finally {
+            mRenderLock.unlock();
         }
     }
 
@@ -144,15 +152,20 @@
         super.requestRender();
     }
 
-    public synchronized void requestLayoutContentPane() {
-        if (mContentView == null || (mFlags & FLAG_NEED_LAYOUT) != 0) return;
+    public void requestLayoutContentPane() {
+        mRenderLock.lock();
+        try {
+            if (mContentView == null || (mFlags & FLAG_NEED_LAYOUT) != 0) return;
 
-        // "View" system will invoke onLayout() for initialization(bug ?), we
-        // have to ignore it since the GLThread is not ready yet.
-        if ((mFlags & FLAG_INITIALIZED) == 0) return;
+            // "View" system will invoke onLayout() for initialization(bug ?), we
+            // have to ignore it since the GLThread is not ready yet.
+            if ((mFlags & FLAG_INITIALIZED) == 0) return;
 
-        mFlags |= FLAG_NEED_LAYOUT;
-        requestRender();
+            mFlags |= FLAG_NEED_LAYOUT;
+            requestRender();
+        } finally {
+            mRenderLock.unlock();
+        }
     }
 
     private void layoutContentPane() {
@@ -220,7 +233,16 @@
         ++mFrameCount;
     }
 
-    public synchronized void onDrawFrame(GL10 gl) {
+    public void onDrawFrame(GL10 gl) {
+        mRenderLock.lock();
+        try {
+            onDrawFrameLocked(gl);
+        } finally {
+            mRenderLock.unlock();
+        }
+    }
+
+    private void onDrawFrameLocked(GL10 gl) {
         if (DEBUG_FPS) outputFps();
 
         // release the unbound textures
@@ -265,11 +287,16 @@
     }
 
     @Override
-    public synchronized boolean dispatchTouchEvent(MotionEvent event) {
-        // If this has been detached from root, we don't need to handle event
-        return mContentView != null
-                ? mContentView.dispatchTouchEvent(event)
-                : false;
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        mRenderLock.lock();
+        try {
+            // If this has been detached from root, we don't need to handle event
+            return mContentView != null
+                    ? mContentView.dispatchTouchEvent(event)
+                    : false;
+        } finally {
+            mRenderLock.unlock();
+        }
     }
 
     public DisplayMetrics getDisplayMetrics() {
@@ -292,17 +319,28 @@
             if (mRenderRequested) return false;
             if (mIdleListeners.isEmpty()) return false;
             OnGLIdleListener listener = mIdleListeners.removeFirst();
-            if (listener.onGLIdle(GLRootView.this)) {
+            if (listener.onGLIdle(GLRootView.this, mCanvas)) {
                 mIdleListeners.addLast(listener);
             }
             return mIdleListeners.isEmpty();
         }
 
         public void run() {
-            synchronized (GLRootView.this) {
+            mRenderLock.lock();
+            try {
                 mActive = runInternal();
                 if (mActive) queueEvent(this);
+            } finally {
+                mRenderLock.unlock();
             }
         }
     }
+
+    public void lockRenderThread() {
+        mRenderLock.lock();
+    }
+
+    public void unlockRenderThread() {
+        mRenderLock.unlock();
+    }
 }
diff --git a/new3d/src/com/android/gallery3d/ui/GLView.java b/new3d/src/com/android/gallery3d/ui/GLView.java
index 6ca66ab..296c66a 100644
--- a/new3d/src/com/android/gallery3d/ui/GLView.java
+++ b/new3d/src/com/android/gallery3d/ui/GLView.java
@@ -31,22 +31,21 @@
     public static final int VISIBLE = 0;
     public static final int INVISIBLE = 1;
 
-    public static final int FLAG_INVISIBLE = 1;
-    public static final int FLAG_SET_MEASURED_SIZE = 2;
-    public static final int FLAG_LAYOUT_REQUESTED = 4;
+    private static final int FLAG_INVISIBLE = 1;
+    private static final int FLAG_SET_MEASURED_SIZE = 2;
+    private static final int FLAG_LAYOUT_REQUESTED = 4;
 
     protected final Rect mBounds = new Rect();
     protected final Rect mPaddings = new Rect();
 
-    private GLRootView mRootView;
+    private GLRoot mRoot;
     protected GLView mParent;
     private ArrayList<GLView> mComponents;
     private GLView mMotionTarget;
 
-    private OnTouchListener mOnTouchListener;
     private CanvasAnimation mAnimation;
 
-    protected int mViewFlags = 0;
+    private int mViewFlags = 0;
 
     protected int mMeasuredWidth = 0;
     protected int mMeasuredHeight = 0;
@@ -60,7 +59,7 @@
     protected int mScrollWidth = 0;
 
     public void startAnimation(CanvasAnimation animation) {
-        GLRootView root = getGLRootView();
+        GLRoot root = getGLRoot();
         if (root == null) throw new IllegalStateException();
 
         mAnimation = animation;
@@ -84,10 +83,6 @@
         return (mViewFlags & FLAG_INVISIBLE) == 0 ? VISIBLE : INVISIBLE;
     }
 
-    public static interface OnTouchListener {
-        public boolean onTouch(GLView view, MotionEvent event);
-    }
-
     private boolean setBounds(int left, int top, int right, int bottom) {
         boolean sizeChanged = (right - left) != (mBounds.right - mBounds.left)
                 || (bottom - top) != (mBounds.bottom - mBounds.top);
@@ -98,8 +93,8 @@
     protected void onAddToParent(GLView parent) {
         if (mParent != null) throw new IllegalStateException();
         mParent = parent;
-        if (parent != null && parent.mRootView != null) {
-            onAttachToRoot(parent.mRootView);
+        if (parent != null && parent.mRoot != null) {
+            onAttachToRoot(parent.mRoot);
         }
     }
 
@@ -161,16 +156,12 @@
         return mBounds.bottom - mBounds.top;
     }
 
-    public GLRootView getGLRootView() {
-        return mRootView;
-    }
-
-    public void setOnTouchListener(OnTouchListener listener) {
-        mOnTouchListener = listener;
+    public GLRoot getGLRoot() {
+        return mRoot;
     }
 
     public void invalidate() {
-        GLRootView root = getGLRootView();
+        GLRoot root = getGLRoot();
         if (root != null) root.requestRender();
     }
 
@@ -180,7 +171,7 @@
             mParent.requestLayout();
         } else {
             // Is this a content pane ?
-            GLRootView root = getGLRootView();
+            GLRoot root = getGLRoot();
             if (root != null) root.requestLayoutContentPane();
         }
     }
@@ -220,9 +211,6 @@
     }
 
     protected boolean onTouch(MotionEvent event) {
-        if (mOnTouchListener != null) {
-            return mOnTouchListener.onTouch(this, event);
-        }
         return false;
     }
 
@@ -363,8 +351,8 @@
         }
     }
 
-    protected void onAttachToRoot(GLRootView root) {
-        mRootView = root;
+    protected void onAttachToRoot(GLRoot root) {
+        mRoot = root;
         for (int i = 0, n = getComponentCount(); i < n; ++i) {
             getComponent(i).onAttachToRoot(root);
         }
@@ -374,6 +362,18 @@
         for (int i = 0, n = getComponentCount(); i < n; ++i) {
             getComponent(i).onDetachFromRoot();
         }
-        mRootView = null;
+        mRoot = null;
+    }
+
+    protected void lockRendering() {
+        if (mRoot != null) {
+            mRoot.lockRenderThread();
+        }
+    }
+
+    protected void unlockRendering() {
+        if (mRoot != null) {
+            mRoot.unlockRenderThread();
+        }
     }
 }
diff --git a/new3d/src/com/android/gallery3d/ui/GalleryView.java b/new3d/src/com/android/gallery3d/ui/GalleryView.java
index fe70687..cad3985 100644
--- a/new3d/src/com/android/gallery3d/ui/GalleryView.java
+++ b/new3d/src/com/android/gallery3d/ui/GalleryView.java
@@ -46,7 +46,7 @@
     public void onStart(Bundle data) {
         initializeViews();
         intializeData();
-        mHandler = new SynchronizedHandler(mContext.getUiMonitor()) {
+        mHandler = new SynchronizedHandler(getGLRoot()) {
             @Override
             public void handleMessage(Message message) {
                 switch (message.what) {
diff --git a/new3d/src/com/android/gallery3d/ui/GridSlotAdapter.java b/new3d/src/com/android/gallery3d/ui/GridSlotAdapter.java
index deafed7..d34378c 100644
--- a/new3d/src/com/android/gallery3d/ui/GridSlotAdapter.java
+++ b/new3d/src/com/android/gallery3d/ui/GridSlotAdapter.java
@@ -145,7 +145,7 @@
             updateContent(content);
         }
 
-        public void updateContent(Texture content) {
+        public synchronized void updateContent(Texture content) {
             mContent = content;
             Rect p = mDrawer.getFramePadding();
 
@@ -172,7 +172,7 @@
         }
 
         @Override
-        public void render(GLCanvas canvas) {
+        public synchronized void render(GLCanvas canvas) {
             mDrawer.draw(canvas, mContent, mWidth, mHeight, mChecked);
         }
     }
diff --git a/new3d/src/com/android/gallery3d/ui/ImageViewer.java b/new3d/src/com/android/gallery3d/ui/ImageViewer.java
index e8363a9..e20a931 100644
--- a/new3d/src/com/android/gallery3d/ui/ImageViewer.java
+++ b/new3d/src/com/android/gallery3d/ui/ImageViewer.java
@@ -106,17 +106,10 @@
     private int mSwitchIndex;
     /*private*/ boolean mInTransition = false;
 
-    private SynchronizedHandler mHandler;
-
     public ImageViewer(GalleryContext context) {
 
-        mHandler = new SynchronizedHandler(
-                context.getUiMonitor(), context.getMainLooper());
-
         mGestureDetector = new GestureDetector(context.getAndroidContext(),
-                new MyGestureListener(), mHandler,
-                true /* ignoreMultitouch */);
-
+                new MyGestureListener(), null, true /* ignoreMultitouch */);
         mScaleDetector = new ScaleGestureDetector(
                 context.getAndroidContext(), new MyScaleListener());
         mDownUpDetector = new DownUpDetector(new MyDownUpListener());
@@ -664,12 +657,11 @@
 
     public void close() {
         mUploadIter = null;
-        GLCanvas canvas = getGLRootView().getCanvas();
         for (Tile texture : mActiveTiles.values()) {
             texture.recycle();
         }
         mActiveTiles.clear();
-        freeRecycledTile(canvas);
+        freeRecycledTile();
 
         for (ScreenNailEntry nail : mScreenNails) {
             if (nail.mTexture != null) nail.mTexture.recycle();
@@ -717,7 +709,7 @@
             if (mUploadIter != null
                     && mUploadIter.hasNext() && !mUploader.mActive) {
                 mUploader.mActive = true;
-                getGLRootView().addOnGLIdleListener(mUploader);
+                getGLRoot().addOnGLIdleListener(mUploader);
             }
         } else {
             invalidate();
@@ -741,7 +733,7 @@
         mRecycledHead = tile;
     }
 
-    private void freeRecycledTile(GLCanvas canvas) {
+    private void freeRecycledTile() {
         Tile tile = mRecycledHead;
         while (tile != null) {
             tile.recycle();
@@ -805,9 +797,8 @@
 
         protected boolean mActive;
 
-        public boolean onGLIdle(GLRootView root) {
+        public boolean onGLIdle(GLRoot root, GLCanvas canvas) {
             int quota = UPLOAD_LIMIT;
-            GLCanvas canvas = root.getCanvas();
 
             if (mUploadIter == null) return false;
             Iterator<Tile> iter = mUploadIter;
@@ -910,8 +901,13 @@
         @Override
         public boolean onScroll(
                 MotionEvent e1, MotionEvent e2, float dx, float dy) {
-            if (mInTransition) return true;
-            mAnimationController.scrollBy(dx, dy);
+            lockRendering();
+            try {
+                if (mInTransition) return true;
+                mAnimationController.scrollBy(dx, dy);
+            } finally {
+                unlockRendering();
+            }
             return true;
         }
     }
diff --git a/new3d/src/com/android/gallery3d/ui/MediaSetSlotAdapter.java b/new3d/src/com/android/gallery3d/ui/MediaSetSlotAdapter.java
index 76e20ab..1d7596c 100644
--- a/new3d/src/com/android/gallery3d/ui/MediaSetSlotAdapter.java
+++ b/new3d/src/com/android/gallery3d/ui/MediaSetSlotAdapter.java
@@ -171,7 +171,7 @@
                 updateContent(content);
             }
 
-            public void updateContent(Texture content) {
+            public synchronized void updateContent(Texture content) {
                 mContent = content;
                 Rect p = mDrawer.getFramePadding();
 
@@ -198,7 +198,7 @@
             }
 
             @Override
-            public void render(GLCanvas canvas) {
+            public synchronized void render(GLCanvas canvas) {
                 mDrawer.draw(canvas, mContent, mWidth, mHeight, mChecked, mTopItem);
             }
         }
diff --git a/new3d/src/com/android/gallery3d/ui/Pathbar.java b/new3d/src/com/android/gallery3d/ui/Pathbar.java
index e006817..eaece7c 100644
--- a/new3d/src/com/android/gallery3d/ui/Pathbar.java
+++ b/new3d/src/com/android/gallery3d/ui/Pathbar.java
@@ -176,7 +176,7 @@
     }
 
     @Override
-    protected void onAttachToRoot(GLRootView root) {
+    protected void onAttachToRoot(GLRoot root) {
         super.onAttachToRoot(root);
         mHandler = new SynchronizedHandler(root) {
             @Override
diff --git a/new3d/src/com/android/gallery3d/ui/PhotoView.java b/new3d/src/com/android/gallery3d/ui/PhotoView.java
index 2dacb6b..6decfd9 100644
--- a/new3d/src/com/android/gallery3d/ui/PhotoView.java
+++ b/new3d/src/com/android/gallery3d/ui/PhotoView.java
@@ -54,7 +54,7 @@
 
     @Override
     public void onStart(Bundle data) {
-        mHandler = new SynchronizedHandler(getGLRootView()) {
+        mHandler = new SynchronizedHandler(getGLRoot()) {
             @Override
             public void handleMessage(Message message) {
                 switch (message.what) {
@@ -85,8 +85,11 @@
 
     @Override
     public void onPause() {
-        synchronized (getGLRootView()) {
+        lockRendering();
+        try {
             mImageViewer.close();
+        } finally {
+            unlockRendering();
         }
     }
 
diff --git a/new3d/src/com/android/gallery3d/ui/PopupWindow.java b/new3d/src/com/android/gallery3d/ui/PopupWindow.java
index d0f3d0f..3563740 100644
--- a/new3d/src/com/android/gallery3d/ui/PopupWindow.java
+++ b/new3d/src/com/android/gallery3d/ui/PopupWindow.java
@@ -44,9 +44,9 @@
     }
 
     @Override
-    protected void onAttachToRoot(GLRootView root) {
+    protected void onAttachToRoot(GLRoot root) {
         super.onAttachToRoot(root);
-        mUsingStencil = root.getEGLConfigChooser().getStencilBits() > 0;
+        mUsingStencil = root.hasStencil();
     }
 
     public void setBackground(Texture background) {
diff --git a/new3d/src/com/android/gallery3d/ui/SynchronizedHandler.java b/new3d/src/com/android/gallery3d/ui/SynchronizedHandler.java
index 93cc42e..d36c21d 100644
--- a/new3d/src/com/android/gallery3d/ui/SynchronizedHandler.java
+++ b/new3d/src/com/android/gallery3d/ui/SynchronizedHandler.java
@@ -22,21 +22,19 @@
 
 public class SynchronizedHandler extends Handler {
 
-    private final Object mMonitor;
+    private final GLRoot mRoot;
 
-    public SynchronizedHandler(Object monitor) {
-        mMonitor = Util.checkNotNull(monitor);
-    }
-
-    public SynchronizedHandler(Object monitor, Looper looper) {
-        super(looper);
-        mMonitor = Util.checkNotNull(monitor);
+    public SynchronizedHandler(GLRoot root) {
+        mRoot = Util.checkNotNull(root);
     }
 
     @Override
     public void dispatchMessage(Message message) {
-        synchronized (mMonitor) {
+        mRoot.lockRenderThread();
+        try {
             super.dispatchMessage(message);
+        } finally {
+            mRoot.unlockRenderThread();
         }
     }
 }