Merge "Removing dependence on enable_facelock"
diff --git a/api/current.txt b/api/current.txt
index 7edfa53..97be454 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -992,6 +992,7 @@
     field public static final int textColorTertiary = 16843282; // 0x1010212
     field public static final int textColorTertiaryInverse = 16843283; // 0x1010213
     field public static final int textCursorDrawable = 16843618; // 0x1010362
+    field public static final int textDirection = 16843688; // 0x10103a8
     field public static final int textEditNoPasteWindowLayout = 16843541; // 0x1010315
     field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314
     field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f
@@ -20478,6 +20479,19 @@
     method public int getTopPadding();
   }
 
+  public abstract interface TextDirectionHeuristic {
+  }
+
+  public class TextDirectionHeuristics {
+    ctor public TextDirectionHeuristics();
+    field public static final android.text.TextDirectionHeuristic ANYRTL_LTR;
+    field public static final android.text.TextDirectionHeuristic FIRSTSTRONG_LTR;
+    field public static final android.text.TextDirectionHeuristic FIRSTSTRONG_RTL;
+    field public static final android.text.TextDirectionHeuristic LOCALE;
+    field public static final android.text.TextDirectionHeuristic LTR;
+    field public static final android.text.TextDirectionHeuristic RTL;
+  }
+
   public class TextPaint extends android.graphics.Paint {
     ctor public TextPaint();
     ctor public TextPaint(int);
@@ -23171,6 +23185,7 @@
     method public final android.view.ViewParent getParent();
     method public float getPivotX();
     method public float getPivotY();
+    method public int getResolvedTextDirection();
     method public android.content.res.Resources getResources();
     method public final int getRight();
     method protected float getRightFadingEdgeStrength();
@@ -23190,6 +23205,7 @@
     method public int getSystemUiVisibility();
     method public java.lang.Object getTag();
     method public java.lang.Object getTag(int);
+    method public int getTextDirection();
     method public final int getTop();
     method protected float getTopFadingEdgeStrength();
     method protected int getTopPaddingOffset();
@@ -23321,8 +23337,10 @@
     method public void requestLayout();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+    method protected void resetResolvedTextDirection();
     method public static int resolveSize(int, int);
     method public static int resolveSizeAndState(int, int, int);
+    method protected void resolveTextDirection();
     method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
@@ -23402,6 +23420,7 @@
     method public void setSystemUiVisibility(int);
     method public void setTag(java.lang.Object);
     method public void setTag(int, java.lang.Object);
+    method public void setTextDirection(int);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
     method public void setTranslationX(float);
@@ -23424,6 +23443,7 @@
     method public boolean willNotCacheDrawing();
     method public boolean willNotDraw();
     field public static final android.util.Property ALPHA;
+    field protected static int DEFAULT_TEXT_DIRECTION;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
     field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
@@ -23500,6 +23520,12 @@
     field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
+    field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
+    field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
+    field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
+    field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
+    field public static final int TEXT_DIRECTION_LTR = 3; // 0x3
+    field public static final int TEXT_DIRECTION_RTL = 4; // 0x4
     field public static final android.util.Property TRANSLATION_X;
     field public static final android.util.Property TRANSLATION_Y;
     field protected static final java.lang.String VIEW_LOG_TAG = "View";
@@ -23766,7 +23792,6 @@
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
     method protected void resetResolvedLayoutDirection();
-    method protected void resetResolvedTextDirection();
     method public void scheduleLayoutAnimation();
     method public void setAddStatesFromChildren(boolean);
     method public void setAlwaysDrawnWithCacheEnabled(boolean);
@@ -27595,7 +27620,6 @@
     method protected void resetResolvedDrawables();
     method protected void resetResolvedLayoutDirection();
     method protected void resolveDrawables();
-    method protected void resolveTextDirection();
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
     method public void setCompoundDrawablePadding(int);
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 844ed6a..b816b11 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -75,13 +75,13 @@
     public Messenger replyTo;
 
     /** If set message is in use */
-    /*package*/ static final int FLAG_IN_USE = 1;
+    /*package*/ static final int FLAG_IN_USE = 1 << 0;
 
-    /** Flags reserved for future use (All are reserved for now) */
-    /*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE;
+    /** If set message is asynchronous */
+    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
 
     /** Flags to clear in the copyFrom method */
-    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE;
+    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
 
     /*package*/ int flags;
 
@@ -363,6 +363,48 @@
         target.sendMessage(this);
     }
 
+    /**
+     * Returns true if the message is asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with represent to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#acquireSyncBarrier()}.
+     *
+     * @return True if the message is asynchronous.
+     *
+     * @see #setAsynchronous(boolean)
+     * @see MessageQueue#acquireSyncBarrier()
+     * @see MessageQueue#releaseSyncBarrier()
+     *
+     * @hide
+     */
+    public boolean isAsynchronous() {
+        return (flags & FLAG_ASYNCHRONOUS) != 0;
+    }
+
+    /**
+     * Sets whether the message is asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with represent to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#acquireSyncBarrier()}.
+     *
+     * @param async True if the message is asynchronous.
+     *
+     * @see #isAsynchronous()
+     * @see MessageQueue#acquireSyncBarrier()
+     * @see MessageQueue#releaseSyncBarrier()
+     *
+     * @hide
+     */
+    public void setAsynchronous(boolean async) {
+        if (async) {
+            flags |= FLAG_ASYNCHRONOUS;
+        } else {
+            flags &= ~FLAG_ASYNCHRONOUS;
+        }
+    }
+
     /*package*/ void clearForRecycle() {
         flags = 0;
         what = 0;
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index a658fc4..11dc124 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -39,6 +39,9 @@
     // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
     private boolean mBlocked;
 
+    // Indicates the barrier nesting level.
+    private int mBarrierNestCount;
+
     @SuppressWarnings("unused")
     private int mPtr; // used by native code
     
@@ -93,7 +96,53 @@
             mIdleHandlers.remove(handler);
         }
     }
-    
+
+    /**
+     * Acquires a synchronization barrier.
+     *
+     * While a synchronization barrier is active, only asynchronous messages are
+     * permitted to execute.  Synchronous messages are retained but are not executed
+     * until the synchronization barrier is released.
+     *
+     * This method is used to immediately postpone execution of all synchronous messages
+     * until a condition is met that releases the barrier.  Asynchronous messages are
+     * exempt from the barrier and continue to be executed as usual.
+     *
+     * This call nests and must be matched by an equal number of calls to
+     * {@link #releaseSyncBarrier}.
+     *
+     * @hide
+     */
+    public final void acquireSyncBarrier() {
+        synchronized (this) {
+            mBarrierNestCount += 1;
+        }
+    }
+
+    /**
+     * Releases a synchronization barrier.
+     *
+     * This class undoes one invocation of {@link #acquireSyncBarrier}.
+     *
+     * @throws IllegalStateException if the barrier is not acquired.
+     *
+     * @hide
+     */
+    public final void releaseSyncBarrier() {
+        synchronized (this) {
+            if (mBarrierNestCount == 0) {
+                throw new IllegalStateException("The message queue synchronization barrier "
+                        + "has not been acquired.");
+            }
+
+            mBarrierNestCount -= 1;
+            if (!mBlocked || mMessages == null) {
+                return;
+            }
+        }
+        nativeWake(mPtr);
+    }
+
     MessageQueue() {
         nativeInit();
     }
@@ -120,28 +169,49 @@
             synchronized (this) {
                 // Try to retrieve the next message.  Return if found.
                 final long now = SystemClock.uptimeMillis();
-                final Message msg = mMessages;
-                if (msg != null) {
+
+                Message prevMsg = null;
+                Message msg = mMessages;
+                for (;;) {
+                    if (msg == null) {
+                        // No more messages.
+                        nextPollTimeoutMillis = -1;
+                        break;
+                    }
+
                     final long when = msg.when;
-                    if (now >= when) {
+                    if (now < when) {
+                        // Next message is not ready.  Set a timeout to wake up when it is ready.
+                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
+                        break;
+                    }
+
+                    if (mBarrierNestCount == 0 || msg.isAsynchronous()) {
+                        // Got a message.
                         mBlocked = false;
-                        mMessages = msg.next;
+                        if (prevMsg != null) {
+                            prevMsg.next = msg.next;
+                        } else {
+                            mMessages = msg.next;
+                        }
                         msg.next = null;
                         if (false) Log.v("MessageQueue", "Returning message: " + msg);
                         msg.markInUse();
                         return msg;
-                    } else {
-                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                     }
-                } else {
-                    nextPollTimeoutMillis = -1;
+
+                    // We have a message that we could return except that it is
+                    // blocked by the sync barrier.  In particular, this means that
+                    // we are not idle yet, so we do not want to run the idle handlers.
+                    prevMsg = msg;
+                    msg = msg.next;
                 }
 
-                // If first time, then get the number of idlers to run.
-                if (pendingIdleHandlerCount < 0) {
+                // If first time idle, then get the number of idlers to run.
+                if (pendingIdleHandlerCount < 0 && msg == mMessages) {
                     pendingIdleHandlerCount = mIdleHandlers.size();
                 }
-                if (pendingIdleHandlerCount == 0) {
+                if (pendingIdleHandlerCount <= 0) {
                     // No idle handlers to run.  Loop and wait some more.
                     mBlocked = true;
                     continue;
@@ -205,10 +275,15 @@
             //Log.d("MessageQueue", "Enqueing: " + msg);
             Message p = mMessages;
             if (p == null || when == 0 || when < p.when) {
+                // New head, wake up the event queue if blocked.
                 msg.next = p;
                 mMessages = msg;
-                needWake = mBlocked; // new head, might need to wake up
+                needWake = mBlocked;
             } else {
+                // Inserted within the middle of the queue.  Usually we don't have to wake
+                // up the event queue unless the message is asynchronous and it might be
+                // possible for it to be returned out of sequence relative to an earlier
+                // synchronous message at the head of the queue.
                 Message prev = null;
                 while (p != null && p.when <= when) {
                     prev = p;
@@ -216,7 +291,8 @@
                 }
                 msg.next = prev.next;
                 prev.next = msg;
-                needWake = false; // still waiting on head, no need to wake up
+                needWake = mBlocked && mBarrierNestCount != 0 && msg.isAsynchronous()
+                        && !mMessages.isAsynchronous();
             }
         }
         if (needWake) {
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index e5c1e5b..ae41eab 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -17,13 +17,10 @@
 package android.text;
 
 
-import java.util.Locale;
-
 import android.util.LocaleUtil;
 
 /**
  * Some objects that implement TextDirectionHeuristic.
- * @hide
  */
 public class TextDirectionHeuristics {
 
@@ -74,9 +71,8 @@
      * Computes the text direction based on an algorithm.  Subclasses implement
      * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
      * direction from the text alone.
-     * @hide
      */
-    public static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
+    private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
         private final TextDirectionAlgorithm mAlgorithm;
 
         public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
@@ -157,13 +153,11 @@
     /**
      * Interface for an algorithm to guess the direction of a paragraph of text.
      *
-     * @hide
      */
-    public static interface TextDirectionAlgorithm {
+    private static interface TextDirectionAlgorithm {
         /**
          * Returns whether the range of text is RTL according to the algorithm.
          *
-         * @hide
          */
         TriState checkRtl(char[] text, int start, int count);
     }
@@ -173,9 +167,8 @@
      * the paragraph direction.  This is the standard Unicode Bidirectional
      * algorithm.
      *
-     * @hide
      */
-    public static class FirstStrong implements TextDirectionAlgorithm {
+    private static class FirstStrong implements TextDirectionAlgorithm {
         @Override
         public TriState checkRtl(char[] text, int start, int count) {
             TriState result = TriState.UNKNOWN;
@@ -196,9 +189,8 @@
      * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
      * direction of text.
      *
-     * @hide
      */
-    public static class AnyStrong implements TextDirectionAlgorithm {
+    private static class AnyStrong implements TextDirectionAlgorithm {
         private final boolean mLookForRtl;
 
         @Override
@@ -239,7 +231,7 @@
     /**
      * Algorithm that uses the Locale direction to force the direction of a paragraph.
      */
-    public static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
+    private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
 
         public TextDirectionHeuristicLocale() {
             super(null);
diff --git a/core/java/android/text/method/KeyListener.java b/core/java/android/text/method/KeyListener.java
index 8594852..318149a 100644
--- a/core/java/android/text/method/KeyListener.java
+++ b/core/java/android/text/method/KeyListener.java
@@ -22,7 +22,7 @@
 
 /**
  * Interface for converting text key events into edit operations on an
- * Editable class.  Note that for must cases this interface has been
+ * Editable class.  Note that for most cases this interface has been
  * superceded by general soft input methods as defined by
  * {@link android.view.inputmethod.InputMethod}; it should only be used
  * for cases where an application has its own on-screen keypad and also wants
diff --git a/core/java/android/text/method/TransformationMethod.java b/core/java/android/text/method/TransformationMethod.java
index 9f51c2a..b542109 100644
--- a/core/java/android/text/method/TransformationMethod.java
+++ b/core/java/android/text/method/TransformationMethod.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Rect;
 import android.view.View;
-import android.widget.TextView;
 
 /**
  * TextView uses TransformationMethods to do things like replacing the
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 3e2d7fc..658da2f 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -37,7 +37,7 @@
  *
  * @hide
  */
-public final class Choreographer extends Handler {
+public final class Choreographer {
     private static final String TAG = "Choreographer";
     private static final boolean DEBUG = false;
 
@@ -92,10 +92,16 @@
     private final Object mLock = new Object();
 
     private final Looper mLooper;
+    private final FrameHandler mHandler;
+
+    private Callback mCallbackPool;
 
     private OnAnimateListener[] mOnAnimateListeners;
     private OnDrawListener[] mOnDrawListeners;
 
+    private Callback mOnAnimateCallbacks;
+    private Callback mOnDrawCallbacks;
+
     private boolean mAnimationScheduled;
     private boolean mDrawScheduled;
     private boolean mFrameDisplayEventReceiverNeeded;
@@ -104,8 +110,8 @@
     private long mLastDrawTime;
 
     private Choreographer(Looper looper) {
-        super(looper);
         mLooper = looper;
+        mHandler = new FrameHandler(looper);
         mLastAnimationTime = Long.MIN_VALUE;
         mLastDrawTime = Long.MIN_VALUE;
     }
@@ -158,12 +164,13 @@
      */
     public void scheduleAnimation() {
         synchronized (mLock) {
-            scheduleAnimationLocked();
+            scheduleAnimationLocked(false);
         }
     }
 
-    private void scheduleAnimationLocked() {
-        if (!mAnimationScheduled) {
+    private void scheduleAnimationLocked(boolean force) {
+        if (!mAnimationScheduled
+                && (force || mOnAnimateListeners != null || mOnAnimateCallbacks != null)) {
             mAnimationScheduled = true;
             if (USE_VSYNC) {
                 if (DEBUG) {
@@ -176,13 +183,14 @@
                 if (!mFrameDisplayEventReceiverNeeded) {
                     mFrameDisplayEventReceiverNeeded = true;
                     if (mFrameDisplayEventReceiver != null) {
-                        removeMessages(MSG_DO_DISPOSE_RECEIVER);
+                        mHandler.removeMessages(MSG_DO_DISPOSE_RECEIVER);
                     }
                 }
                 if (isRunningOnLooperThreadLocked()) {
                     doScheduleVsyncLocked();
                 } else {
-                    sendMessageAtFrontOfQueue(obtainMessage(MSG_DO_SCHEDULE_VSYNC));
+                    mHandler.sendMessageAtFrontOfQueue(
+                            mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC));
                 }
             } else {
                 final long now = SystemClock.uptimeMillis();
@@ -190,7 +198,7 @@
                 if (DEBUG) {
                     Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
                 }
-                sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime);
+                mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime);
             }
         }
     }
@@ -212,16 +220,21 @@
      */
     public void scheduleDraw() {
         synchronized (mLock) {
-            if (!mDrawScheduled) {
-                mDrawScheduled = true;
-                if (USE_ANIMATION_TIMER_FOR_DRAW) {
-                    scheduleAnimationLocked();
-                } else {
-                    if (DEBUG) {
-                        Log.d(TAG, "Scheduling draw immediately.");
-                    }
-                    sendEmptyMessage(MSG_DO_DRAW);
+            scheduleDrawLocked();
+        }
+    }
+
+    private void scheduleDrawLocked() {
+        if (!mDrawScheduled
+                && (mOnDrawListeners != null || mOnDrawCallbacks != null)) {
+            mDrawScheduled = true;
+            if (USE_ANIMATION_TIMER_FOR_DRAW) {
+                scheduleAnimationLocked(true);
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "Scheduling draw immediately.");
                 }
+                mHandler.sendEmptyMessage(MSG_DO_DRAW);
             }
         }
     }
@@ -237,116 +250,6 @@
         }
     }
 
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case MSG_DO_ANIMATION:
-                doAnimation();
-                break;
-            case MSG_DO_DRAW:
-                doDraw();
-                break;
-            case MSG_DO_SCHEDULE_VSYNC:
-                doScheduleVsync();
-                break;
-            case MSG_DO_DISPOSE_RECEIVER:
-                doDisposeReceiver();
-                break;
-        }
-    }
-
-    private void doAnimation() {
-        doAnimationInner();
-
-        if (USE_ANIMATION_TIMER_FOR_DRAW) {
-            doDraw();
-        }
-    }
-
-    private void doAnimationInner() {
-        final long start;
-        final OnAnimateListener[] listeners;
-        synchronized (mLock) {
-            if (!mAnimationScheduled) {
-                return; // no work to do
-            }
-            mAnimationScheduled = false;
-
-            start = SystemClock.uptimeMillis();
-            if (DEBUG) {
-                Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
-                        + " ms have elapsed since previous animation.");
-            }
-            mLastAnimationTime = start;
-
-            listeners = mOnAnimateListeners;
-        }
-
-        if (listeners != null) {
-            for (int i = 0; i < listeners.length; i++) {
-                listeners[i].onAnimate();
-            }
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
-        }
-    }
-
-    private void doDraw() {
-        final long start;
-        final OnDrawListener[] listeners;
-        synchronized (mLock) {
-            if (!mDrawScheduled) {
-                return; // no work to do
-            }
-            mDrawScheduled = false;
-
-            start = SystemClock.uptimeMillis();
-            if (DEBUG) {
-                Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime)
-                        + " ms have elapsed since previous draw.");
-            }
-            mLastDrawTime = start;
-
-            listeners = mOnDrawListeners;
-        }
-
-        if (listeners != null) {
-            for (int i = 0; i < listeners.length; i++) {
-                listeners[i].onDraw();
-            }
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
-        }
-    }
-
-    private void doScheduleVsync() {
-        synchronized (mLock) {
-            doScheduleVsyncLocked();
-        }
-    }
-
-    private void doScheduleVsyncLocked() {
-        if (mFrameDisplayEventReceiverNeeded && mAnimationScheduled) {
-            if (mFrameDisplayEventReceiver == null) {
-                mFrameDisplayEventReceiver = new FrameDisplayEventReceiver(mLooper);
-            }
-            mFrameDisplayEventReceiver.scheduleVsync();
-        }
-    }
-
-    private void doDisposeReceiver() {
-        synchronized (mLock) {
-            if (!mFrameDisplayEventReceiverNeeded && mFrameDisplayEventReceiver != null) {
-                mFrameDisplayEventReceiver.dispose();
-                mFrameDisplayEventReceiver = null;
-            }
-        }
-    }
-
     /**
      * Adds an animation listener.
      *
@@ -384,7 +287,7 @@
         synchronized (mLock) {
             mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
                     mOnAnimateListeners, listener);
-            stopTimingLoopIfNoListenersLocked();
+            stopTimingLoopIfNoListenersOrCallbacksLocked();
         }
     }
 
@@ -426,12 +329,200 @@
         synchronized (mLock) {
             mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
                     mOnDrawListeners, listener);
-            stopTimingLoopIfNoListenersLocked();
+            stopTimingLoopIfNoListenersOrCallbacksLocked();
         }
     }
 
-    private void stopTimingLoopIfNoListenersLocked() {
-        if (mOnDrawListeners == null && mOnAnimateListeners == null) {
+
+    /**
+     * Posts a callback to run on the next animation cycle and schedules an animation cycle.
+     * The callback only runs once and then is automatically removed.
+     *
+     * @param runnable The callback to run during the next animation cycle.
+     *
+     * @see #removeOnAnimateCallback
+     */
+    public void postOnAnimateCallback(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
+        synchronized (mLock) {
+            mOnAnimateCallbacks = addCallbackLocked(mOnAnimateCallbacks, runnable);
+            scheduleAnimationLocked(false);
+        }
+    }
+
+    /**
+     * Removes an animation callback.
+     * Does nothing if the specified animation callback has not been posted or has already
+     * been removed.
+     *
+     * @param runnable The animation callback to remove.
+     *
+     * @see #postOnAnimateCallback
+     */
+    public void removeOnAnimateCallback(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
+        synchronized (mLock) {
+            mOnAnimateCallbacks = removeCallbackLocked(mOnAnimateCallbacks, runnable);
+            stopTimingLoopIfNoListenersOrCallbacksLocked();
+        }
+    }
+
+    /**
+     * Posts a callback to run on the next draw cycle and schedules a draw cycle.
+     * The callback only runs once and then is automatically removed.
+     *
+     * @param runnable The callback to run during the next draw cycle.
+     *
+     * @see #removeOnDrawCallback
+     */
+    public void postOnDrawCallback(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
+        synchronized (mLock) {
+            mOnDrawCallbacks = addCallbackLocked(mOnDrawCallbacks, runnable);
+            scheduleDrawLocked();
+        }
+    }
+
+    /**
+     * Removes a draw callback.
+     * Does nothing if the specified draw callback has not been posted or has already
+     * been removed.
+     *
+     * @param runnable The draw callback to remove.
+     *
+     * @see #postOnDrawCallback
+     */
+    public void removeOnDrawCallback(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
+        synchronized (mLock) {
+            mOnDrawCallbacks = removeCallbackLocked(mOnDrawCallbacks, runnable);
+            stopTimingLoopIfNoListenersOrCallbacksLocked();
+        }
+    }
+
+    void doAnimation() {
+        doAnimationInner();
+
+        if (USE_ANIMATION_TIMER_FOR_DRAW) {
+            doDraw();
+        }
+    }
+
+    void doAnimationInner() {
+        final long start;
+        final OnAnimateListener[] listeners;
+        final Callback callbacks;
+        synchronized (mLock) {
+            if (!mAnimationScheduled) {
+                return; // no work to do
+            }
+            mAnimationScheduled = false;
+
+            start = SystemClock.uptimeMillis();
+            if (DEBUG) {
+                Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
+                        + " ms have elapsed since previous animation.");
+            }
+            mLastAnimationTime = start;
+
+            listeners = mOnAnimateListeners;
+            callbacks = mOnAnimateCallbacks;
+            mOnAnimateCallbacks = null;
+        }
+
+        if (listeners != null) {
+            for (int i = 0; i < listeners.length; i++) {
+                listeners[i].onAnimate();
+            }
+        }
+
+        if (callbacks != null) {
+            runCallbacks(callbacks);
+            synchronized (mLock) {
+                recycleCallbacksLocked(callbacks);
+            }
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
+        }
+    }
+
+    void doDraw() {
+        final long start;
+        final OnDrawListener[] listeners;
+        final Callback callbacks;
+        synchronized (mLock) {
+            if (!mDrawScheduled) {
+                return; // no work to do
+            }
+            mDrawScheduled = false;
+
+            start = SystemClock.uptimeMillis();
+            if (DEBUG) {
+                Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime)
+                        + " ms have elapsed since previous draw.");
+            }
+            mLastDrawTime = start;
+
+            listeners = mOnDrawListeners;
+            callbacks = mOnDrawCallbacks;
+            mOnDrawCallbacks = null;
+        }
+
+        if (listeners != null) {
+            for (int i = 0; i < listeners.length; i++) {
+                listeners[i].onDraw();
+            }
+        }
+
+        if (callbacks != null) {
+            runCallbacks(callbacks);
+            synchronized (mLock) {
+                recycleCallbacksLocked(callbacks);
+            }
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
+        }
+    }
+
+    void doScheduleVsync() {
+        synchronized (mLock) {
+            doScheduleVsyncLocked();
+        }
+    }
+
+    private void doScheduleVsyncLocked() {
+        if (mFrameDisplayEventReceiverNeeded && mAnimationScheduled) {
+            if (mFrameDisplayEventReceiver == null) {
+                mFrameDisplayEventReceiver = new FrameDisplayEventReceiver(mLooper);
+            }
+            mFrameDisplayEventReceiver.scheduleVsync();
+        }
+    }
+
+    void doDisposeReceiver() {
+        synchronized (mLock) {
+            if (!mFrameDisplayEventReceiverNeeded && mFrameDisplayEventReceiver != null) {
+                mFrameDisplayEventReceiver.dispose();
+                mFrameDisplayEventReceiver = null;
+            }
+        }
+    }
+
+    private void stopTimingLoopIfNoListenersOrCallbacksLocked() {
+        if (mOnAnimateListeners == null && mOnDrawListeners == null
+                && mOnAnimateCallbacks == null && mOnDrawCallbacks == null) {
             if (DEBUG) {
                 Log.d(TAG, "Stopping timing loop.");
             }
@@ -439,16 +530,16 @@
             if (mAnimationScheduled) {
                 mAnimationScheduled = false;
                 if (USE_VSYNC) {
-                    removeMessages(MSG_DO_SCHEDULE_VSYNC);
+                    mHandler.removeMessages(MSG_DO_SCHEDULE_VSYNC);
                 } else {
-                    removeMessages(MSG_DO_ANIMATION);
+                    mHandler.removeMessages(MSG_DO_ANIMATION);
                 }
             }
 
             if (mDrawScheduled) {
                 mDrawScheduled = false;
                 if (!USE_ANIMATION_TIMER_FOR_DRAW) {
-                    removeMessages(MSG_DO_DRAW);
+                    mHandler.removeMessages(MSG_DO_DRAW);
                 }
             }
 
@@ -460,7 +551,8 @@
             if (mFrameDisplayEventReceiverNeeded) {
                 mFrameDisplayEventReceiverNeeded = false;
                 if (mFrameDisplayEventReceiver != null) {
-                    sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER, DISPOSE_RECEIVER_DELAY);
+                    mHandler.sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER,
+                            DISPOSE_RECEIVER_DELAY);
                 }
             }
         }
@@ -470,6 +562,71 @@
         return Looper.myLooper() == mLooper;
     }
 
+    private Callback addCallbackLocked(Callback head, Runnable runnable) {
+        Callback callback = obtainCallbackLocked(runnable);
+        if (head == null) {
+            return callback;
+        }
+        Callback tail = head;
+        while (tail.next != null) {
+            tail = tail.next;
+        }
+        tail.next = callback;
+        return head;
+    }
+
+    private Callback removeCallbackLocked(Callback head, Runnable runnable) {
+        Callback predecessor = null;
+        for (Callback callback = head; callback != null;) {
+            final Callback next = callback.next;
+            if (callback.runnable == runnable) {
+                if (predecessor != null) {
+                    predecessor.next = next;
+                } else {
+                    head = next;
+                }
+                recycleCallbackLocked(callback);
+            } else {
+                predecessor = callback;
+            }
+            callback = next;
+        }
+        return head;
+    }
+
+    private void runCallbacks(Callback head) {
+        while (head != null) {
+            head.runnable.run();
+            head = head.next;
+        }
+    }
+
+    private void recycleCallbacksLocked(Callback head) {
+        while (head != null) {
+            final Callback next = head.next;
+            recycleCallbackLocked(head);
+            head = next;
+        }
+    }
+
+    private Callback obtainCallbackLocked(Runnable runnable) {
+        Callback callback = mCallbackPool;
+        if (callback == null) {
+            callback = new Callback();
+        } else {
+            mCallbackPool = callback.next;
+            callback.next = null;
+        }
+        callback.runnable = runnable;
+        return callback;
+    }
+
+    private void recycleCallbackLocked(Callback callback) {
+        callback.runnable = null;
+        callback.next = mCallbackPool;
+        mCallbackPool = callback;
+    }
+
     /**
      * Listens for animation frame timing events.
      */
@@ -490,6 +647,30 @@
         public void onDraw();
     }
 
+    private final class FrameHandler extends Handler {
+        public FrameHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_DO_ANIMATION:
+                    doAnimation();
+                    break;
+                case MSG_DO_DRAW:
+                    doDraw();
+                    break;
+                case MSG_DO_SCHEDULE_VSYNC:
+                    doScheduleVsync();
+                    break;
+                case MSG_DO_DISPOSE_RECEIVER:
+                    doDisposeReceiver();
+                    break;
+            }
+        }
+    }
+
     private final class FrameDisplayEventReceiver extends DisplayEventReceiver {
         public FrameDisplayEventReceiver(Looper looper) {
             super(looper);
@@ -500,4 +681,9 @@
             doAnimation();
         }
     }
+
+    private static final class Callback {
+        public Callback next;
+        public Runnable runnable;
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5042258..38f055f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2618,7 +2618,6 @@
 
     /**
      * Text direction is inherited thru {@link ViewGroup}
-     * @hide
      */
     public static final int TEXT_DIRECTION_INHERIT = 0;
 
@@ -2627,7 +2626,6 @@
      * determines the paragraph direction. If there is no strong directional character, the
      * paragraph direction is the view's resolved layout direction.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_FIRST_STRONG = 1;
 
@@ -2636,42 +2634,36 @@
      * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters.
      * If there are neither, the paragraph direction is the view's resolved layout direction.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_ANY_RTL = 2;
 
     /**
      * Text direction is forced to LTR.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_LTR = 3;
 
     /**
      * Text direction is forced to RTL.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_RTL = 4;
 
     /**
      * Text direction is coming from the system Locale.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_LOCALE = 5;
 
     /**
      * Default text direction is inherited
      *
-     * @hide
      */
     protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
 
     /**
      * The text direction that has been defined by {@link #setTextDirection(int)}.
      *
-     * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -2689,7 +2681,6 @@
      * not TEXT_DIRECTION_INHERIT, otherwise resolution proceeds up the parent
      * chain of the view.
      *
-     * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -3775,14 +3766,6 @@
         }
 
         if ((mPrivateFlags & FOCUSED) != 0) {
-            // If this is the first focusable do not clear focus since the we
-            // try to give it focus every time a view clears its focus. Hence,
-            // the view that would gain focus already has it.
-            View firstFocusable = getFirstFocusable();
-            if (firstFocusable == this) {
-                return;
-            }
-
             mPrivateFlags &= ~FOCUSED;
 
             if (mParent != null) {
@@ -3791,24 +3774,9 @@
 
             onFocusChanged(false, 0, null);
             refreshDrawableState();
-
-            // The view cleared focus and invoked the callbacks, so  now is the
-            // time to give focus to the the first focusable to ensure that the
-            // gain focus is announced after clear focus.
-            if (firstFocusable != null) {
-                firstFocusable.requestFocus(FOCUS_FORWARD);
-            }
         }
     }
 
-    private View getFirstFocusable() {
-        ViewRootImpl viewRoot = getViewRootImpl();
-        if (viewRoot != null) {
-            return viewRoot.focusSearch(null, FOCUS_FORWARD);
-        }
-        return null;
-    }
-
     /**
      * Called to clear the focus of a view that is about to be removed.
      * Doesn't call clearChildFocus, which prevents this view from taking
@@ -3820,6 +3788,11 @@
 
             onFocusChanged(false, 0, null);
             refreshDrawableState();
+
+            // The view cleared focus and invoked the callbacks, so  now is the
+            // time to give focus to the the first focusable from the top to
+            // ensure that the gain focus is announced after clear focus.
+            getRootView().requestFocus(FOCUS_FORWARD);
         }
     }
 
@@ -10951,20 +10924,70 @@
     }
 
     /**
+     * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common
+     * case of an active Animation being run on the view.
+     */
+    private boolean drawAnimation(ViewGroup parent, long drawingTime,
+            Animation a, boolean scalingRequired) {
+        Transformation invalidationTransform;
+        final int flags = parent.mGroupFlags;
+        final boolean initialized = a.isInitialized();
+        if (!initialized) {
+            a.initialize(mRight - mLeft, mBottom - mTop, getWidth(), getHeight());
+            a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
+            onAnimationStart();
+        }
+
+        boolean more = a.getTransformation(drawingTime, parent.mChildTransformation, 1f);
+        if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
+            if (parent.mInvalidationTransformation == null) {
+                parent.mInvalidationTransformation = new Transformation();
+            }
+            invalidationTransform = parent.mInvalidationTransformation;
+            a.getTransformation(drawingTime, invalidationTransform, 1f);
+        } else {
+            invalidationTransform = parent.mChildTransformation;
+        }
+        if (more) {
+            if (!a.willChangeBounds()) {
+                if ((flags & (parent.FLAG_OPTIMIZE_INVALIDATE | parent.FLAG_ANIMATION_DONE)) ==
+                        parent.FLAG_OPTIMIZE_INVALIDATE) {
+                    parent.mGroupFlags |= parent.FLAG_INVALIDATE_REQUIRED;
+                } else if ((flags & parent.FLAG_INVALIDATE_REQUIRED) == 0) {
+                    // The child need to draw an animation, potentially offscreen, so
+                    // make sure we do not cancel invalidate requests
+                    parent.mPrivateFlags |= DRAW_ANIMATION;
+                    parent.invalidate(mLeft, mTop, mRight, mBottom);
+                }
+            } else {
+                if (parent.mInvalidateRegion == null) {
+                    parent.mInvalidateRegion = new RectF();
+                }
+                final RectF region = parent.mInvalidateRegion;
+                a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
+                        invalidationTransform);
+
+                // The child need to draw an animation, potentially offscreen, so
+                // make sure we do not cancel invalidate requests
+                parent.mPrivateFlags |= DRAW_ANIMATION;
+
+                final int left = mLeft + (int) region.left;
+                final int top = mTop + (int) region.top;
+                parent.invalidate(left, top, left + (int) (region.width() + .5f),
+                        top + (int) (region.height() + .5f));
+            }
+        }
+        return more;
+    }
+
+    /**
      * This method is called by ViewGroup.drawChild() to have each child view draw itself.
      * This draw() method is an implementation detail and is not intended to be overridden or
      * to be called from anywhere else other than ViewGroup.drawChild().
      */
     boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
         boolean more = false;
-
-        final int cl = mLeft;
-        final int ct = mTop;
-        final int cr = mRight;
-        final int cb = mBottom;
-
         final boolean childHasIdentityMatrix = hasIdentityMatrix();
-
         final int flags = parent.mGroupFlags;
 
         if ((flags & parent.FLAG_CLEAR_TRANSFORMATION) == parent.FLAG_CLEAR_TRANSFORMATION) {
@@ -10973,8 +10996,6 @@
         }
 
         Transformation transformToApply = null;
-        Transformation invalidationTransform;
-        final Animation a = getAnimation();
         boolean concatMatrix = false;
 
         boolean scalingRequired = false;
@@ -10990,59 +11011,15 @@
             caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
         }
 
+        final Animation a = getAnimation();
         if (a != null) {
-            final boolean initialized = a.isInitialized();
-            if (!initialized) {
-                a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
-                a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
-                onAnimationStart();
-            }
-
-            more = a.getTransformation(drawingTime, parent.mChildTransformation, 1f);
-            if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
-                if (parent.mInvalidationTransformation == null) {
-                    parent.mInvalidationTransformation = new Transformation();
-                }
-                invalidationTransform = parent.mInvalidationTransformation;
-                a.getTransformation(drawingTime, invalidationTransform, 1f);
-            } else {
-                invalidationTransform = parent.mChildTransformation;
-            }
-            transformToApply = parent.mChildTransformation;
-
+            more = drawAnimation(parent, drawingTime, a, scalingRequired);
             concatMatrix = a.willChangeTransformationMatrix();
-
-            if (more) {
-                if (!a.willChangeBounds()) {
-                    if ((flags & (parent.FLAG_OPTIMIZE_INVALIDATE | parent.FLAG_ANIMATION_DONE)) ==
-                            parent.FLAG_OPTIMIZE_INVALIDATE) {
-                        parent.mGroupFlags |= parent.FLAG_INVALIDATE_REQUIRED;
-                    } else if ((flags & parent.FLAG_INVALIDATE_REQUIRED) == 0) {
-                        // The child need to draw an animation, potentially offscreen, so
-                        // make sure we do not cancel invalidate requests
-                        parent.mPrivateFlags |= DRAW_ANIMATION;
-                        invalidate(cl, ct, cr, cb);
-                    }
-                } else {
-                    if (parent.mInvalidateRegion == null) {
-                        parent.mInvalidateRegion = new RectF();
-                    }
-                    final RectF region = parent.mInvalidateRegion;
-                    a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
-
-                    // The child need to draw an animation, potentially offscreen, so
-                    // make sure we do not cancel invalidate requests
-                    parent.mPrivateFlags |= DRAW_ANIMATION;
-
-                    final int left = cl + (int) region.left;
-                    final int top = ct + (int) region.top;
-                    invalidate(left, top, left + (int) (region.width() + .5f),
-                            top + (int) (region.height() + .5f));
-                }
-            }
+            transformToApply = parent.mChildTransformation;
         } else if ((flags & parent.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
                 parent.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
-            final boolean hasTransform = parent.getChildStaticTransformation(this, parent.mChildTransformation);
+            final boolean hasTransform =
+                    parent.getChildStaticTransformation(this, parent.mChildTransformation);
             if (hasTransform) {
                 final int transformType = parent.mChildTransformation.getTransformationType();
                 transformToApply = transformType != Transformation.TYPE_IDENTITY ?
@@ -11057,7 +11034,7 @@
         // to call invalidate() successfully when doing animations
         mPrivateFlags |= DRAWN;
 
-        if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
+        if (!concatMatrix && canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
                 (mPrivateFlags & DRAW_ANIMATION) == 0) {
             return more;
         }
@@ -11105,9 +11082,9 @@
 
         final int restoreTo = canvas.save();
         if (offsetForScroll) {
-            canvas.translate(cl - sx, ct - sy);
+            canvas.translate(mLeft - sx, mTop - sy);
         } else {
-            canvas.translate(cl, ct);
+            canvas.translate(mLeft, mTop);
             if (scalingRequired) {
                 // mAttachInfo cannot be null, otherwise scalingRequired == false
                 final float scale = 1.0f / mAttachInfo.mApplicationScale;
@@ -11163,8 +11140,8 @@
                         if (layerType == LAYER_TYPE_NONE) {
                             final int scrollX = hasDisplayList ? 0 : sx;
                             final int scrollY = hasDisplayList ? 0 : sy;
-                            canvas.saveLayerAlpha(scrollX, scrollY, scrollX + cr - cl,
-                                    scrollY + cb - ct, multipliedAlpha, layerFlags);
+                            canvas.saveLayerAlpha(scrollX, scrollY, scrollX + mRight - mLeft,
+                                    scrollY + mBottom - mTop, multipliedAlpha, layerFlags);
                         }
                     } else {
                         // Alpha is handled by the child directly, clobber the layer's alpha
@@ -11179,10 +11156,10 @@
 
         if ((flags & parent.FLAG_CLIP_CHILDREN) == parent.FLAG_CLIP_CHILDREN) {
             if (offsetForScroll) {
-                canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
+                canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop));
             } else {
                 if (!scalingRequired || cache == null) {
-                    canvas.clipRect(0, 0, cr - cl, cb - ct);
+                    canvas.clipRect(0, 0, mRight - mLeft, mBottom - mTop);
                 } else {
                     canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
                 }
@@ -11212,7 +11189,7 @@
                     final int scrollX = hasDisplayList ? 0 : sx;
                     final int scrollY = hasDisplayList ? 0 : sy;
                     canvas.saveLayer(scrollX, scrollY,
-                            scrollX + cr - cl, scrollY + cb - ct, mLayerPaint,
+                            scrollX + mRight - mLeft, scrollY + mBottom - mTop, mLayerPaint,
                             Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
                 }
             }
@@ -11231,7 +11208,8 @@
                     }
                 } else {
                     mPrivateFlags &= ~DIRTY_MASK;
-                    ((HardwareCanvas) canvas).drawDisplayList(displayList, cr - cl, cb - ct, null);
+                    ((HardwareCanvas) canvas).drawDisplayList(displayList,
+                            mRight - mLeft, mBottom - mTop, null);
                 }
             }
         } else if (cache != null) {
@@ -11248,7 +11226,8 @@
                 if (alpha < 1.0f) {
                     cachePaint.setAlpha((int) (alpha * 255));
                     parent.mGroupFlags |= parent.FLAG_ALPHA_LOWER_THAN_ONE;
-                } else if  ((flags & parent.FLAG_ALPHA_LOWER_THAN_ONE) == parent.FLAG_ALPHA_LOWER_THAN_ONE) {
+                } else if  ((flags & parent.FLAG_ALPHA_LOWER_THAN_ONE) ==
+                        parent.FLAG_ALPHA_LOWER_THAN_ONE) {
                     cachePaint.setAlpha(255);
                     parent.mGroupFlags &= ~parent.FLAG_ALPHA_LOWER_THAN_ONE;
                 }
@@ -14100,7 +14079,6 @@
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
      *
-     * @hide
      */
     public int getTextDirection() {
         return mTextDirection;
@@ -14118,7 +14096,6 @@
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
      *
-     * @hide
      */
     public void setTextDirection(int textDirection) {
         if (textDirection != mTextDirection) {
@@ -14139,7 +14116,6 @@
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
      *
-     * @hide
      */
     public int getResolvedTextDirection() {
         if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) {
@@ -14151,7 +14127,6 @@
     /**
      * Resolve the text direction.
      *
-     * @hide
      */
     protected void resolveTextDirection() {
         if (mTextDirection != TEXT_DIRECTION_INHERIT) {
@@ -14168,7 +14143,6 @@
     /**
      * Reset resolved text direction. Will be resolved during a call to getResolvedTextDirection().
      *
-     * @hide
      */
     protected void resetResolvedTextDirection() {
         mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b27eef0..a2e85a3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2620,7 +2620,7 @@
      *
      * @param canvas The canvas on which to draw the child
      * @param child Who to draw
-     * @param drawingTime The time at which draw is occuring
+     * @param drawingTime The time at which draw is occurring
      * @return True if an invalidate() was issued
      */
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index fbafc64..332a0eb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -4803,7 +4803,9 @@
         if (layer == 0 || isPictureAfterFirstLayout) {
             mWebViewCore.resumeWebKitDraw();
         } else if (queueFull) {
-            mWebViewCore.pauseWebKitDraw();
+            // temporarily disable webkit draw throttling
+            // TODO: re-enable
+            // mWebViewCore.pauseWebKitDraw();
         }
 
         if (mHTML5VideoViewProxy != null) {
@@ -6111,7 +6113,7 @@
         calcOurContentVisibleRectF(mVisibleContentRect);
         nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
                 mGLViewportEmpty ? null : mViewRectViewport,
-                mVisibleContentRect);
+                mVisibleContentRect, getScale());
     }
 
     /**
@@ -9885,7 +9887,7 @@
     private native int      nativeGetDrawGLFunction(int nativeInstance, Rect rect,
             Rect viewRect, RectF visibleRect, float scale, int extras);
     private native void     nativeUpdateDrawGLFunction(Rect rect, Rect viewRect,
-            RectF visibleRect);
+            RectF visibleRect, float scale);
     private native void     nativeExtendSelection(int x, int y);
     private native int      nativeFindAll(String findLower, String findUpper,
             boolean sameAsLastSearch);
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 570f0f9..909f383 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -426,7 +426,8 @@
                     }
 
                     // A new word has been created across the interval boundaries with this edit.
-                    // Previous spans (ended on start / started on end) removed, not valid anymore
+                    // The previous spans (that ended on start / started on end) are not valid
+                    // anymore and must be removed.
                     if (wordStart < start && wordEnd > start) {
                         removeSpansAt(editable, start, spellCheckSpans);
                         removeSpansAt(editable, start, suggestionSpans);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 99349b0..7db8a1e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3472,6 +3472,9 @@
         if (mText.length() == 0) {
             invalidate();
         }
+
+        // Invalidate display list if hint will be used
+        if (mText.length() == 0 && mHint != null) mTextDisplayListIsValid = false;
     }
 
     /**
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 547607e..68a8de8 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -220,7 +220,7 @@
         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
         goto native_init_failure;
     }
-    // read the audio session ID back from AudioTrack in case a new session was created during set()
+    // read the audio session ID back from AudioRecord in case a new session was created during set()
     nSession[0] = lpRecorder->getSessionId();
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 31c5ed4..ee5eb7e 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1,4 +1,4 @@
-/* //device/libs/android_runtime/android_media_AudioSystem.cpp
+/*
 **
 ** Copyright 2006, The Android Open Source Project
 **
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 0a337fae..544b4c0 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -1,4 +1,4 @@
-/* //device/libs/android_runtime/android_media_AudioSystem.cpp
+/*
  **
  ** Copyright 2008, The Android Open Source Project
  **
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 2ac3ca8..c5ff16e 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -116,14 +116,9 @@
     return (jboolean)(::wifi_unload_driver() == 0);
 }
 
-static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject)
+static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject, jboolean p2pSupported)
 {
-    return (jboolean)(::wifi_start_supplicant() == 0);
-}
-
-static jboolean android_net_wifi_startP2pSupplicant(JNIEnv* env, jobject)
-{
-    return (jboolean)(::wifi_start_p2p_supplicant() == 0);
+    return (jboolean)(::wifi_start_supplicant(p2pSupported) == 0);
 }
 
 static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
@@ -207,8 +202,7 @@
     { "loadDriver", "()Z",  (void *)android_net_wifi_loadDriver },
     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded },
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
-    { "startSupplicant", "()Z",  (void *)android_net_wifi_startSupplicant },
-    { "startP2pSupplicant", "()Z",  (void *)android_net_wifi_startP2pSupplicant },
+    { "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },
     { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
     { "connectToSupplicant", "(Ljava/lang/String;)Z",
             (void *)android_net_wifi_connectToSupplicant },
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 5377425..6d783de 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -199,9 +199,9 @@
         bool isInitialized = parcel->readInt32();
         if (isInitialized) {
             String8 name = parcel->readString8();
-            int32_t rawFd = parcel->readFileDescriptor();
-            int32_t dupFd = dup(rawFd);
-            if (rawFd < 0) {
+            int rawFd = parcel->readFileDescriptor();
+            int dupFd = dup(rawFd);
+            if (dupFd < 0) {
                 ALOGE("Error %d dup channel fd %d.", errno, rawFd);
                 jniThrowRuntimeException(env,
                         "Could not read input channel file descriptors from parcel.");
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 4b737ede..47b3f66 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -93,7 +93,7 @@
 }
 
 status_t NativeInputEventReceiver::initialize() {
-    int32_t receiveFd = mInputConsumer.getChannel()->getFd();
+    int receiveFd = mInputConsumer.getChannel()->getFd();
     mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     return OK;
 }
diff --git a/core/res/res/anim/recents_fade_in.xml b/core/res/res/anim/recents_fade_in.xml
new file mode 100644
index 0000000..c516fadb
--- /dev/null
+++ b/core/res/res/anim/recents_fade_in.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/recents_fade_in.xml
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@interpolator/decelerate_quad"
+        android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="150" />
diff --git a/core/res/res/anim/recents_fade_out.xml b/core/res/res/anim/recents_fade_out.xml
new file mode 100644
index 0000000..7468cc19
--- /dev/null
+++ b/core/res/res/anim/recents_fade_out.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/recents_fade_out.xml
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/accelerate_quad"
+    android:fromAlpha="1.0"
+    android:toAlpha="0.0"
+    android:duration="150" />
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 23ae6c2..0a22133 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Разрешава на приложението да прехвърля задачи на преден и на заден план. Злонамерените приложения могат сами да се изведат на преден план без ваша намеса."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"спиране на изпълняваните приложения"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Разрешава на приложението да премахва задачи и да прекратява приложенията им. Злонамерените приложения могат да нарушат поведението на други приложения."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"задаване на съвместимост на екрана"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Разрешава на приложението да контролира режима на съвместимост на екрана на други приложения. Злонамерените програми могат да нарушат поведението на други приложения."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"активиране на отстраняването на грешки в приложения"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Разрешава на приложението да включва отстраняването на грешки за друго приложение. Злонамерените приложения могат да използват това, за да прекратят други приложения."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"промяна на настройките ви за потребителския интерфейс"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index c801fb4..545d957 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Umožňuje aplikaci přesunout úlohy na popředí nebo pozadí. Škodlivé aplikace mohou vynutit zobrazení na popředí bez vašeho svolení."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"zastavení činnosti aplikací"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Umožňuje aplikaci odstranit úlohy a ukončit jejich aplikace. Škodlivé aplikace mohou narušit chování ostatních aplikací."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nastavit kompatibilitu obrazovky"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Umožňuje aplikaci ovládat režim kompatibility obrazovky v ostatních aplikacích. Škodlivé aplikace mohou narušit chování ostatních aplikací."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"povolení ladění aplikací"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Umožňuje aplikaci zapnout ladění jiné aplikace. Škodlivé aplikace mohou toto oprávnění použít k ukončení ostatních aplikací."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"změna vašeho nastavení uživatelského rozhraní"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index ea4af66..cadf935 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリに許可します。この許可を悪意のあるアプリに利用されると、悪意のあるアプリが強制的に優先される恐れがあります。"</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"実行中のアプリの停止"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"タスクの削除とアプリの終了をアプリに許可します。この許可を悪意のあるアプリケーションに利用されると、他のアプリの動作が妨害される恐れがあります。"</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"画面互換性の設定"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"他のアプリケーションの画面互換性モードをコントロールすることをアプリに許可します。この許可を悪意のあるアプリケーションに利用されると、他のアプリケーションの動作が中断される恐れがあります。"</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"アプリのデバッグの有効化"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"別のアプリをデバッグモードにすることをアプリに許可します。この許可を悪意のあるアプリに利用されると、他のアプリが強制終了される恐れがあります。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI設定の変更"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 2c664c9..031e9f5 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"앱이 작업을 포그라운드나 백그라운드로 이동할 수 있도록 허용합니다. 이 경우 악성 앱이 사용자의 조작 없이 앞으로 이동할 수 있습니다."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"실행 중인 앱 중지"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"애플리케이션이 작업을 삭제하거나 다른 애플리케이션을 중지시킬 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션의 동작을 멈추게 할 수 있습니다."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"화면 호환성 설정"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"앱이 다른 애플리케이션의 화면 호환성 모드를 제어할 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션의 동작을 멈추게 할 수 있습니다."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"앱 디버깅 사용"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"애플리케이션이 다른 애플리케이션에 대해 디버깅을 사용할 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션을 중지시킬 수 있습니다."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI 설정 변경"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 659a7ad..6aa9e4c 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Membenarkan apl untuk memindahkan tugasan ke latar depan dan latar belakang. Apl hasad boleh memaksa diri mereka ke hadapan tanpa kawalan anda."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"hentikan apl yang sedang dijalankan"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Membenarkan apl untuk mengalih keluar tugasan dan melupuskan aplnya. Apl hasad boleh mengganggu tingkah laku apl lain."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"tetapkan keserasian skrin"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Membenarkan apl mengawal mod keserasian skrin aplikasi lain. Aplikasi hasad mungkin mematahkan kelakuan aplikasi lain."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"dayakan penyahpepijatan apl"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Membenarkan apl untuk menghidupkan penyahpepijatan untuk apl lain. Apl hasad boleh menggunakannya untuk menghapuskan apl lain."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"tukar tetapan UI anda"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4915a233..5c16708 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Permite que o aplicativo mova tarefas para o primeiro plano e para o plano de fundo. Aplicativos maliciosos podem forçar-se para a primeiro plano sem que você tenha controle sobre a ação."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"parar os aplicativos em execução"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que um aplicativo remova tarefas e elimine seus aplicativos. Aplicativos maliciosos podem interferir no comportamento de outros aplicativos."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"definir a compatibilidade de tela"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permite que o aplicativo controle o modo de compatibilidade de tela de outros aplicativos. Aplicativos maliciosos podem interromper o comportamento de outros aplicativos."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"ativar depuração do aplicativo"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Permite que o aplicativo ative a depuração para outro aplicativo. Aplicativos maliciosos podem usar esse recurso para cancelar outros aplicativos."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"alterar as suas configurações de UI"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 69bc4b8..7d1aa78 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Umožňuje aplikácii presúvať úlohy do popredia a pozadia. Škodlivé aplikácie sa môžu pretlačiť do popredia bez vášho vedomia."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"zastaviť spustené aplikácie"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Umožňuje aplikácii odstrániť úlohy a ukončiť ich aplikácie. Škodlivé aplikácie môžu narušiť správanie iných aplikácií."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nastaviť kompatibilitu obrazovky"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Umožňuje aplikácii ovládať režim kompatibility obrazovky v ostatných aplikáciách. Škodlivé aplikácie môžu narušiť správanie ostatných aplikácií."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"povoliť ladenie aplikácií"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Umožňuje aplikácii zapnúť ladenie inej aplikácie. Škodlivé aplikácie môžu pomocou tohto nastavenia ukončiť iné aplikácie."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"zmeny vašich nastavení používateľského rozhrania"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 20d3e15..8d52c8c 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Uygulamaya, görevleri ön plana ve arka plana taşıma izni verir. Kötü amaçlı uygulamalar kendilerini sizin denetiminiz dışında ön plana taşıyabilir."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"çalışan uygulamaları durdur"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Uygulamaya, görevleri kaldırma ve bunlara ait uygulamaları kapatma izni verir. Kötü amaçlı uygulamalar diğer uygulamaların çalışmasını bozabilir."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ekran uyumluluğunu ayarla"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Uygulamaya diğer uygulamaların ekran uyumluluk modunu denetleme izni verir. Kötü amaçlı uygulamalar diğer uygulamaların çalışma şeklini bozabilir."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"uygulama hata ayıklamayı etkinleştir"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Uygulamaya, başka bir uygulama için hata ayıklamayı açma izni verir. Kötü amaçlı uygulamalar diğer uygulamaları kaldırmak için bunu kullanabilir."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"kullanıcı arayüzü ayarlarınızı değiştirin"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 1065183..a9f431c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"允许应用程序将任务移动到前台和后台。恶意应用程序可能会不受您的控制,强行让自己处于前台。"</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"停止正在运行的应用程序"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"允许该应用程序删除任务并终止这些任务的应用程序。恶意应用程序可以籍此影响其他应用程序的行为。"</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"设置屏幕兼容性"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"允许应用程序控制其他应用程序的屏幕兼容模式。恶意应用程序可以籍此影响其他应用程序的行为。"</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"启用应用程序调试"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"允许该应用程序对其他应用程序启用调试。恶意应用程序可以籍此终止其他的应用程序。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"更改用户界面设置"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 659c2de..5aee830 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"允許應用程式將工作移至前景或背景。請注意,惡意應用程式可能利用此功能自行移動至前景。"</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"停止執行中的應用程式"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"允許應用程式移除工作並終止執行工作的應用程式。請注意,惡意應用程式可能利用此功能干擾其他應用程式的行為。"</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"設定螢幕相容性"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"允許應用程式控制其他應用程式的螢幕相容性模式。惡意應用程式可能藉此破壞其他應用程式的正常運作。"</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"啟用應用程式偵錯"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"允許應用程式為其他程式開啟偵錯功能。提醒您,惡意應用程式可能會利用這個功能終止其他應用程式。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"變更介面設定"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 84f4319..36da63d 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Ivumela insiza ukuthi ihambise izenzo ziye ngaphambili kanye nasemumva. Izinsiza ezinobungozi zingaziphoqelela ukuth iziye phambili ngaphandle kokulawula kwakho."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"misa izinsiza ezisebenzayo"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Vumela ukuthi insiza isuse okumele kwenziwe ibulale nezinsiza zakho. Izinsiza eziwubungozi zingaphazamisa ukusebenza kwezinye izinsiza."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"misa ukuhambelana kwesikrini"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Ivumela uhlelo lokusebenza ukulawula imodi yokuhambelana kwesikrini kwezinye izinhlelo zokusebenza. Izinhlelo zokusebenza ezinonya zingase zephule ukuziphatha kwezinye izinhlelo zokusebenza."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"vumela insiza ilungise inkinga"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Ivumela insiza ukuthi ivule uhlelo lokulungisa lwenye insiza. Izinsiza ezinobungozi zingasebenzisa lokhu ukubulala ezinye izinsiza."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"shintsha izilungiselelo zakho ze-UI"</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e84cb5c..4e55b4f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -218,6 +218,7 @@
   <java-symbol type="bool" name="config_allowActionMenuItemTextWithIcon" />
   <java-symbol type="bool" name="config_bluetooth_adapter_quick_switch" />
   <java-symbol type="bool" name="config_bluetooth_sco_off_call" />
+  <java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
   <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
   <java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
   <java-symbol type="bool" name="config_enable_puk_unlock_screen" />
@@ -1083,6 +1084,7 @@
   <java-symbol type="style" name="ActiveWallpaperSettings" />
   <java-symbol type="style" name="Animation.InputMethodFancy" />
   <java-symbol type="style" name="Animation.Wallpaper" />
+  <java-symbol type="style" name="Animation.RecentApplications" />
   <java-symbol type="style" name="Animation.ZoomButtons" />
   <java-symbol type="style" name="PreviewWallpaperSettings" />
   <java-symbol type="style" name="TextAppearance.SlidingTabActive" />
@@ -3494,4 +3496,6 @@
      =============================================================== -->
   <public type="attr" name="isolatedProcess" id="0x010103a7" />
 
+  <public type="attr" name="textDirection"/>
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4995d2c..c95dddd 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2708,11 +2708,6 @@
     <!-- Do not translate. Default access point SSID used for tethering -->
     <string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
 
-    <!-- Wi-Fi p2p dialog title-->
-    <string name="wifi_p2p_dialog_title">Wi-Fi Direct</string>
-    <string name="wifi_p2p_turnon_message">Start Wi-Fi Direct. This will turn off Wi-Fi client/hotspot.</string>
-    <string name="wifi_p2p_failed_message">Couldn\'t start Wi-Fi Direct.</string>
-
     <string name="accept">Accept</string>
     <string name="decline">Decline</string>
     <string name="wifi_p2p_invitation_sent_title">Invitation sent</string>
@@ -2723,9 +2718,6 @@
     <string name="wifi_p2p_enter_pin_message">Type the required PIN: </string>
     <string name="wifi_p2p_show_pin_message">PIN: </string>
 
-    <string name="wifi_p2p_enabled_notification_title">Wi-Fi Direct is on</string>
-    <string name="wifi_p2p_enabled_notification_message">Touch for settings</string>
-
     <!-- Name of the dialog that lets the user choose an accented character to insert -->
     <string name="select_character">Insert character</string>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 571c4ad..610bad8 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -198,8 +198,10 @@
     <!-- A special animation we can use for recent applications,
          for devices that can support it (do alpha transformations). -->
     <style name="Animation.RecentApplications">
-        <item name="windowEnterAnimation">@anim/fade_in</item>
-        <item name="windowExitAnimation">@anim/fade_out</item>
+        <item name="windowEnterAnimation">@anim/recents_fade_in</item>
+        <item name="windowShowAnimation">@anim/recents_fade_in</item>
+        <item name="windowExitAnimation">@anim/recents_fade_out</item>
+        <item name="windowHideAnimation">@anim/recents_fade_out</item>
     </style>
 
     <!-- A special animation value used internally for popup windows. -->
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index b81f774..88f3f34 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -22,7 +22,7 @@
 	$(call all-java-files-under, EnabledTestApp/src)
 
 LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava littlemock
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index baf587e..f2eba23 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -16,6 +16,9 @@
 
 package android.widget.focus;
 
+import static com.google.testing.littlemock.LittleMock.inOrder;
+import static com.google.testing.littlemock.LittleMock.mock;
+
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
@@ -28,9 +31,7 @@
 import android.widget.Button;
 
 import com.android.frameworks.coretests.R;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.google.testing.littlemock.LittleMock.InOrder;
 
 /**
  * {@link RequestFocusTest} is set up to exercise cases where the views that
@@ -105,43 +106,47 @@
      * This tests checks the case in which the first focusable View clears focus.
      * In such a case the framework tries to give the focus to another View starting
      * from the top. Hence, the framework will try to give focus to the view that
-     * wants to clear its focus. From a client perspective, the view does not loose
-     * focus after the call, therefore no callback for focus change should be invoked.
+     * wants to clear its focus.
      *
      * @throws Exception If an error occurs.
      */
     @UiThreadTest
-    public void testOnFocusChangeNotCalledIfFocusDoesNotMove() throws Exception {
+    public void testOnFocusChangeCallbackOrderWhenClearingFocusOfFirstFocusable()
+            throws Exception {
         // Get the first focusable.
-        Button button = mTopLeftButton;
+        Button clearingFocusButton = mTopLeftButton;
+        Button gainingFocusButton = mTopLeftButton;
 
-        // Make sure that the button is the first focusable and focus it.
-        button.getRootView().requestFocus(View.FOCUS_DOWN);
-        assertTrue(button.hasFocus());
+        // Make sure that the clearing focus View is the first focusable.
+        View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
+                View.FOCUS_FORWARD);
+        assertSame("The clearing focus button is the first focusable.",
+                clearingFocusButton, focusCandidate);
+        assertSame("The gaining focus button is the first focusable.",
+                gainingFocusButton, focusCandidate);
 
-        // Attach on focus change listener that should not be called.
-        button.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                throw new IllegalStateException("Unexpeced call to"
-                        + "OnFocusChangeListener#onFocusChange");
-            }
-        });
+        // Focus the clearing focus button.
+        clearingFocusButton.requestFocus();
+        assertTrue(clearingFocusButton.hasFocus());
 
-        // Attach on global focus change listener that should not be called.
-        button.getViewTreeObserver().addOnGlobalFocusChangeListener(
-                new OnGlobalFocusChangeListener() {
-            @Override
-            public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-                throw new IllegalStateException("Unexpeced call to"
-                        + "OnFocusChangeListener#onFocusChange");
-            }
-        });
+        // Register the invocation order checker.
+        CombinedListeners mock = mock(CombinedListeners.class);
+        clearingFocusButton.setOnFocusChangeListener(mock);
+        gainingFocusButton.setOnFocusChangeListener(mock);
+        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(mock);
 
         // Try to clear focus.
-        button.clearFocus();
+        clearingFocusButton.clearFocus();
+
+        // Check that no callback was invoked since focus did not move.
+        InOrder inOrder = inOrder(mock);
+        inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
+        inOrder.verify(mock).onGlobalFocusChanged(clearingFocusButton, gainingFocusButton);
+        inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
     }
 
+    public interface CombinedListeners extends OnFocusChangeListener, OnGlobalFocusChangeListener {}
+
     /**
      * This tests check whether the on focus change callbacks are invoked in
      * the proper order when a View loses focus and the framework gives it to
@@ -150,12 +155,12 @@
      * @throws Exception
      */
     @UiThreadTest
-    public void testOnFocusChangeCallbackOrder() throws Exception {
-        // Get the first focusable.
+    public void testOnFocusChangeCallbackOrderWhenClearingFocusOfNotFirstFocusable()
+            throws Exception {
         Button clearingFocusButton = mTopRightButton;
         Button gainingFocusButton = mTopLeftButton;
 
-        // Make sure that the clearing focus is not the first focusable.
+        // Make sure that the clearing focus View is not the first focusable.
         View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
                 View.FOCUS_FORWARD);
         assertNotSame("The clearing focus button is not the first focusable.",
@@ -168,77 +173,18 @@
         assertTrue(clearingFocusButton.hasFocus());
 
         // Register the invocation order checker.
-        CallbackOrderChecker checker = new CallbackOrderChecker(clearingFocusButton,
-                gainingFocusButton);
-        clearingFocusButton.setOnFocusChangeListener(checker);
-        gainingFocusButton.setOnFocusChangeListener(checker);
-        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(checker);
+        CombinedListeners mock = mock(CombinedListeners.class);
+        clearingFocusButton.setOnFocusChangeListener(mock);
+        gainingFocusButton.setOnFocusChangeListener(mock);
+        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(mock);
 
         // Try to clear focus.
         clearingFocusButton.clearFocus();
 
         // Check that no callback was invoked since focus did not move.
-        checker.verify();
-    }
-
-    /**
-     * This class check whether the focus change callback are invoked in order.
-     */
-    private class CallbackOrderChecker implements OnFocusChangeListener,
-            OnGlobalFocusChangeListener {
-
-        private class CallbackInvocation {
-            final String mMethodName;
-            final Object[] mArguments;
-
-            CallbackInvocation(String methodName, Object[] arguments) {
-                mMethodName = methodName;
-                mArguments = arguments;
-            }
-        }
-
-        private final View mClearingFocusView;
-        private final View mGainingFocusView;
-
-        private final List<CallbackInvocation> mInvocations = new ArrayList<CallbackInvocation>();
-
-        public CallbackOrderChecker(View clearingFocusView, View gainingFocusView) {
-            mClearingFocusView = clearingFocusView;
-            mGainingFocusView = gainingFocusView;
-        }
-
-        @Override
-        public void onFocusChange(View view, boolean hasFocus) {
-            CallbackInvocation invocation = new CallbackInvocation(
-                    "OnFocusChangeListener#onFocusChange", new Object[] {view, hasFocus});
-            mInvocations.add(invocation);
-        }
-
-        @Override
-        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-            CallbackInvocation invocation = new CallbackInvocation(
-                    "OnFocusChangeListener#onFocusChange", new Object[] {oldFocus, newFocus});
-            mInvocations.add(invocation);
-        }
-
-        public void verify() {
-            assertSame("All focus change callback should be invoked.", 3, mInvocations.size());
-            assertInvioked("Callback for View clearing focus explected.", 0,
-                    "OnFocusChangeListener#onFocusChange",
-                    new Object[] {mClearingFocusView, false});
-            assertInvioked("Callback for View global focus change explected.", 1,
-                    "OnFocusChangeListener#onFocusChange", new Object[] {mClearingFocusView,
-                    mGainingFocusView});
-            assertInvioked("Callback for View gaining focus explected.", 2,
-                    "OnFocusChangeListener#onFocusChange", new Object[] {mGainingFocusView, true});
-        }
-
-        private void assertInvioked(String message, int order, String methodName,
-                Object[] arguments) {
-            CallbackInvocation invocation = mInvocations.get(order);
-            assertEquals(message, methodName, invocation.mMethodName);
-            assertEquals(message, arguments[0], invocation.mArguments[0]);
-            assertEquals(message, arguments[1], invocation.mArguments[1]);
-        }
+        InOrder inOrder = inOrder(mock);
+        inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
+        inOrder.verify(mock).onGlobalFocusChanged(clearingFocusButton, gainingFocusButton);
+        inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
     }
 }
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 02d2f3d..ef38a60 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -51,6 +51,11 @@
     $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(font_symlink)
 
 ################################
+# On space-constrained devices, we include a subset of fonts:
+ifeq ($(SMALLER_FONT_FOOTPRINT),true)
+droidsans_fallback_src := DroidSansFallback.ttf
+extra_droidsans_fonts := DroidSans.ttf DroidSans-Bold.ttf
+else
 include $(CLEAR_VARS)
 LOCAL_MODULE := DroidSansEthiopic-Regular.ttf
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
@@ -59,15 +64,11 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
 include $(BUILD_PREBUILT)
 
-################################
-ifeq ($(SMALLER_FONT_FOOTPRINT),true)
-droidsans_fallback_src := DroidSansFallback.ttf
-extra_droidsans_fonts := DroidSans.ttf DroidSans-Bold.ttf
-else
 droidsans_fallback_src := DroidSansFallbackFull.ttf
 extra_droidsans_fonts := DroidSans.ttf DroidSans-Bold.ttf DroidSansEthiopic-Regular.ttf
 endif  # SMALLER_FONT_FOOTPRINT
 
+################################
 include $(CLEAR_VARS)
 LOCAL_MODULE := DroidSansFallback.ttf
 LOCAL_SRC_FILES := $(droidsans_fallback_src)
@@ -81,3 +82,46 @@
 font_symlink :=
 droidsans_fallback_src :=
 extra_droidsans_fonts :=
+
+################################
+# Build the rest font files as prebuilt.
+
+# $(1): The source file name in LOCAL_PATH.
+#       It also serves as the module name and the dest file name.
+define build-one-font-module
+$(eval include $(CLEAR_VARS))\
+$(eval LOCAL_MODULE := $(1))\
+$(eval LOCAL_SRC_FILES := $(1))\
+$(eval LOCAL_MODULE_CLASS := ETC)\
+$(eval LOCAL_MODULE_TAGS := optional)\
+$(eval LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts)\
+$(eval include $(BUILD_PREBUILT))
+endef
+
+font_src_files := \
+    Roboto-Regular.ttf \
+    Roboto-Bold.ttf \
+    Roboto-Italic.ttf \
+    Roboto-BoldItalic.ttf \
+    DroidSansArabic.ttf \
+    DroidNaskh-Regular.ttf \
+    DroidSansHebrew-Regular.ttf \
+    DroidSansHebrew-Bold.ttf \
+    DroidSansThai.ttf \
+    DroidSerif-Regular.ttf \
+    DroidSerif-Bold.ttf \
+    DroidSerif-Italic.ttf \
+    DroidSerif-BoldItalic.ttf \
+    DroidSansMono.ttf \
+    DroidSansArmenian.ttf \
+    DroidSansGeorgian.ttf \
+    AndroidEmoji.ttf \
+    Clockopia.ttf \
+    AndroidClock.ttf \
+    AndroidClock_Highlight.ttf \
+    AndroidClock_Solid.ttf \
+
+$(foreach f, $(font_src_files), $(call build-one-font-module, $(f)))
+
+build-one-font-module :=
+font_src_files :=
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 6a9ed53..458f85b 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -14,34 +14,30 @@
 
 # Warning: this is actually a product definition, to be inherited from
 
-# On space-constrained devices, we include a subset of fonts:
-# First, core/required fonts
 PRODUCT_COPY_FILES := \
-    frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \
-    frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
-    frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
-    frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
-    frameworks/base/data/fonts/DroidSansArabic.ttf:system/fonts/DroidSansArabic.ttf \
-    frameworks/base/data/fonts/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \
-    frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \
-    frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \
-    frameworks/base/data/fonts/DroidSansThai.ttf:system/fonts/DroidSansThai.ttf \
-    frameworks/base/data/fonts/DroidSerif-Regular.ttf:system/fonts/DroidSerif-Regular.ttf \
-    frameworks/base/data/fonts/DroidSerif-Bold.ttf:system/fonts/DroidSerif-Bold.ttf \
-    frameworks/base/data/fonts/DroidSerif-Italic.ttf:system/fonts/DroidSerif-Italic.ttf \
-    frameworks/base/data/fonts/DroidSerif-BoldItalic.ttf:system/fonts/DroidSerif-BoldItalic.ttf \
-    frameworks/base/data/fonts/DroidSansMono.ttf:system/fonts/DroidSansMono.ttf \
-    frameworks/base/data/fonts/DroidSansArmenian.ttf:system/fonts/DroidSansArmenian.ttf \
-    frameworks/base/data/fonts/DroidSansGeorgian.ttf:system/fonts/DroidSansGeorgian.ttf \
-    frameworks/base/data/fonts/AndroidEmoji.ttf:system/fonts/AndroidEmoji.ttf \
-    frameworks/base/data/fonts/Clockopia.ttf:system/fonts/Clockopia.ttf \
-    frameworks/base/data/fonts/AndroidClock.ttf:system/fonts/AndroidClock.ttf \
-    frameworks/base/data/fonts/AndroidClock_Highlight.ttf:system/fonts/AndroidClock_Highlight.ttf \
-    frameworks/base/data/fonts/AndroidClock_Solid.ttf:system/fonts/AndroidClock_Solid.ttf \
     frameworks/base/data/fonts/system_fonts.xml:system/etc/system_fonts.xml \
     frameworks/base/data/fonts/fallback_fonts.xml:system/etc/fallback_fonts.xml
 
-# Next, include additional fonts, depending on how much space we have.
-# Details see module definitions in Android.mk.
 PRODUCT_PACKAGES := \
-    DroidSansFallback.ttf
+    DroidSansFallback.ttf \
+    Roboto-Regular.ttf \
+    Roboto-Bold.ttf \
+    Roboto-Italic.ttf \
+    Roboto-BoldItalic.ttf \
+    DroidSansArabic.ttf \
+    DroidNaskh-Regular.ttf \
+    DroidSansHebrew-Regular.ttf \
+    DroidSansHebrew-Bold.ttf \
+    DroidSansThai.ttf \
+    DroidSerif-Regular.ttf \
+    DroidSerif-Bold.ttf \
+    DroidSerif-Italic.ttf \
+    DroidSerif-BoldItalic.ttf \
+    DroidSansMono.ttf \
+    DroidSansArmenian.ttf \
+    DroidSansGeorgian.ttf \
+    AndroidEmoji.ttf \
+    Clockopia.ttf \
+    AndroidClock.ttf \
+    AndroidClock_Highlight.ttf \
+    AndroidClock_Solid.ttf \
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index afbad57..29f0749 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -1,16 +1,16 @@
 ndk=true
 
-ndk.win_download=android-ndk-r7-windows.zip
-ndk.win_bytes=81270552
-ndk.win_checksum=55483482cf2b75e8dd1a5d9a7caeb6e5
+ndk.win_download=android-ndk-r7b-windows.zip
+ndk.win_bytes=80346206
+ndk.win_checksum=c42b0c9c14428397337421d5e4999380
 
-ndk.mac_download=android-ndk-r7-darwin-x86.tar.bz2
-ndk.mac_bytes=71262092
-ndk.mac_checksum=817ca5675a1dd44078098e43070f19b6
+ndk.mac_download=android-ndk-r7b-darwin-x86.tar.bz2
+ndk.mac_bytes=73817184
+ndk.mac_checksum=6daa82ca6b73bc0614c9997430079c7a
 
-ndk.linux_download=android-ndk-r7-linux-x86.tar.bz2
-ndk.linux_bytes=64884365
-ndk.linux_checksum=bf15e6b47bf50824c4b96849bf003ca3
+ndk.linux_download=android-ndk-r7b-linux-x86.tar.bz2
+ndk.linux_bytes=64349733
+ndk.linux_checksum=0eb8af18796cdaa082df8f7c54ad7f9a
 
 page.title=Android NDK
 
@@ -62,6 +62,116 @@
 <div class="toggleable open">
   <a href="#" onclick="return toggleDiv(this)"><img src=
   "{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px">
+  Android NDK, Revision 7b</a> <em>(February 2012)</em>
+
+  <div class="toggleme">
+    <p>This release of the NDK includes fixes for native Windows builds, Cygwin and many other
+      improvements:</p>
+
+    <dl>
+      <dt>Important bug fixes:</dt>
+
+      <dd>
+        <ul>
+          <li>Updated {@code sys/atomics.h} to avoid correctness issues
+            on some multi-core ARM-based devices. Rebuild your unmodified sources with this
+            version of the NDK and this problem should be completely eliminated.
+            For more details, read {@code docs/ANDROID-ATOMICS.html}.</li>
+          <li>Reverted to {@code binutils} 2.19 to fix debugging issues that
+            appeared in NDK r7 (which switched to {@code binutils} 2.20.1).</li>
+          <li>Fixed {@code ndk-build} on 32-bit Linux. A packaging error put a 64-bit version
+            of the {@code awk} executable under {@code prebuilt/linux-x86/bin} in NDK r7.</li>
+          <li>Fixed native Windows build ({@code ndk-build.cmd}). Other build modes were not
+            affected. The fixes include:
+            <ul>
+              <li>Removed an infinite loop / stack overflow bug that happened when trying
+                to call {@code ndk-build.cmd} from a directory that was <em>not</em> the top of
+                your project path (e.g., in any sub-directory of it).</li>
+              <li>Fixed a problem where the auto-generated dependency files were ignored. This
+                meant that updating a header didn't trigger recompilation of sources that included
+                it.</li>
+              <li>Fixed a problem where special characters in files or paths, other than spaces and
+                quotes, were not correctly handled.</li>
+            </ul>
+          </li>
+          <li>Fixed the standalone toolchain to generate proper binaries when using
+            {@code -lstdc++} (i.e., linking against the GNU {@code libstdc++} C++ runtime). You
+            should use {@code -lgnustl_shared} if you want to link against the shared library
+            version or {@code -lstdc++} for the static version.
+
+            <p>See {@code docs/STANDALONE-TOOLCHAIN.html} for more details about this fix.</p>
+          </li>
+          <li>Fixed {@code gnustl_shared} on Cygwin. The linker complained that it couldn't find
+            {@code libsupc++.a} even though the file was at the right location.</li>
+          <li>Fixed Cygwin C++ link when not using any specific C++ runtime through
+            {@code APP_STL}.</li>
+        </ul>
+      </dd>
+    </dl>
+
+    <dl>
+      <dt>Other changes:</dt>
+
+      <dd>
+        <ul>
+          <li>When your application uses the GNU {@code libstdc++} runtime, the compiler will
+            no longer forcibly enable exceptions and RTTI. This change results in smaller code.
+            <p>If you need these features, you must do one of the following:</p>
+            <ul>
+              <li>Enable exceptions and/or RTTI explicitly in your modules or
+                {@code Application.mk}. (recommended)</li>
+              <li>Define {@code APP_GNUSTL_FORCE_CPP_FEATURES} to {@code 'exceptions'},
+                {@code 'rtti'} or both in your {@code Application.mk}. See
+                {@code docs/APPLICATION-MK.html} for more details.</li>
+            </ul>
+          </li>
+          <li>{@code ndk-gdb} now works properly when your application has private services
+            running in independent processes. It debugs the main application process, instead of the
+            first process listed by {@code ps}, which is usually a service process.</li>
+          <li>Fixed a rare bug where NDK r7 would fail to honor the {@code LOCAL_ARM_MODE} value
+            and always compile certain source files (but not all) to 32-bit instructions.</li>
+          <li>{@code stlport}: Refresh the sources to match the Android platform version. This
+            update fixes a few minor bugs:
+            <ul>
+               <li>Fixed instantiation of an incomplete type</li>
+               <li>Fixed minor "==" versus "=" typo</li>
+               <li>Used {@code memmove} instead of {@code memcpy} in {@code string::assign}</li>
+               <li>Added better handling of {@code IsNANorINF}, {@code IsINF}, {@code IsNegNAN},
+                 etc.</li>
+             </ul>
+             <p>For complete details, see the commit log.</p>
+          </li>
+          <li>{@code stlport}: Removed 5 unnecessary static initializers from the library.</li>
+          <li>The GNU libstdc++ libraries for armeabi-v7a were mistakenly compiled for
+            armeabi instead. This change had no impact on correctness, but using the right
+            ABI should provide slightly better performance.</li>
+          <li>The {@code cpu-features} helper library was updated to report three optional
+            x86 CPU features ({@code SSSE3}, {@code MOVBE} and {@code POPCNT}). See
+            {@code docs/CPU-FEATURES.html} for more details.</li>
+          <li>{@code docs/NDK-BUILD.html} was updated to mention {@code NDK_APPLICATION_MK} instead
+            of {@code NDK_APP_APPLICATION_MK} to select a custom {@code Application.mk} file.</li>
+          <li>Cygwin: {@code ndk-build} no longer creates an empty "NUL" file in the current
+            directory when invoked.</li>
+          <li>Cygwin: Added better automatic dependency detection. In the previous version, it
+            didn't work properly in the following cases:
+            <ul>
+              <li>When the Cygwin drive prefix was not {@code /cygdrive}.</li>
+              <li>When using drive-less mounts, for example, when Cygwin would translate
+                {@code /home} to {@code \\server\subdir} instead of {@code C:\Some\Dir}.</li>
+            </ul>
+          </li>
+          <li>Cygwin: {@code ndk-build} does not try to use the native Windows tools under
+            {@code $NDK/prebuilt/windows/bin} with certain versions of Cygwin and/or GNU Make.</li>
+        </ul>
+      </dd>
+    </dl>
+  </div>
+</div>
+
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)"><img src=
+  "{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px">
   Android NDK, Revision 7</a> <em>(November 2011)</em>
 
   <div class="toggleme">
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 0de477a..f7541f7 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -195,7 +195,7 @@
       <span style="display:none" class="zh-TW"></span>
     </span>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r7</a>
+      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r7b</a>
         <span class="new">new!</span>
         </li>
       <li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 380b3d8..6f939be 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -408,16 +408,19 @@
     }
 
     /**
-     * Creates a new bitmap, scaled from an existing bitmap.
+     * Creates a new bitmap, scaled from an existing bitmap, when possible. If the
+     * specified width and height are the same as the current width and height of 
+     * the source btimap, the source bitmap is returned and now new bitmap is
+     * created.
      *
      * @param src       The source bitmap.
      * @param dstWidth  The new bitmap's desired width.
      * @param dstHeight The new bitmap's desired height.
      * @param filter    true if the source should be filtered.
-     * @return the new scaled bitmap.
+     * @return The new scaled bitmap or the source bitmap if no scaling is required.
      */
-    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
-            int dstHeight, boolean filter) {
+    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight,
+            boolean filter) {
         Matrix m;
         synchronized (Bitmap.class) {
             // small pool of just 1 matrix
@@ -458,14 +461,15 @@
     /**
      * Returns an immutable bitmap from the specified subset of the source
      * bitmap. The new bitmap may be the same object as source, or a copy may
-     * have been made.  It is
-     * initialized with the same density as the original bitmap.
+     * have been made. It is initialized with the same density as the original
+     * bitmap.
      *
      * @param source   The bitmap we are subsetting
      * @param x        The x coordinate of the first pixel in source
      * @param y        The y coordinate of the first pixel in source
      * @param width    The number of pixels in each row
      * @param height   The number of rows
+     * @return A copy of a subset of the source bitmap or the source bitmap itself.
      */
     public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {
         return createBitmap(source, x, y, width, height, null, false);
@@ -473,8 +477,13 @@
 
     /**
      * Returns an immutable bitmap from subset of the source bitmap,
-     * transformed by the optional matrix.  It is
+     * transformed by the optional matrix. The new bitmap may be the
+     * same object as source, or a copy may have been made. It is
      * initialized with the same density as the original bitmap.
+     * 
+     * If the source bitmap is immutable and the requested subset is the
+     * same as the source bitmap itself, then the source bitmap is
+     * returned and no new bitmap is created.
      *
      * @param source   The bitmap we are subsetting
      * @param x        The x coordinate of the first pixel in source
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 509b972..44401c8 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2012 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.
@@ -131,7 +131,7 @@
     public static final int USAGE_GRAPHICS_RENDER_TARGET = 0x0010;
 
     /**
-     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allcation will be
+     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allocation will be
      * used with a SurfaceTexture object.  This usage will cause the
      * allocation to be created read only.
      *
@@ -140,18 +140,18 @@
     public static final int USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE = 0x0020;
 
     /**
-     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allcation will be
+     * USAGE_IO_INPUT The allocation will be
      * used with a SurfaceTexture object.  This usage will cause the
      * allocation to be created read only.
      *
      * @hide
      */
-
     public static final int USAGE_IO_INPUT = 0x0040;
+
     /**
-     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allcation will be
+     * USAGE_IO_OUTPUT The allocation will be
      * used with a SurfaceTexture object.  This usage will cause the
-     * allocation to be created read only.
+     * allocation to be created write only.
      *
      * @hide
      */
@@ -921,7 +921,31 @@
         if (type.getID() == 0) {
             throw new RSInvalidStateException("Bad Type");
         }
-        int id = rs.nAllocationCreateTyped(type.getID(), mips.mID, usage);
+        int id = rs.nAllocationCreateTyped(type.getID(), mips.mID, usage, 0);
+        if (id == 0) {
+            throw new RSRuntimeException("Allocation creation failed.");
+        }
+        return new Allocation(id, rs, type, usage);
+    }
+
+    /**
+     * @hide
+     * This API is hidden and only intended to be used for
+     * transitional purposes.
+     *
+     * @param type renderscript type describing data layout
+     * @param mips specifies desired mipmap behaviour for the
+     *             allocation
+     * @param usage bit field specifying how the allocation is
+     *              utilized
+     */
+    static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips,
+                                         int usage, int pointer) {
+        rs.validate();
+        if (type.getID() == 0) {
+            throw new RSInvalidStateException("Bad Type");
+        }
+        int id = rs.nAllocationCreateTyped(type.getID(), mips.mID, usage, pointer);
         if (id == 0) {
             throw new RSRuntimeException("Allocation creation failed.");
         }
@@ -976,7 +1000,7 @@
         b.setX(count);
         Type t = b.create();
 
-        int id = rs.nAllocationCreateTyped(t.getID(), MipmapControl.MIPMAP_NONE.mID, usage);
+        int id = rs.nAllocationCreateTyped(t.getID(), MipmapControl.MIPMAP_NONE.mID, usage, 0);
         if (id == 0) {
             throw new RSRuntimeException("Allocation creation failed.");
         }
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 22ef7bb..f6a0281 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -54,26 +54,59 @@
     int[] mArraySizes;
     int[] mOffsetInBytes;
 
+    int[] mVisibleElementMap;
+
     DataType mType;
     DataKind mKind;
     boolean mNormalized;
     int mVectorSize;
 
+    private void updateVisibleSubElements() {
+        if (mElements == null) {
+            return;
+        }
+
+        int noPaddingFieldCount = 0;
+        int fieldCount = mElementNames.length;
+        // Find out how many elements are not padding
+        for (int ct = 0; ct < fieldCount; ct ++) {
+            if (mElementNames[ct].charAt(0) != '#') {
+                noPaddingFieldCount ++;
+            }
+        }
+        mVisibleElementMap = new int[noPaddingFieldCount];
+
+        // Make a map that points us at non-padding elements
+        for (int ct = 0, ctNoPadding = 0; ct < fieldCount; ct ++) {
+            if (mElementNames[ct].charAt(0) != '#') {
+                mVisibleElementMap[ctNoPadding ++] = ct;
+            }
+        }
+    }
+
     /**
     * @hide
     * @return element size in bytes
     */
     public int getSizeBytes() {return mSize;}
 
+    /**
+    * @hide
+    * @return element vector size
+    */
+    public int getVectorSize() {return mVectorSize;}
+
 
     /**
      * DataType represents the basic type information for a basic element.  The
-     * naming convention follows.  For numeric types its FLOAT, SIGNED, UNSIGNED
-     * followed by the _BITS where BITS is the size of the data.  BOOLEAN is a
-     * true / false (1,0) represented in an 8 bit container.  The UNSIGNED
-     * variants with multiple bit definitions are for packed graphical data
-     * formats and represents vectors with per vector member sizes which are
-     * treated as a single unit for packing and alignment purposes.
+     * naming convention follows.  For numeric types it is FLOAT,
+     * SIGNED, or UNSIGNED followed by the _BITS where BITS is the
+     * size of the data.  BOOLEAN is a true / false (1,0)
+     * represented in an 8 bit container.  The UNSIGNED variants
+     * with multiple bit definitions are for packed graphical data
+     * formats and represent vectors with per vector member sizes
+     * which are treated as a single unit for packing and alignment
+     * purposes.
      *
      * MATRIX the three matrix types contain FLOAT_32 elements and are treated
      * as 32 bits for alignment purposes.
@@ -81,6 +114,11 @@
      * RS_* objects.  32 bit opaque handles.
      */
     public enum DataType {
+        /**
+        * @hide
+        * new enum
+        */
+        NONE (0, 0),
         //FLOAT_16 (1, 2),
         FLOAT_32 (2, 4),
         FLOAT_64 (3, 8),
@@ -167,10 +205,10 @@
     * @return number of sub-elements in this element
     */
     public int getSubElementCount() {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             return 0;
         }
-        return mElements.length;
+        return mVisibleElementMap.length;
     }
 
     /**
@@ -179,13 +217,13 @@
     * @return sub-element in this element at given index
     */
     public Element getSubElement(int index) {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             throw new RSIllegalArgumentException("Element contains no sub-elements");
         }
-        if (index < 0 || index >= mElements.length) {
+        if (index < 0 || index >= mVisibleElementMap.length) {
             throw new RSIllegalArgumentException("Illegal sub-element index");
         }
-        return mElements[index];
+        return mElements[mVisibleElementMap[index]];
     }
 
     /**
@@ -194,13 +232,13 @@
     * @return sub-element in this element at given index
     */
     public String getSubElementName(int index) {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             throw new RSIllegalArgumentException("Element contains no sub-elements");
         }
-        if (index < 0 || index >= mElements.length) {
+        if (index < 0 || index >= mVisibleElementMap.length) {
             throw new RSIllegalArgumentException("Illegal sub-element index");
         }
-        return mElementNames[index];
+        return mElementNames[mVisibleElementMap[index]];
     }
 
     /**
@@ -209,13 +247,13 @@
     * @return array size of sub-element in this element at given index
     */
     public int getSubElementArraySize(int index) {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             throw new RSIllegalArgumentException("Element contains no sub-elements");
         }
-        if (index < 0 || index >= mElements.length) {
+        if (index < 0 || index >= mVisibleElementMap.length) {
             throw new RSIllegalArgumentException("Illegal sub-element index");
         }
-        return mArraySizes[index];
+        return mArraySizes[mVisibleElementMap[index]];
     }
 
     /**
@@ -224,13 +262,29 @@
     * @return offset in bytes of sub-element in this element at given index
     */
     public int getSubElementOffsetBytes(int index) {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             throw new RSIllegalArgumentException("Element contains no sub-elements");
         }
-        if (index < 0 || index >= mElements.length) {
+        if (index < 0 || index >= mVisibleElementMap.length) {
             throw new RSIllegalArgumentException("Illegal sub-element index");
         }
-        return mOffsetInBytes[index];
+        return mOffsetInBytes[mVisibleElementMap[index]];
+    }
+
+    /**
+    * @hide
+    * @return element data type
+    */
+    public DataType getDataType() {
+        return mType;
+    }
+
+    /**
+    * @hide
+    * @return element data kind
+    */
+    public DataKind getDataKind() {
+        return mKind;
     }
 
     /**
@@ -681,14 +735,18 @@
     Element(int id, RenderScript rs, Element[] e, String[] n, int[] as) {
         super(id, rs);
         mSize = 0;
+        mVectorSize = 1;
         mElements = e;
         mElementNames = n;
         mArraySizes = as;
+        mType = DataType.NONE;
+        mKind = DataKind.USER;
         mOffsetInBytes = new int[mElements.length];
         for (int ct = 0; ct < mElements.length; ct++ ) {
             mOffsetInBytes[ct] = mSize;
             mSize += mElements[ct].mSize * mArraySizes[ct];
         }
+        updateVisibleSubElements();
     }
 
     Element(int id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
@@ -753,7 +811,7 @@
                 mSize += mElements[i].mSize * mArraySizes[i];
             }
         }
-
+        updateVisibleSubElements();
     }
 
     /**
diff --git a/graphics/java/android/renderscript/Path.java b/graphics/java/android/renderscript/Path.java
new file mode 100644
index 0000000..83ae150
--- /dev/null
+++ b/graphics/java/android/renderscript/Path.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 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 android.renderscript;
+
+import java.util.Vector;
+import android.util.Log;
+
+/**
+ * @hide
+ *
+ */
+public class Path extends BaseObj {
+
+    public enum Primitive {
+        QUADRATIC_BEZIER(0),
+        CUBIC_BEZIER(1);
+
+        int mID;
+        Primitive(int id) {
+            mID = id;
+        }
+    }
+
+    Allocation mVertexBuffer;
+    Allocation mLoopBuffer;
+    Primitive mPrimitive;
+    float mQuality;
+    boolean mCoverageToAlpha;
+
+    Path(int id, RenderScript rs, Primitive p, Allocation vtx, Allocation loop, float q) {
+        super(id, rs);
+        mVertexBuffer = vtx;
+        mLoopBuffer = loop;
+        mPrimitive = p;
+        mQuality = q;
+    }
+
+    public Allocation getVertexAllocation() {
+        return mVertexBuffer;
+    }
+
+    public Allocation getLoopAllocation() {
+        return mLoopBuffer;
+    }
+
+    public Primitive getPrimitive() {
+        return mPrimitive;
+    }
+
+    @Override
+    void updateFromNative() {
+    }
+
+
+    public static Path createStaticPath(RenderScript rs, Primitive p, float quality, Allocation vtx) {
+        int id = rs.nPathCreate(p.mID, false, vtx.getID(), 0, quality);
+        Path newPath = new Path(id, rs, p, null, null, quality);
+        return newPath;
+    }
+
+    public static Path createStaticPath(RenderScript rs, Primitive p, float quality, Allocation vtx, Allocation loops) {
+        return null;
+    }
+
+    public static Path createDynamicPath(RenderScript rs, Primitive p, float quality, Allocation vtx) {
+        return null;
+    }
+
+    public static Path createDynamicPath(RenderScript rs, Primitive p, float quality, Allocation vtx, Allocation loops) {
+        return null;
+    }
+
+
+}
+
+
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index a1b1ba3..3f769ee 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -77,6 +77,40 @@
     }
 
     /**
+     * @hide
+     */
+    public int getConstantCount() {
+        return mConstants != null ? mConstants.length : 0;
+    }
+
+    /**
+     * @hide
+     */
+    public Type getConstant(int slot) {
+        if (slot < 0 || slot >= mConstants.length) {
+            throw new IllegalArgumentException("Slot ID out of range.");
+        }
+        return mConstants[slot];
+    }
+
+    /**
+     * @hide
+     */
+    public int getTextureCount() {
+        return mTextureCount;
+    }
+
+    /**
+     * @hide
+     */
+    public TextureType getTextureType(int slot) {
+        if ((slot < 0) || (slot >= mTextureCount)) {
+            throw new IllegalArgumentException("Slot ID out of range.");
+        }
+        return mTextures[slot];
+    }
+
+    /**
      * Binds a constant buffer to be used as uniform inputs to the
      * program
      *
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 56bb836..b3c1bd9 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -55,6 +55,23 @@
     }
 
     /**
+     * @hide
+     */
+    public int getInputCount() {
+        return mInputs != null ? mInputs.length : 0;
+    }
+
+    /**
+     * @hide
+     */
+    public Element getInput(int slot) {
+        if (slot < 0 || slot >= mInputs.length) {
+            throw new IllegalArgumentException("Slot ID out of range.");
+        }
+        return mInputs[slot];
+    }
+
+    /**
     * Builder class for creating ProgramVertex objects.
     * The builder starts empty and the user must minimally provide
     * the GLSL shader code, and the varying inputs. Constant, or
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 3f3033b..d3c801f2 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -231,10 +231,10 @@
         rsnTypeGetNativeData(mContext, id, typeData);
     }
 
-    native int  rsnAllocationCreateTyped(int con, int type, int mip, int usage);
-    synchronized int nAllocationCreateTyped(int type, int mip, int usage) {
+    native int  rsnAllocationCreateTyped(int con, int type, int mip, int usage, int pointer);
+    synchronized int nAllocationCreateTyped(int type, int mip, int usage, int pointer) {
         validate();
-        return rsnAllocationCreateTyped(mContext, type, mip, usage);
+        return rsnAllocationCreateTyped(mContext, type, mip, usage, pointer);
     }
     native int  rsnAllocationCreateFromBitmap(int con, int type, int mip, Bitmap bmp, int usage);
     synchronized int nAllocationCreateFromBitmap(int type, int mip, Bitmap bmp, int usage) {
@@ -590,6 +590,11 @@
         rsnMeshGetIndices(mContext, id, idxIds, primitives, vtxIdCount);
     }
 
+    native int  rsnPathCreate(int con, int prim, boolean isStatic, int vtx, int loop, float q);
+    synchronized int nPathCreate(int prim, boolean isStatic, int vtx, int loop, float q) {
+        validate();
+        return rsnPathCreate(mContext, prim, isStatic, vtx, loop, q);
+    }
 
     int     mDev;
     int     mContext;
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index a9f0f1f..94f19b3 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -430,10 +430,10 @@
 // -----------------------------------
 
 static jint
-nAllocationCreateTyped(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mips, jint usage)
+nAllocationCreateTyped(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mips, jint usage, jint pointer)
 {
-    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i)", con, (RsElement)type, mips, usage);
-    return (jint) rsAllocationCreateTyped(con, (RsType)type, (RsAllocationMipmapControl)mips, (uint32_t)usage);
+    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)", con, (RsElement)type, mips, usage, (void *)pointer);
+    return (jint) rsAllocationCreateTyped(con, (RsType)type, (RsAllocationMipmapControl)mips, (uint32_t)usage, (uint32_t)pointer);
 }
 
 static void
@@ -1129,6 +1129,17 @@
 
 // ---------------------------------------------------------------------------
 
+//native int  rsnPathCreate(int con, int prim, boolean isStatic, int vtx, int loop, float q);
+static jint
+nPathCreate(JNIEnv *_env, jobject _this, RsContext con, jint prim, jboolean isStatic, jint _vtx, jint _loop, jfloat q) {
+    LOG_API("nPathCreate, con(%p)", con);
+
+    int id = (int)rsPathCreate(con, (RsPathPrimitive)prim, isStatic,
+                               (RsAllocation)_vtx,
+                               (RsAllocation)_loop, q);
+    return id;
+}
+
 static jint
 nMeshCreate(JNIEnv *_env, jobject _this, RsContext con, jintArray _vtx, jintArray _idx, jintArray _prim)
 {
@@ -1257,7 +1268,7 @@
 {"rsnTypeCreate",                    "(IIIIIZZ)I",                            (void*)nTypeCreate },
 {"rsnTypeGetNativeData",             "(II[I)V",                               (void*)nTypeGetNativeData },
 
-{"rsnAllocationCreateTyped",         "(IIII)I",                               (void*)nAllocationCreateTyped },
+{"rsnAllocationCreateTyped",         "(IIIII)I",                               (void*)nAllocationCreateTyped },
 {"rsnAllocationCreateFromBitmap",    "(IIILandroid/graphics/Bitmap;I)I",      (void*)nAllocationCreateFromBitmap },
 {"rsnAllocationCubeCreateFromBitmap","(IIILandroid/graphics/Bitmap;I)I",      (void*)nAllocationCubeCreateFromBitmap },
 
@@ -1318,6 +1329,7 @@
 
 {"rsnSamplerCreate",                 "(IIIIIIF)I",                            (void*)nSamplerCreate },
 
+{"rsnPathCreate",                    "(IIZIIF)I",                             (void*)nPathCreate },
 {"rsnMeshCreate",                    "(I[I[I[I)I",                            (void*)nMeshCreate },
 
 {"rsnMeshGetVertexBufferCount",      "(II)I",                                 (void*)nMeshGetVertexBufferCount },
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 991a181..ae99160 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -60,6 +60,8 @@
     BufferQueue(bool allowSynchronousMode = true);
     virtual ~BufferQueue();
 
+    virtual int query(int what, int* value);
+
     // setBufferCount updates the number of available buffer slots.  After
     // calling this all buffer slots are both unallocated and owned by the
     // BufferQueue object (i.e. they are not owned by the client).
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 4318f0f..dcab049 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -55,7 +55,7 @@
 
     virtual ~SurfaceTexture();
 
-    virtual int query(int what, int* value);
+
 
     // updateTexImage sets the image contents of the target texture to that of
     // the most recently queued buffer.
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index ca57f9e..437a89c 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -341,7 +341,7 @@
     private:
         friend class AudioRecord;
         virtual bool        threadLoop();
-        virtual status_t    readyToRun() { return NO_ERROR; }
+        virtual status_t    readyToRun();
         virtual void        onFirstRef() {}
         AudioRecord& mReceiver;
     };
@@ -359,7 +359,9 @@
     sp<IAudioRecord>        mAudioRecord;
     sp<IMemory>             mCblkMemory;
     sp<ClientRecordThread>  mClientRecordThread;
+    status_t                mReadyToRun;
     Mutex                   mLock;
+    Condition               mCondition;
 
     uint32_t                mFrameCount;
 
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index da99620..1916ac5 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -131,7 +131,7 @@
         NUM_CONFIG_EVENTS
     };
 
-    // audio output descritor used to cache output configurations in client process to avoid frequent calls
+    // audio output descriptor used to cache output configurations in client process to avoid frequent calls
     // through IAudioFlinger
     class OutputDescriptor {
     public:
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 11db81b..ac7f6cf 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -58,8 +58,8 @@
         EVENT_BUFFER_END = 5        // Playback head is at the end of the buffer.
     };
 
-    /* Create Buffer on the stack and pass it to obtainBuffer()
-     * and releaseBuffer().
+    /* Client should declare Buffer on the stack and pass address to obtainBuffer()
+     * and releaseBuffer().  See also callback_t for EVENT_MORE_DATA.
      */
 
     class Buffer
@@ -68,12 +68,16 @@
         enum {
             MUTE    = 0x00000001
         };
-        uint32_t    flags;
+        uint32_t    flags;        // 0 or MUTE
         audio_format_t format; // but AUDIO_FORMAT_PCM_8_BIT -> AUDIO_FORMAT_PCM_16_BIT
         // accessed directly by WebKit ANP callback
         int         channelCount; // will be removed in the future, do not use
-        size_t      frameCount;
-        size_t      size;
+
+        size_t      frameCount;   // number of sample frames corresponding to size;
+                                  // on input it is the number of frames desired,
+                                  // on output is the number of frames actually filled
+
+        size_t      size;         // input/output in byte units
         union {
             void*       raw;
             short*      i16;    // signed 16-bit
@@ -84,15 +88,15 @@
 
     /* As a convenience, if a callback is supplied, a handler thread
      * is automatically created with the appropriate priority. This thread
-     * invokes the callback when a new buffer becomes available or an underrun condition occurs.
+     * invokes the callback when a new buffer becomes available or various conditions occur.
      * Parameters:
      *
      * event:   type of event notified (see enum AudioTrack::event_type).
      * user:    Pointer to context for use by the callback receiver.
      * info:    Pointer to optional parameter according to event type:
      *          - EVENT_MORE_DATA: pointer to AudioTrack::Buffer struct. The callback must not write
-     *          more bytes than indicated by 'size' field and update 'size' if less bytes are
-     *          written.
+     *            more bytes than indicated by 'size' field and update 'size' if fewer bytes are
+     *            written.
      *          - EVENT_UNDERRUN: unused.
      *          - EVENT_LOOP_END: pointer to an int indicating the number of loops remaining.
      *          - EVENT_MARKER: pointer to an uint32_t containing the marker position in frames.
@@ -225,7 +229,7 @@
      */
             uint32_t     latency() const;
 
-    /* getters, see constructor */
+    /* getters, see constructors and set() */
 
             audio_stream_type_t streamType() const;
             audio_format_t format() const;
@@ -299,7 +303,6 @@
      *          (loopEnd-loopStart) <= framecount()
      */
             status_t    setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount);
-            status_t    getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount) const;
 
     /* Sets marker position. When playback reaches the number of frames specified, a callback with
      * event type EVENT_MARKER is called. Calling setMarkerPosition with marker == 0 cancels marker
@@ -401,13 +404,19 @@
             status_t    attachAuxEffect(int effectId);
 
     /* Obtains a buffer of "frameCount" frames. The buffer must be
-     * filled entirely. If the track is stopped, obtainBuffer() returns
+     * filled entirely, and then released with releaseBuffer().
+     * If the track is stopped, obtainBuffer() returns
      * STOPPED instead of NO_ERROR as long as there are buffers available,
      * at which point NO_MORE_BUFFERS is returned.
      * Buffers will be returned until the pool (buffercount())
      * is exhausted, at which point obtainBuffer() will either block
      * or return WOULD_BLOCK depending on the value of the "blocking"
      * parameter.
+     *
+     * Interpretation of waitCount:
+     *  +n  limits wait time to n * WAIT_PERIOD_MS,
+     *  -1  causes an (almost) infinite wait time,
+     *   0  non-blocking.
      */
 
         enum {
@@ -416,12 +425,19 @@
         };
 
             status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount);
+
+    /* Release a filled buffer of "frameCount" frames for AudioFlinger to process. */
             void        releaseBuffer(Buffer* audioBuffer);
 
     /* As a convenience we provide a write() interface to the audio buffer.
-     * This is implemented on top of lockBuffer/unlockBuffer. For best
-     * performance use callbacks. Return actual number of bytes written.
-     *
+     * This is implemented on top of obtainBuffer/releaseBuffer. For best
+     * performance use callbacks. Returns actual number of bytes written >= 0,
+     * or one of the following negative status codes:
+     *      INVALID_OPERATION   AudioTrack is configured for shared buffer mode
+     *      BAD_VALUE           size is invalid
+     *      STOPPED             AudioTrack was stopped during the write
+     *      NO_MORE_BUFFERS     when obtainBuffer() returns same
+     *      or any other error code returned by IAudioTrack::start() or restoreTrack_l().
      */
             ssize_t     write(const void* buffer, size_t size);
 
@@ -448,6 +464,7 @@
         AudioTrack& mReceiver;
     };
 
+            // body of AudioTrackThread::threadLoop()
             bool processAudioBuffer(const sp<AudioTrackThread>& thread);
             status_t createTrack_l(audio_stream_type_t streamType,
                                  uint32_t sampleRate,
@@ -484,7 +501,7 @@
 
     bool                    mActive;                // protected by mLock
 
-    callback_t              mCbf;
+    callback_t              mCbf;                   // callback handler for events, or NULL
     void*                   mUserData;
     uint32_t                mNotificationFramesReq; // requested number of frames between each notification callback
     uint32_t                mNotificationFramesAct; // actual number of frames between each notification callback
diff --git a/include/media/EffectsFactoryApi.h b/include/media/EffectsFactoryApi.h
index df83995..65c26f47 100644
--- a/include/media/EffectsFactoryApi.h
+++ b/include/media/EffectsFactoryApi.h
@@ -87,7 +87,7 @@
 //    Description:    Creates an effect engine of the specified type and returns an
 //          effect control interface on this engine. The function will allocate the
 //          resources for an instance of the requested effect engine and return
-//          a handler on the effect control interface.
+//          a handle on the effect control interface.
 //
 //    Input:
 //          pEffectUuid:    pointer to the effect uuid.
@@ -115,17 +115,17 @@
 //
 //    Function:       EffectRelease
 //
-//    Description:    Releases the effect engine whose handler is given as argument.
+//    Description:    Releases the effect engine whose handle is given as argument.
 //          All resources allocated to this particular instance of the effect are
 //          released.
 //
 //    Input:
-//          handle:    handler on the effect interface to be released.
+//          handle:    handle on the effect interface to be released.
 //
 //    Output:
 //        returned value:    0          successful operation.
 //                          -ENODEV     factory failed to initialize
-//                          -EINVAL     invalid interface handler
+//                          -EINVAL     invalid interface handle
 //
 ////////////////////////////////////////////////////////////////////////////////
 int EffectRelease(effect_handle_t handle);
diff --git a/include/media/IAudioRecord.h b/include/media/IAudioRecord.h
index 46735de..7869020 100644
--- a/include/media/IAudioRecord.h
+++ b/include/media/IAudioRecord.h
@@ -37,8 +37,9 @@
 
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
+     * tid identifies the client callback thread, or 0 if not needed.
      */
-    virtual status_t    start() = 0;
+    virtual status_t    start(pid_t tid) = 0;
 
     /* Stop a track. If set, the callback will cease being called and
      * obtainBuffer will return an error. Buffers that are already released 
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index b83e552..e4772a1 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -40,17 +40,18 @@
 
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
+     * tid identifies the client callback thread, or 0 if not needed.
      */
-    virtual status_t    start() = 0;
+    virtual status_t    start(pid_t tid) = 0;
 
     /* Stop a track. If set, the callback will cease being called and
      * obtainBuffer will return an error. Buffers that are already released 
-     * will be processed, unless flush() is called.
+     * will continue to be processed, unless/until flush() is called.
      */
     virtual void        stop() = 0;
 
-    /* Flush a stopped track. All pending buffers are discarded.
-     * This function has no effect if the track is not stopped.
+    /* Flush a stopped or paused track. All pending/released buffers are discarded.
+     * This function has no effect if the track is not stopped or paused.
      */
     virtual void        flush() = 0;
 
@@ -61,7 +62,7 @@
     
     /* Pause a track. If set, the callback will cease being called and
      * obtainBuffer will return an error. Buffers that are already released 
-     * will be processed, unless flush() is called.
+     * will continue to be processed, unless/until flush() is called.
      */
     virtual void        pause() = 0;
 
diff --git a/include/media/MemoryLeakTrackUtil.h b/include/media/MemoryLeakTrackUtil.h
index 290b748..ac0f6b2 100644
--- a/include/media/MemoryLeakTrackUtil.h
+++ b/include/media/MemoryLeakTrackUtil.h
@@ -19,7 +19,7 @@
 
 namespace android {
 /*
- * Dump the memory adddress of the calling process to the given fd.
+ * Dump the memory address of the calling process to the given fd.
  */
 extern void dumpMemoryAddresses(int fd);
 
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 23226c0..af2db93 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -62,16 +62,23 @@
     // are in the same line of data cache.
                 Mutex       lock;           // sizeof(int)
                 Condition   cv;             // sizeof(int)
+
+                // next 4 are offsets within "buffers"
     volatile    uint32_t    user;
     volatile    uint32_t    server;
                 uint32_t    userBase;
                 uint32_t    serverBase;
+
+                // if there is a shared buffer, "buffers" is the value of pointer() for the shared
+                // buffer, otherwise "buffers" points immediately after the control block
                 void*       buffers;
                 uint32_t    frameCount;
+
                 // Cache line boundary
+
                 uint32_t    loopStart;
-                uint32_t    loopEnd;
-                int         loopCount;
+                uint32_t    loopEnd;        // read-only for server, read/write for client
+                int         loopCount;      // read/write for client
 
                 // Channel volumes are fixed point U4.12, so 0x1000 means 1.0.
                 // Left channel is in [0:15], right channel is in [16:31].
@@ -82,29 +89,39 @@
 public:
 
                 uint32_t    sampleRate;
+
                 // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for
                 // 8 bit PCM data: in this case,  mCblk->frameSize is based on a sample size of
                 // 16 bit because data is converted to 16 bit before being stored in buffer
 
+                // read-only for client, server writes once at initialization and is then read-only
                 uint8_t     frameSize;       // would normally be size_t, but 8 bits is plenty
+
+                // never used
                 uint8_t     pad1;
+
+                // used by client only
                 uint16_t    bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
 
-                uint16_t    waitTimeMs;      // Cumulated wait time
+                uint16_t    waitTimeMs;      // Cumulated wait time, used by client only
 private:
+                // client write-only, server read-only
                 uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0
 public:
     volatile    int32_t     flags;
 
                 // Cache line boundary (32 bytes)
 
+                // Since the control block is always located in shared memory, this constructor
+                // is only used for placement new().  It is never used for regular new() or stack.
                             audio_track_cblk_t();
-                uint32_t    stepUser(uint32_t frameCount);
-                bool        stepServer(uint32_t frameCount);
+                uint32_t    stepUser(uint32_t frameCount);      // called by client only, where
+                // client includes regular AudioTrack and AudioFlinger::PlaybackThread::OutputTrack
+                bool        stepServer(uint32_t frameCount);    // called by server only
                 void*       buffer(uint32_t offset) const;
                 uint32_t    framesAvailable();
                 uint32_t    framesAvailable_l();
-                uint32_t    framesReady();
+                uint32_t    framesReady();                      // called by server only
                 bool        tryLock();
 
                 // No barriers on the following operations, so the ordering of loads/stores
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 1f738cd..0facce3 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -123,7 +123,7 @@
     virtual ~InputChannel();
 
 public:
-    InputChannel(const String8& name, int32_t fd);
+    InputChannel(const String8& name, int fd);
 
     /* Creates a pair of input channels.
      *
@@ -133,7 +133,7 @@
             sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
 
     inline String8 getName() const { return mName; }
-    inline int32_t getFd() const { return mFd; }
+    inline int getFd() const { return mFd; }
 
     /* Sends a message to the other endpoint.
      *
@@ -162,7 +162,7 @@
 
 private:
     String8 mName;
-    int32_t mFd;
+    int mFd;
 };
 
 /*
diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h
index 6bcdea4f..fcc3bcf 100644
--- a/include/utils/KeyedVector.h
+++ b/include/utils/KeyedVector.h
@@ -66,7 +66,7 @@
             ssize_t         indexOfKey(const KEY& key) const;
 
     /*!
-     * modifing the array
+     * modifying the array
      */
 
             VALUE&          editValueFor(const KEY& key);
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index c7e2c0f..0791de2 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -166,6 +166,37 @@
     return OK;
 }
 
+int BufferQueue::query(int what, int* outValue)
+{
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("query: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
+    int value;
+    switch (what) {
+    case NATIVE_WINDOW_WIDTH:
+        value = mDefaultWidth;
+        break;
+    case NATIVE_WINDOW_HEIGHT:
+        value = mDefaultHeight;
+        break;
+    case NATIVE_WINDOW_FORMAT:
+        value = mPixelFormat;
+        break;
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+        value = mSynchronousMode ?
+                (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
+        break;
+    default:
+        return BAD_VALUE;
+    }
+    outValue[0] = value;
+    return NO_ERROR;
+}
+
 status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
     ST_LOGV("requestBuffer: slot=%d", slot);
     Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index be1bcd1..a7bfc61 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -416,36 +416,7 @@
     return mSynchronousMode;
 }
 
-int SurfaceTexture::query(int what, int* outValue)
-{
-    Mutex::Autolock lock(mMutex);
 
-    if (mAbandoned) {
-        ST_LOGE("query: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-
-    int value;
-    switch (what) {
-    case NATIVE_WINDOW_WIDTH:
-        value = mDefaultWidth;
-        break;
-    case NATIVE_WINDOW_HEIGHT:
-        value = mDefaultHeight;
-        break;
-    case NATIVE_WINDOW_FORMAT:
-        value = mPixelFormat;
-        break;
-    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
-        value = mSynchronousMode ?
-                (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
-        break;
-    default:
-        return BAD_VALUE;
-    }
-    outValue[0] = value;
-    return NO_ERROR;
-}
 
 void SurfaceTexture::abandon() {
     Mutex::Autolock lock(mMutex);
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index 2166ce7..9c5d06b 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -95,6 +95,7 @@
 	rsMatrix4x4.cpp \
 	rsMesh.cpp \
 	rsMutex.cpp \
+	rsPath.cpp \
 	rsProgram.cpp \
 	rsProgramFragment.cpp \
 	rsProgramStore.cpp \
@@ -117,6 +118,7 @@
 	driver/rsdGL.cpp \
 	driver/rsdMesh.cpp \
 	driver/rsdMeshObj.cpp \
+	driver/rsdPath.cpp \
 	driver/rsdProgram.cpp \
 	driver/rsdProgramRaster.cpp \
 	driver/rsdProgramStore.cpp \
@@ -201,6 +203,7 @@
 	rsMatrix4x4.cpp \
 	rsMesh.cpp \
 	rsMutex.cpp \
+	rsPath.cpp \
 	rsProgram.cpp \
 	rsProgramFragment.cpp \
 	rsProgramStore.cpp \
diff --git a/libs/rs/RenderScriptDefines.h b/libs/rs/RenderScriptDefines.h
index 5b0a3da..990ef26 100644
--- a/libs/rs/RenderScriptDefines.h
+++ b/libs/rs/RenderScriptDefines.h
@@ -41,6 +41,7 @@
 typedef void * RsSampler;
 typedef void * RsScript;
 typedef void * RsMesh;
+typedef void * RsPath;
 typedef void * RsType;
 typedef void * RsObjectBase;
 
@@ -155,6 +156,8 @@
     RS_TYPE_PROGRAM_VERTEX,
     RS_TYPE_PROGRAM_RASTER,
     RS_TYPE_PROGRAM_STORE,
+
+    RS_TYPE_INVALID = 10000,
 };
 
 enum RsDataKind {
@@ -166,6 +169,8 @@
     RS_KIND_PIXEL_RGB,
     RS_KIND_PIXEL_RGBA,
     RS_KIND_PIXEL_DEPTH,
+
+    RS_KIND_INVALID = 100,
 };
 
 enum RsSamplerParam {
@@ -184,6 +189,8 @@
     RS_SAMPLER_WRAP,
     RS_SAMPLER_CLAMP,
     RS_SAMPLER_LINEAR_MIP_NEAREST,
+
+    RS_SAMPLER_INVALID = 100,
 };
 
 enum RsTextureTarget {
@@ -224,7 +231,8 @@
     RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA,   // 5
     RS_BLEND_SRC_DST_ALPHA,             // 6
     RS_BLEND_SRC_ONE_MINUS_DST_ALPHA,   // 7
-    RS_BLEND_SRC_SRC_ALPHA_SATURATE     // 8
+    RS_BLEND_SRC_SRC_ALPHA_SATURATE,    // 8
+    RS_BLEND_SRC_INVALID = 100,
 };
 
 enum RsBlendDstFunc {
@@ -235,7 +243,9 @@
     RS_BLEND_DST_SRC_ALPHA,             // 4
     RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,   // 5
     RS_BLEND_DST_DST_ALPHA,             // 6
-    RS_BLEND_DST_ONE_MINUS_DST_ALPHA    // 7
+    RS_BLEND_DST_ONE_MINUS_DST_ALPHA,   // 7
+
+    RS_BLEND_DST_INVALID = 100,
 };
 
 enum RsTexEnvMode {
@@ -258,7 +268,14 @@
     RS_PRIMITIVE_LINE_STRIP,
     RS_PRIMITIVE_TRIANGLE,
     RS_PRIMITIVE_TRIANGLE_STRIP,
-    RS_PRIMITIVE_TRIANGLE_FAN
+    RS_PRIMITIVE_TRIANGLE_FAN,
+
+    RS_PRIMITIVE_INVALID = 100,
+};
+
+enum RsPathPrimitive {
+    RS_PATH_PRIMITIVE_QUADRATIC_BEZIER,
+    RS_PATH_PRIMITIVE_CUBIC_BEZIER
 };
 
 enum RsError {
@@ -312,7 +329,8 @@
 enum RsCullMode {
     RS_CULL_BACK,
     RS_CULL_FRONT,
-    RS_CULL_NONE
+    RS_CULL_NONE,
+    RS_CULL_INVALID = 100,
 };
 
 typedef struct {
diff --git a/libs/rs/driver/rsdAllocation.cpp b/libs/rs/driver/rsdAllocation.cpp
index fac40f2..ea92192 100644
--- a/libs/rs/driver/rsdAllocation.cpp
+++ b/libs/rs/driver/rsdAllocation.cpp
@@ -219,10 +219,13 @@
         return false;
     }
 
-    void * ptr = malloc(alloc->mHal.state.type->getSizeBytes());
+    void * ptr = alloc->mHal.state.usrPtr;
     if (!ptr) {
-        free(drv);
-        return false;
+        ptr = malloc(alloc->mHal.state.type->getSizeBytes());
+        if (!ptr) {
+            free(drv);
+            return false;
+        }
     }
 
     drv->glTarget = GL_NONE;
@@ -276,7 +279,7 @@
         drv->renderTargetID = 0;
     }
 
-    if (drv->mallocPtr) {
+    if (drv->mallocPtr && !alloc->mHal.state.usrPtr) {
         free(drv->mallocPtr);
         drv->mallocPtr = NULL;
     }
diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp
index 9987027..e011955 100644
--- a/libs/rs/driver/rsdCore.cpp
+++ b/libs/rs/driver/rsdCore.cpp
@@ -18,6 +18,7 @@
 #include "rsdAllocation.h"
 #include "rsdBcc.h"
 #include "rsdGL.h"
+#include "rsdPath.h"
 #include "rsdProgramStore.h"
 #include "rsdProgramRaster.h"
 #include "rsdProgramVertex.h"
@@ -115,6 +116,13 @@
     },
 
     {
+        rsdPathInitStatic,
+        rsdPathInitDynamic,
+        rsdPathDraw,
+        rsdPathDestroy
+    },
+
+    {
         rsdSamplerInit,
         rsdSamplerDestroy
     },
@@ -260,6 +268,9 @@
     for (uint32_t ct=0; ct < dc->mWorkers.mCount; ct++) {
         setpriority(PRIO_PROCESS, dc->mWorkers.mNativeThreadId[ct], priority);
     }
+    if (dc->mHasGraphics) {
+        rsdGLSetPriority(rsc, priority);
+    }
 }
 
 void Shutdown(Context *rsc) {
diff --git a/libs/rs/driver/rsdCore.h b/libs/rs/driver/rsdCore.h
index 126c87a..168bdf3 100644
--- a/libs/rs/driver/rsdCore.h
+++ b/libs/rs/driver/rsdCore.h
@@ -41,6 +41,7 @@
 typedef struct RsdHalRec {
     uint32_t version_major;
     uint32_t version_minor;
+    bool mHasGraphics;
 
     struct Workers {
         volatile int mRunningCount;
diff --git a/libs/rs/driver/rsdGL.cpp b/libs/rs/driver/rsdGL.cpp
index 368dd710..b136cc7 100644
--- a/libs/rs/driver/rsdGL.cpp
+++ b/libs/rs/driver/rsdGL.cpp
@@ -361,6 +361,7 @@
     dc->gl.vertexArrayState = new RsdVertexArrayState();
     dc->gl.vertexArrayState->init(dc->gl.gl.maxVertexAttribs);
     dc->gl.currentFrameBuffer = NULL;
+    dc->mHasGraphics = true;
 
     ALOGV("%p initGLThread end", rsc);
     rsc->setWatchdogGL(NULL, 0, NULL);
@@ -421,6 +422,15 @@
     RSD_CALL_GL(eglSwapBuffers, dc->gl.egl.display, dc->gl.egl.surface);
 }
 
+void rsdGLSetPriority(const Context *rsc, int32_t priority) {
+    if (priority > 0) {
+        // Mark context as low priority.
+        ALOGV("low pri");
+    } else {
+        ALOGV("normal pri");
+    }
+}
+
 void rsdGLCheckError(const android::renderscript::Context *rsc,
                      const char *msg, bool isFatal) {
     GLenum err = glGetError();
diff --git a/libs/rs/driver/rsdGL.h b/libs/rs/driver/rsdGL.h
index 51893c3..e015cb1 100644
--- a/libs/rs/driver/rsdGL.h
+++ b/libs/rs/driver/rsdGL.h
@@ -82,6 +82,8 @@
 void rsdGLSwap(const android::renderscript::Context *rsc);
 void rsdGLCheckError(const android::renderscript::Context *rsc,
                      const char *msg, bool isFatal = false);
+void rsdGLSetPriority(const android::renderscript::Context *rsc,
+                      int32_t priority);
 
 #endif
 
diff --git a/libs/rs/driver/rsdMesh.cpp b/libs/rs/driver/rsdMesh.cpp
index eb62ddb..50daf3e 100644
--- a/libs/rs/driver/rsdMesh.cpp
+++ b/libs/rs/driver/rsdMesh.cpp
@@ -35,7 +35,7 @@
     }
     drv = new RsdMeshObj(rsc, m);
     m->mHal.drv = drv;
-    return drv->init();
+    return drv->init(rsc);
 }
 
 void rsdMeshDraw(const Context *rsc, const Mesh *m, uint32_t primIndex, uint32_t start, uint32_t len) {
diff --git a/libs/rs/driver/rsdMeshObj.cpp b/libs/rs/driver/rsdMeshObj.cpp
index 99d79dc..893f046 100644
--- a/libs/rs/driver/rsdMeshObj.cpp
+++ b/libs/rs/driver/rsdMeshObj.cpp
@@ -50,14 +50,9 @@
 }
 
 bool RsdMeshObj::isValidGLComponent(const Element *elem, uint32_t fieldIdx) {
-    // Do not create attribs for padding
-    if (elem->getFieldName(fieldIdx)[0] == '#') {
-        return false;
-    }
-
     // Only GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, GL_FLOAT are accepted.
     // Filter rs types accordingly
-    RsDataType dt = elem->getField(fieldIdx)->getComponent().getType();
+    RsDataType dt = elem->mHal.state.fields[fieldIdx]->mHal.state.dataType;
     if (dt != RS_TYPE_FLOAT_32 && dt != RS_TYPE_UNSIGNED_8 &&
         dt != RS_TYPE_UNSIGNED_16 && dt != RS_TYPE_SIGNED_8 &&
         dt != RS_TYPE_SIGNED_16) {
@@ -65,7 +60,7 @@
     }
 
     // Now make sure they are not arrays
-    uint32_t arraySize = elem->getFieldArraySize(fieldIdx);
+    uint32_t arraySize = elem->mHal.state.fieldArraySizes[fieldIdx];
     if (arraySize != 1) {
         return false;
     }
@@ -73,15 +68,15 @@
     return true;
 }
 
-bool RsdMeshObj::init() {
+bool RsdMeshObj::init(const Context *rsc) {
 
-    updateGLPrimitives();
+    updateGLPrimitives(rsc);
 
     // Count the number of gl attrs to initialize
     mAttribCount = 0;
     for (uint32_t ct=0; ct < mRSMesh->mHal.state.vertexBuffersCount; ct++) {
         const Element *elem = mRSMesh->mHal.state.vertexBuffers[ct]->getType()->getElement();
-        for (uint32_t ct=0; ct < elem->getFieldCount(); ct++) {
+        for (uint32_t ct=0; ct < elem->mHal.state.fieldsCount; ct++) {
             if (isValidGLComponent(elem, ct)) {
                 mAttribCount ++;
             }
@@ -104,21 +99,21 @@
     uint32_t userNum = 0;
     for (uint32_t ct=0; ct < mRSMesh->mHal.state.vertexBuffersCount; ct++) {
         const Element *elem = mRSMesh->mHal.state.vertexBuffers[ct]->getType()->getElement();
-        uint32_t stride = elem->getSizeBytes();
-        for (uint32_t fieldI=0; fieldI < elem->getFieldCount(); fieldI++) {
-            const Component &c = elem->getField(fieldI)->getComponent();
+        uint32_t stride = elem->mHal.state.elementSizeBytes;
+        for (uint32_t fieldI=0; fieldI < elem->mHal.state.fieldsCount; fieldI++) {
+            const Element *f = elem->mHal.state.fields[fieldI];
 
             if (!isValidGLComponent(elem, fieldI)) {
                 continue;
             }
 
-            mAttribs[userNum].size = c.getVectorSize();
-            mAttribs[userNum].offset = elem->getFieldOffsetBytes(fieldI);
-            mAttribs[userNum].type = rsdTypeToGLType(c.getType());
-            mAttribs[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized();
+            mAttribs[userNum].size = f->mHal.state.vectorSize;
+            mAttribs[userNum].offset = elem->mHal.state.fieldOffsetBytes[fieldI];
+            mAttribs[userNum].type = rsdTypeToGLType(f->mHal.state.dataType);
+            mAttribs[userNum].normalized = f->mHal.state.dataType != RS_TYPE_FLOAT_32;
             mAttribs[userNum].stride = stride;
             String8 tmp(RS_SHADER_ATTR);
-            tmp.append(elem->getFieldName(fieldI));
+            tmp.append(elem->mHal.state.fieldNames[fieldI]);
             mAttribs[userNum].name.setTo(tmp.string());
 
             // Remember which allocation this attribute came from
@@ -133,7 +128,7 @@
 void RsdMeshObj::renderPrimitiveRange(const Context *rsc, uint32_t primIndex,
                                       uint32_t start, uint32_t len) const {
     if (len < 1 || primIndex >= mRSMesh->mHal.state.primitivesCount || mAttribCount == 0) {
-        ALOGE("Invalid mesh or parameters");
+        rsc->setError(RS_ERROR_FATAL_DRIVER, "Invalid mesh or parameters");
         return;
     }
 
@@ -186,7 +181,7 @@
     rsdGLCheckError(rsc, "Mesh::renderPrimitiveRange");
 }
 
-void RsdMeshObj::updateGLPrimitives() {
+void RsdMeshObj::updateGLPrimitives(const Context *rsc) {
     mGLPrimitives = new uint32_t[mRSMesh->mHal.state.primitivesCount];
     for (uint32_t i = 0; i < mRSMesh->mHal.state.primitivesCount; i ++) {
         switch (mRSMesh->mHal.state.primitives[i]) {
@@ -196,6 +191,7 @@
             case RS_PRIMITIVE_TRIANGLE:       mGLPrimitives[i] = GL_TRIANGLES; break;
             case RS_PRIMITIVE_TRIANGLE_STRIP: mGLPrimitives[i] = GL_TRIANGLE_STRIP; break;
             case RS_PRIMITIVE_TRIANGLE_FAN:   mGLPrimitives[i] = GL_TRIANGLE_FAN; break;
+            default: rsc->setError(RS_ERROR_FATAL_DRIVER, "Invalid mesh primitive"); break;
         }
     }
 }
diff --git a/libs/rs/driver/rsdMeshObj.h b/libs/rs/driver/rsdMeshObj.h
index 8b1271b..1370f01 100644
--- a/libs/rs/driver/rsdMeshObj.h
+++ b/libs/rs/driver/rsdMeshObj.h
@@ -37,15 +37,16 @@
             const android::renderscript::Mesh *);
     ~RsdMeshObj();
 
-    void renderPrimitiveRange(const android::renderscript::Context *, uint32_t primIndex, uint32_t start, uint32_t len) const;
+    void renderPrimitiveRange(const android::renderscript::Context *,
+                              uint32_t primIndex, uint32_t start, uint32_t len) const;
 
-    bool init();
+    bool init(const android::renderscript::Context *rsc);
 
 protected:
     const android::renderscript::Mesh *mRSMesh;
 
     uint32_t *mGLPrimitives;
-    void updateGLPrimitives();
+    void updateGLPrimitives(const android::renderscript::Context *rsc);
 
     bool isValidGLComponent(const android::renderscript::Element *elem, uint32_t fieldIdx);
     // Attribues that allow us to map to GL
diff --git a/libs/rs/driver/rsdPath.cpp b/libs/rs/driver/rsdPath.cpp
new file mode 100644
index 0000000..e04bc02
--- /dev/null
+++ b/libs/rs/driver/rsdPath.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <GLES/glext.h>
+
+#include <rs_hal.h>
+#include <rsContext.h>
+#include <rsPath.h>
+
+#include "rsdCore.h"
+#include "rsdPath.h"
+#include "rsdAllocation.h"
+#include "rsdGL.h"
+#include "rsdVertexArray.h"
+#include "rsdShaderCache.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+class DrvPath {
+protected:
+    DrvPath();
+public:
+    virtual ~DrvPath();
+    virtual void draw(Context *) = 0;
+};
+
+class DrvPathStatic : public DrvPath {
+public:
+    typedef struct {
+        float x1, xc, x2;
+        float y1, yc, y2;
+    } segment_t;
+
+    segment_t *mSegments;
+    uint32_t mSegmentCount;
+
+    DrvPathStatic(const Allocation *vtx, const Allocation *loops);
+    virtual ~DrvPathStatic();
+
+    virtual void draw(Context *);
+};
+
+class DrvPathDynamic : public DrvPath {
+public:
+    DrvPathDynamic();
+    virtual ~DrvPathDynamic();
+};
+
+static void cleanup(const Context *rsc, const Path *m) {
+    DrvPath *dp = (DrvPath *)m->mHal.drv;
+    if (dp) {
+        delete dp;
+    }
+}
+
+bool rsdPathInitStatic(const Context *rsc, const Path *m,
+                       const Allocation *vtx, const Allocation *loops) {
+    DrvPathStatic *drv = NULL;
+    cleanup(rsc, m);
+
+    DrvPathStatic *dps = new DrvPathStatic(vtx, loops);
+    //LOGE("init path m %p,  %p", m, dps);
+    m->mHal.drv = dps;
+    return dps != NULL;
+}
+
+bool rsdPathInitDynamic(const Context *rsc, const Path *m) {
+    return false;
+}
+
+
+void rsdPathDraw(const Context *rsc, const Path *m) {
+    //LOGE("render m=%p", m);
+
+    DrvPath *drv = (DrvPath *)m->mHal.drv;
+    if(drv) {
+        //LOGE("render 2 drv=%p", drv);
+        drv->draw((Context *)rsc);
+    }
+}
+
+void rsdPathDestroy(const Context *rsc, const Path *m) {
+    cleanup(rsc, m);
+    m->mHal.drv = NULL;
+}
+
+
+
+
+DrvPath::DrvPath() {
+}
+
+DrvPath::~DrvPath() {
+}
+
+DrvPathStatic::DrvPathStatic(const Allocation *vtx, const Allocation *loops) {
+    mSegmentCount = vtx->getType()->getDimX() / 3;
+    mSegments = new segment_t[mSegmentCount];
+
+    const float *fin = (const float *)vtx->getPtr();
+    for (uint32_t ct=0; ct < mSegmentCount; ct++) {
+        segment_t *s = &mSegments[ct];
+        s->x1 = fin[0];
+        s->y1 = fin[1];
+
+        s->xc = fin[2];
+        s->yc = fin[3];
+
+        s->x2 = fin[4];
+        s->y2 = fin[5];
+        fin += 6;
+    }
+}
+
+DrvPathStatic::~DrvPathStatic() {
+}
+
+void DrvPathStatic::draw(Context *rsc) {
+    const static float color[24] = {
+        1.f, 0.f, 0.f, 1.f,  0.5f, 0.f, 0.f, 1.f,
+        1.f, 0.f, 0.f, 1.f,  0.5f, 0.f, 0.f, 1.f,
+        1.f, 1.f, 1.f, 1.f,  1.f, 1.f, 1.f, 1.f};
+    float vtx[12];
+
+    //LOGE("draw");
+    if (!rsc->setupCheck()) {
+        return;
+    }
+
+    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+    if (!dc->gl.shaderCache->setup(rsc)) {
+        return;
+    }
+
+    RsdVertexArray::Attrib attribs[2];
+    attribs[0].set(GL_FLOAT, 2, 8, false, (uint32_t)vtx, "ATTRIB_position");
+    attribs[1].set(GL_FLOAT, 4, 16, false, (uint32_t)color, "ATTRIB_color");
+    RsdVertexArray va(attribs, 2);
+    va.setup(rsc);
+
+    //LOGE("mSegmentCount %i", mSegmentCount);
+    for (uint32_t ct=0; ct < mSegmentCount; ct++) {
+        segment_t *s = &mSegments[ct];
+
+        vtx[0] = s->x1;
+        vtx[1] = s->y1;
+        vtx[2] = s->xc;
+        vtx[3] = s->yc;
+
+        vtx[4] = s->x2;
+        vtx[5] = s->y2;
+        vtx[6] = s->xc;
+        vtx[7] = s->yc;
+
+        vtx[8] = s->x1;
+        vtx[9] = s->y1;
+        vtx[10] = s->x2;
+        vtx[11] = s->y2;
+
+        RSD_CALL_GL(glDrawArrays, GL_LINES, 0, 6);
+    }
+
+}
+
+DrvPathDynamic::DrvPathDynamic() {
+}
+
+DrvPathDynamic::~DrvPathDynamic() {
+}
diff --git a/libs/rs/driver/rsdPath.h b/libs/rs/driver/rsdPath.h
new file mode 100644
index 0000000..fa00972
--- /dev/null
+++ b/libs/rs/driver/rsdPath.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef RSD_PATH_H
+#define RSD_PATH_H
+
+#include <rs_hal.h>
+
+
+bool rsdPathInitStatic(const android::renderscript::Context *rsc,
+                       const android::renderscript::Path *m,
+                       const android::renderscript::Allocation *vertex,
+                       const android::renderscript::Allocation *loops);
+bool rsdPathInitDynamic(const android::renderscript::Context *rsc,
+                        const android::renderscript::Path *m);
+void rsdPathDraw(const android::renderscript::Context *rsc,
+                 const android::renderscript::Path *m);
+void rsdPathDestroy(const android::renderscript::Context *rsc,
+                    const android::renderscript::Path *m);
+
+
+#endif
diff --git a/libs/rs/driver/rsdProgramRaster.cpp b/libs/rs/driver/rsdProgramRaster.cpp
index b493759..e5a0291 100644
--- a/libs/rs/driver/rsdProgramRaster.cpp
+++ b/libs/rs/driver/rsdProgramRaster.cpp
@@ -45,6 +45,9 @@
         case RS_CULL_NONE:
             RSD_CALL_GL(glDisable, GL_CULL_FACE);
             break;
+        default:
+            rsc->setError(RS_ERROR_FATAL_DRIVER, "Invalid cull type");
+            break;
     }
 
 }
diff --git a/libs/rs/driver/rsdProgramStore.cpp b/libs/rs/driver/rsdProgramStore.cpp
index fca9ba9..c1295e8 100644
--- a/libs/rs/driver/rsdProgramStore.cpp
+++ b/libs/rs/driver/rsdProgramStore.cpp
@@ -111,7 +111,7 @@
         drv->blendSrc = GL_SRC_ALPHA_SATURATE;
         break;
     default:
-        ALOGE("Unknown blend src mode.");
+        rsc->setError(RS_ERROR_FATAL_DRIVER, "Unknown blend src mode.");
         goto error;
     }
 
@@ -141,7 +141,7 @@
         drv->blendDst = GL_ONE_MINUS_DST_ALPHA;
         break;
     default:
-        ALOGE("Unknown blend dst mode.");
+        rsc->setError(RS_ERROR_FATAL_DRIVER, "Unknown blend dst mode.");
         goto error;
     }
 
diff --git a/libs/rs/driver/rsdRuntimeStubs.cpp b/libs/rs/driver/rsdRuntimeStubs.cpp
index 14c2970..44bfb1c 100644
--- a/libs/rs/driver/rsdRuntimeStubs.cpp
+++ b/libs/rs/driver/rsdRuntimeStubs.cpp
@@ -25,6 +25,7 @@
 #include "rsdCore.h"
 
 #include "rsdRuntime.h"
+#include "rsdPath.h"
 
 #include <time.h>
 
@@ -89,6 +90,16 @@
     rsrBindTexture(rsc, sc, pf, slot, a);
 }
 
+static void SC_BindVertexConstant(ProgramVertex *pv, uint32_t slot, Allocation *a) {
+    GET_TLS();
+    rsrBindConstant(rsc, sc, pv, slot, a);
+}
+
+static void SC_BindFragmentConstant(ProgramFragment *pf, uint32_t slot, Allocation *a) {
+    GET_TLS();
+    rsrBindConstant(rsc, sc, pf, slot, a);
+}
+
 static void SC_BindSampler(ProgramFragment *pf, uint32_t slot, Sampler *s) {
     GET_TLS();
     rsrBindSampler(rsc, sc, pf, slot, s);
@@ -204,6 +215,12 @@
     rsrDrawRect(rsc, sc, x1, y1, x2, y2, z);
 }
 
+static void SC_DrawPath(Path *p) {
+    GET_TLS();
+    //rsrDrawPath(rsc, sc, p);
+    rsdPathDraw(rsc, p);
+}
+
 static void SC_DrawMesh(Mesh *m) {
     GET_TLS();
     rsrDrawMesh(rsc, sc, m);
@@ -533,6 +550,10 @@
     { "_Z13rsClearObjectP9rs_script", (void *)&SC_ClearObject, true },
     { "_Z10rsIsObject9rs_script", (void *)&SC_IsObject, true },
 
+    { "_Z11rsSetObjectP7rs_pathS_", (void *)&SC_SetObject, true },
+    { "_Z13rsClearObjectP7rs_path", (void *)&SC_ClearObject, true },
+    { "_Z10rsIsObject7rs_path", (void *)&SC_IsObject, true },
+
     { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_SetObject, true },
     { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_ClearObject, true },
     { "_Z10rsIsObject7rs_mesh", (void *)&SC_IsObject, true },
@@ -580,6 +601,8 @@
     { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_BindProgramRaster, false },
     { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_BindSampler, false },
     { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_BindTexture, false },
+    { "_Z15rsgBindConstant19rs_program_fragmentj13rs_allocation", (void *)&SC_BindFragmentConstant, false },
+    { "_Z15rsgBindConstant17rs_program_vertexj13rs_allocation", (void *)&SC_BindVertexConstant, false },
 
     { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_VpLoadProjectionMatrix, false },
     { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_VpLoadModelMatrix, false },
@@ -603,6 +626,8 @@
     { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_DrawMeshPrimitiveRange, false },
     { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_MeshComputeBoundingBox, false },
 
+    { "_Z11rsgDrawPath7rs_path", (void *)&SC_DrawPath, false },
+
     { "_Z13rsgClearColorffff", (void *)&SC_ClearColor, false },
     { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth, false },
 
diff --git a/libs/rs/driver/rsdShader.cpp b/libs/rs/driver/rsdShader.cpp
index 056a33a..3bca794 100644
--- a/libs/rs/driver/rsdShader.cpp
+++ b/libs/rs/driver/rsdShader.cpp
@@ -90,12 +90,12 @@
     String8 s;
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
         const Element *e = mRSProgram->mHal.state.inputElements[ct];
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
+        for (uint32_t field=0; field < e->mHal.state.fieldsCount; field++) {
+            const Element *f = e->mHal.state.fields[field];
 
             // Cannot be complex
-            rsAssert(!f->getFieldCount());
-            switch (f->getComponent().getVectorSize()) {
+            rsAssert(!f->mHal.state.fieldsCount);
+            switch (f->mHal.state.vectorSize) {
             case 1: s.append("attribute float ATTRIB_"); break;
             case 2: s.append("attribute vec2 ATTRIB_"); break;
             case 3: s.append("attribute vec3 ATTRIB_"); break;
@@ -104,7 +104,7 @@
                 rsAssert(0);
             }
 
-            s.append(e->getFieldName(field));
+            s.append(e->mHal.state.fieldNames[field]);
             s.append(";\n");
         }
     }
@@ -114,17 +114,13 @@
 void RsdShader::appendAttributes() {
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
         const Element *e = mRSProgram->mHal.state.inputElements[ct];
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            const char *fn = e->getFieldName(field);
-
-            if (fn[0] == '#') {
-                continue;
-            }
+        for (uint32_t field=0; field < e->mHal.state.fieldsCount; field++) {
+            const Element *f = e->mHal.state.fields[field];
+            const char *fn = e->mHal.state.fieldNames[field];
 
             // Cannot be complex
-            rsAssert(!f->getFieldCount());
-            switch (f->getComponent().getVectorSize()) {
+            rsAssert(!f->mHal.state.fieldsCount);
+            switch (f->mHal.state.vectorSize) {
             case 1: mShader.append("attribute float ATTRIB_"); break;
             case 2: mShader.append("attribute vec2 ATTRIB_"); break;
             case 3: mShader.append("attribute vec3 ATTRIB_"); break;
@@ -215,24 +211,20 @@
 void RsdShader::appendUserConstants() {
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
         const Element *e = mRSProgram->mHal.state.constantTypes[ct]->getElement();
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            const char *fn = e->getFieldName(field);
-
-            if (fn[0] == '#') {
-                continue;
-            }
+        for (uint32_t field=0; field < e->mHal.state.fieldsCount; field++) {
+            const Element *f = e->mHal.state.fields[field];
+            const char *fn = e->mHal.state.fieldNames[field];
 
             // Cannot be complex
-            rsAssert(!f->getFieldCount());
-            if (f->getType() == RS_TYPE_MATRIX_4X4) {
+            rsAssert(!f->mHal.state.fieldsCount);
+            if (f->mHal.state.dataType == RS_TYPE_MATRIX_4X4) {
                 mShader.append("uniform mat4 UNI_");
-            } else if (f->getType() == RS_TYPE_MATRIX_3X3) {
+            } else if (f->mHal.state.dataType == RS_TYPE_MATRIX_3X3) {
                 mShader.append("uniform mat3 UNI_");
-            } else if (f->getType() == RS_TYPE_MATRIX_2X2) {
+            } else if (f->mHal.state.dataType == RS_TYPE_MATRIX_2X2) {
                 mShader.append("uniform mat2 UNI_");
             } else {
-                switch (f->getComponent().getVectorSize()) {
+                switch (f->mHal.state.vectorSize) {
                 case 1: mShader.append("uniform float UNI_"); break;
                 case 2: mShader.append("uniform vec2 UNI_"); break;
                 case 3: mShader.append("uniform vec3 UNI_"); break;
@@ -243,8 +235,8 @@
             }
 
             mShader.append(fn);
-            if (e->getFieldArraySize(field) > 1) {
-                mShader.appendFormat("[%d]", e->getFieldArraySize(field));
+            if (e->mHal.state.fieldArraySizes[field] > 1) {
+                mShader.appendFormat("[%d]", e->mHal.state.fieldArraySizes[field]);
             }
             mShader.append(";\n");
         }
@@ -252,8 +244,8 @@
 }
 
 void RsdShader::logUniform(const Element *field, const float *fd, uint32_t arraySize ) {
-    RsDataType dataType = field->getType();
-    uint32_t elementSize = field->getSizeBytes() / sizeof(float);
+    RsDataType dataType = field->mHal.state.dataType;
+    uint32_t elementSize = field->mHal.state.elementSizeBytes / sizeof(float);
     for (uint32_t i = 0; i < arraySize; i ++) {
         if (arraySize > 1) {
             ALOGV("Array Element [%u]", i);
@@ -274,7 +266,7 @@
             ALOGV("{%f, %f",  fd[0], fd[2]);
             ALOGV(" %f, %f}", fd[1], fd[3]);
         } else {
-            switch (field->getComponent().getVectorSize()) {
+            switch (field->mHal.state.vectorSize) {
             case 1:
                 ALOGV("Uniform 1 = %f", fd[0]);
                 break;
@@ -299,7 +291,7 @@
 
 void RsdShader::setUniform(const Context *rsc, const Element *field, const float *fd,
                          int32_t slot, uint32_t arraySize ) {
-    RsDataType dataType = field->getType();
+    RsDataType dataType = field->mHal.state.dataType;
     if (dataType == RS_TYPE_MATRIX_4X4) {
         RSD_CALL_GL(glUniformMatrix4fv, slot, arraySize, GL_FALSE, fd);
     } else if (dataType == RS_TYPE_MATRIX_3X3) {
@@ -307,7 +299,7 @@
     } else if (dataType == RS_TYPE_MATRIX_2X2) {
         RSD_CALL_GL(glUniformMatrix2fv, slot, arraySize, GL_FALSE, fd);
     } else {
-        switch (field->getComponent().getVectorSize()) {
+        switch (field->mHal.state.vectorSize) {
         case 1:
             RSD_CALL_GL(glUniform1fv, slot, arraySize, fd);
             break;
@@ -462,15 +454,11 @@
 
         const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
         const Element *e = mRSProgram->mHal.state.constantTypes[ct]->getElement();
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            const char *fieldName = e->getFieldName(field);
-            // If this field is padding, skip it
-            if (fieldName[0] == '#') {
-                continue;
-            }
+        for (uint32_t field=0; field < e->mHal.state.fieldsCount; field++) {
+            const Element *f = e->mHal.state.fields[field];
+            const char *fieldName = e->mHal.state.fieldNames[field];
 
-            uint32_t offset = e->getFieldOffsetBytes(field);
+            uint32_t offset = e->mHal.state.fieldOffsetBytes[field];
             const float *fd = reinterpret_cast<const float *>(&data[offset]);
 
             int32_t slot = -1;
@@ -509,22 +497,13 @@
     mAttribCount = 0;
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
         const Element *elem = mRSProgram->mHal.state.inputElements[ct];
-        for (uint32_t field=0; field < elem->getFieldCount(); field++) {
-            if (elem->getFieldName(field)[0] != '#') {
-                mAttribCount ++;
-            }
-        }
+        mAttribCount += elem->mHal.state.fieldsCount;
     }
 
     mUniformCount = 0;
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
         const Element *elem = mRSProgram->mHal.state.constantTypes[ct]->getElement();
-
-        for (uint32_t field=0; field < elem->getFieldCount(); field++) {
-            if (elem->getFieldName(field)[0] != '#') {
-                mUniformCount ++;
-            }
-        }
+        mUniformCount += elem->mHal.state.fieldsCount;
     }
     mUniformCount += mRSProgram->mHal.state.texturesCount;
 
@@ -544,17 +523,17 @@
 
 void RsdShader::initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths,
                                    uint32_t *count, const char *prefix) {
-    rsAssert(e->getFieldCount());
-    for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
-        const Element *ce = e->getField(ct);
-        if (ce->getFieldCount()) {
+    rsAssert(e->mHal.state.fieldsCount);
+    for (uint32_t ct=0; ct < e->mHal.state.fieldsCount; ct++) {
+        const Element *ce = e->mHal.state.fields[ct];
+        if (ce->mHal.state.fieldsCount) {
             initAddUserElement(ce, names, arrayLengths, count, prefix);
-        } else if (e->getFieldName(ct)[0] != '#') {
+        } else {
             String8 tmp(prefix);
-            tmp.append(e->getFieldName(ct));
+            tmp.append(e->mHal.state.fieldNames[ct]);
             names[*count].setTo(tmp.string());
             if (arrayLengths) {
-                arrayLengths[*count] = e->getFieldArraySize(ct);
+                arrayLengths[*count] = e->mHal.state.fieldArraySizes[ct];
             }
             (*count)++;
         }
diff --git a/libs/rs/driver/rsdShaderCache.h b/libs/rs/driver/rsdShaderCache.h
index d64780b..0beecae 100644
--- a/libs/rs/driver/rsdShaderCache.h
+++ b/libs/rs/driver/rsdShaderCache.h
@@ -98,7 +98,8 @@
     struct ProgramEntry {
         ProgramEntry(uint32_t numVtxAttr, uint32_t numVtxUnis,
                      uint32_t numFragUnis) : vtx(0), frag(0), program(0), vtxAttrCount(0),
-                                             vtxAttrs(0), vtxUniforms(0), fragUniforms(0) {
+                                             vtxAttrs(0), vtxUniforms(0), fragUniforms(0),
+                                             fragUniformIsSTO(0) {
             vtxAttrCount = numVtxAttr;
             if (numVtxAttr) {
                 vtxAttrs = new AttrData[numVtxAttr];
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 09a2986..6759bc7 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -42,6 +42,7 @@
     param RsType vtype
     param RsAllocationMipmapControl mips
     param uint32_t usages
+    param uint32_t ptr
     ret RsAllocation
 }
 
@@ -392,3 +393,13 @@
 	param uint32_t *primType
 	ret RsMesh
 	}
+
+PathCreate {
+    param RsPathPrimitive pp
+    param bool isStatic
+    param RsAllocation vertex
+    param RsAllocation loops
+    param float quality
+    ret RsPath
+    }
+
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 5f45abf..02c6809 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -22,21 +22,22 @@
 using namespace android::renderscript;
 
 Allocation::Allocation(Context *rsc, const Type *type, uint32_t usages,
-                       RsAllocationMipmapControl mc)
+                       RsAllocationMipmapControl mc, void * ptr)
     : ObjectBase(rsc) {
 
     memset(&mHal, 0, sizeof(mHal));
     mHal.state.mipmapControl = RS_ALLOCATION_MIPMAP_NONE;
     mHal.state.usageFlags = usages;
     mHal.state.mipmapControl = mc;
+    mHal.state.usrPtr = ptr;
 
     setType(type);
     updateCache();
 }
 
 Allocation * Allocation::createAllocation(Context *rsc, const Type *type, uint32_t usages,
-                              RsAllocationMipmapControl mc) {
-    Allocation *a = new Allocation(rsc, type, usages, mc);
+                              RsAllocationMipmapControl mc, void * ptr) {
+    Allocation *a = new Allocation(rsc, type, usages, mc, ptr);
 
     if (!rsc->mHal.funcs.allocation.init(rsc, a, type->getElement()->getHasReferences())) {
         rsc->setError(RS_ERROR_FATAL_DRIVER, "Allocation::Allocation, alloc failure");
@@ -577,8 +578,8 @@
 
 RsAllocation rsi_AllocationCreateTyped(Context *rsc, RsType vtype,
                                        RsAllocationMipmapControl mips,
-                                       uint32_t usages) {
-    Allocation * alloc = Allocation::createAllocation(rsc, static_cast<Type *>(vtype), usages, mips);
+                                       uint32_t usages, uint32_t ptr) {
+    Allocation * alloc = Allocation::createAllocation(rsc, static_cast<Type *>(vtype), usages, mips, (void *)ptr);
     if (!alloc) {
         return NULL;
     }
@@ -591,7 +592,7 @@
                                             const void *data, size_t data_length, uint32_t usages) {
     Type *t = static_cast<Type *>(vtype);
 
-    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages);
+    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages, 0);
     Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
     if (texAlloc == NULL) {
         ALOGE("Memory allocation failure");
@@ -615,7 +616,7 @@
     // Cubemap allocation's faces should be Width by Width each.
     // Source data should have 6 * Width by Width pixels
     // Error checking is done in the java layer
-    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages);
+    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages, 0);
     Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
     if (texAlloc == NULL) {
         ALOGE("Memory allocation failure");
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index a26d835..58a582b 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -68,7 +68,8 @@
     Hal mHal;
 
     static Allocation * createAllocation(Context *rsc, const Type *, uint32_t usages,
-                                  RsAllocationMipmapControl mc = RS_ALLOCATION_MIPMAP_NONE);
+                                         RsAllocationMipmapControl mc = RS_ALLOCATION_MIPMAP_NONE,
+                                         void *ptr = 0);
     virtual ~Allocation();
     void updateCache();
 
@@ -137,7 +138,7 @@
 
 private:
     void freeChildrenUnlocked();
-    Allocation(Context *rsc, const Type *, uint32_t usages, RsAllocationMipmapControl mc);
+    Allocation(Context *rsc, const Type *, uint32_t usages, RsAllocationMipmapControl mc, void *ptr);
 
     uint32_t getPackedSize() const;
     static void writePackedData(const Type *type, uint8_t *dst, const uint8_t *src, bool dstPadded);
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
index 21b98f6..9c2c200 100644
--- a/libs/rs/rsComponent.cpp
+++ b/libs/rs/rsComponent.cpp
@@ -62,6 +62,7 @@
         rsAssert(mNormalized == true);
         break;
     default:
+        rsAssert(mKind != RS_KIND_INVALID);
         break;
     }
 
@@ -167,6 +168,9 @@
     case RS_TYPE_BOOLEAN:
         mTypeBits = 8;
         break;
+    default:
+        rsAssert(mType != RS_TYPE_INVALID);
+        break;
     }
 
     mBitsUnpadded = mTypeBits * mVectorSize;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 1a34bd5..adaefc6 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -361,6 +361,7 @@
 #else
     setpriority(PRIO_PROCESS, mNativeThreadId, p);
 #endif
+    mHal.funcs.setPriority(this, mThreadPriority);
 }
 
 Context::Context() {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index a844a20..05c799e 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -32,6 +32,7 @@
 #include "rsAdapter.h"
 #include "rsSampler.h"
 #include "rsFont.h"
+#include "rsPath.h"
 #include "rsProgramFragment.h"
 #include "rsProgramStore.h"
 #include "rsProgramRaster.h"
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index dff9585..fb2892c 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -27,6 +27,7 @@
     mFields = NULL;
     mFieldCount = 0;
     mHasReference = false;
+    memset(&mHal, 0, sizeof(mHal));
 }
 
 Element::~Element() {
@@ -47,6 +48,12 @@
     mFields = NULL;
     mFieldCount = 0;
     mHasReference = false;
+
+    delete [] mHal.state.fields;
+    delete [] mHal.state.fieldArraySizes;
+    delete [] mHal.state.fieldNames;
+    delete [] mHal.state.fieldNameLengths;
+    delete [] mHal.state.fieldOffsetBytes;
 }
 
 size_t Element::getSizeBits() const {
@@ -157,16 +164,36 @@
 }
 
 void Element::compute() {
+    mHal.state.dataType = mComponent.getType();
+    mHal.state.dataKind = mComponent.getKind();
+    mHal.state.vectorSize = mComponent.getVectorSize();
+
     if (mFieldCount == 0) {
         mBits = mComponent.getBits();
         mBitsUnpadded = mComponent.getBitsUnpadded();
         mHasReference = mComponent.isReference();
+
+        mHal.state.elementSizeBytes = getSizeBytes();
         return;
     }
 
+    uint32_t noPaddingFieldCount = 0;
+    for (uint32_t ct = 0; ct < mFieldCount; ct ++) {
+        if (mFields[ct].name.string()[0] != '#') {
+            noPaddingFieldCount ++;
+        }
+    }
+
+    mHal.state.fields = new const Element*[noPaddingFieldCount];
+    mHal.state.fieldArraySizes = new uint32_t[noPaddingFieldCount];
+    mHal.state.fieldNames = new const char*[noPaddingFieldCount];
+    mHal.state.fieldNameLengths = new uint32_t[noPaddingFieldCount];
+    mHal.state.fieldOffsetBytes = new uint32_t[noPaddingFieldCount];
+    mHal.state.fieldsCount = noPaddingFieldCount;
+
     size_t bits = 0;
     size_t bitsUnpadded = 0;
-    for (size_t ct=0; ct < mFieldCount; ct++) {
+    for (size_t ct = 0, ctNoPadding = 0; ct < mFieldCount; ct++) {
         mFields[ct].offsetBits = bits;
         mFields[ct].offsetBitsUnpadded = bitsUnpadded;
         bits += mFields[ct].e->getSizeBits() * mFields[ct].arraySize;
@@ -175,8 +202,21 @@
         if (mFields[ct].e->mHasReference) {
             mHasReference = true;
         }
+
+        if (mFields[ct].name.string()[0] == '#') {
+            continue;
+        }
+
+        mHal.state.fields[ctNoPadding] = mFields[ct].e.get();
+        mHal.state.fieldArraySizes[ctNoPadding] = mFields[ct].arraySize;
+        mHal.state.fieldNames[ctNoPadding] = mFields[ct].name.string();
+        mHal.state.fieldNameLengths[ctNoPadding] = mFields[ct].name.length() + 1; // to include 0
+        mHal.state.fieldOffsetBytes[ctNoPadding] = mFields[ct].offsetBits >> 3;
+
+        ctNoPadding ++;
     }
 
+    mHal.state.elementSizeBytes = getSizeBytes();
 }
 
 ObjectBaseRef<const Element> Element::createRef(Context *rsc, RsDataType dt, RsDataKind dk,
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 04010fa..4b6b460 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -24,10 +24,38 @@
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-
+/*****************************************************************************
+ * CAUTION
+ *
+ * Any layout changes for this class may require a corresponding change to be
+ * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains
+ * a partial copy of the information below.
+ *
+ *****************************************************************************/
 // An element is a group of Components that occupies one cell in a structure.
 class Element : public ObjectBase {
 public:
+    struct Hal {
+        mutable void *drv;
+
+        struct State {
+            RsDataType dataType;
+            RsDataKind dataKind;
+            uint32_t vectorSize;
+            uint32_t elementSizeBytes;
+
+            // Subelements
+            const Element **fields;
+            uint32_t *fieldArraySizes;
+            const char **fieldNames;
+            uint32_t *fieldNameLengths;
+            uint32_t *fieldOffsetBytes;
+            uint32_t fieldsCount;
+        };
+        State state;
+    };
+    Hal mHal;
+
     class Builder {
     public:
         Builder();
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
index 0fc73fb..166b5d3 100644
--- a/libs/rs/rsMesh.h
+++ b/libs/rs/rsMesh.h
@@ -23,20 +23,18 @@
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-
+/*****************************************************************************
+ * CAUTION
+ *
+ * Any layout changes for this class may require a corresponding change to be
+ * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains
+ * a partial copy of the information below.
+ *
+ *****************************************************************************/
 
 // An element is a group of Components that occupies one cell in a structure.
 class Mesh : public ObjectBase {
 public:
-    Mesh(Context *);
-    Mesh(Context *, uint32_t vertexBuffersCount, uint32_t primitivesCount);
-    ~Mesh();
-
-    virtual void serialize(OStream *stream) const;
-    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; }
-    static Mesh *createFromStream(Context *rsc, IStream *stream);
-    void init();
-
     struct Hal {
         mutable void *drv;
 
@@ -57,6 +55,15 @@
     };
     Hal mHal;
 
+    Mesh(Context *);
+    Mesh(Context *, uint32_t vertexBuffersCount, uint32_t primitivesCount);
+    ~Mesh();
+
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; }
+    static Mesh *createFromStream(Context *rsc, IStream *stream);
+    void init();
+
     void setVertexBuffer(Allocation *vb, uint32_t index) {
         mVertexBuffers[index].set(vb);
         mHal.state.vertexBuffers[index] = vb;
diff --git a/libs/rs/rsPath.cpp b/libs/rs/rsPath.cpp
new file mode 100644
index 0000000..c4f4978
--- /dev/null
+++ b/libs/rs/rsPath.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Path::Path(Context *rsc) : ObjectBase(rsc) {
+}
+
+Path::Path(Context *rsc, RsPathPrimitive pp, bool isStatic,
+                      Allocation *vtx, Allocation *loops, float quality)
+: ObjectBase(rsc) {
+
+    memset(&mHal, 0, sizeof(mHal));
+    mHal.state.quality = quality;
+    mHal.state.primitive = pp;
+
+    //LOGE("i1");
+    rsc->mHal.funcs.path.initStatic(rsc, this, vtx, loops);
+
+    //LOGE("i2");
+}
+
+Path::Path(Context *rsc, uint32_t vertexBuffersCount, uint32_t primitivesCount)
+: ObjectBase(rsc) {
+
+}
+
+Path::~Path() {
+
+}
+
+
+void Path::rasterize(const BezierSegment_t *s, uint32_t num, Allocation *alloc) {
+
+    for (uint32_t i=0; i < num; i++) {
+
+    }
+
+}
+
+void Path::render(Context *rsc) {
+}
+
+void Path::serialize(OStream *stream) const {
+
+}
+
+RsA3DClassID Path::getClassId() const {
+    return RS_A3D_CLASS_ID_UNKNOWN;
+}
+
+namespace android {
+namespace renderscript {
+
+RsPath rsi_PathCreate(Context *rsc, RsPathPrimitive pp, bool isStatic,
+                      RsAllocation vtx, RsAllocation loops, float quality) {
+    return new Path(rsc, pp, isStatic, (Allocation *)vtx, (Allocation *)loops, quality);
+}
+
+}
+}
diff --git a/libs/rs/rsPath.h b/libs/rs/rsPath.h
new file mode 100644
index 0000000..dac795e
--- /dev/null
+++ b/libs/rs/rsPath.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ANDROID_RS_PATH_H
+#define ANDROID_RS_PATH_H
+
+
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Path : public ObjectBase {
+public:
+    struct {
+        mutable void * drv;
+
+        struct State {
+            RsPathPrimitive primitive;
+            float quality;
+        };
+        State state;
+    } mHal;
+
+    Path(Context *);
+    Path(Context *, uint32_t vertexBuffersCount, uint32_t primitivesCount);
+    Path(Context *, RsPathPrimitive pp, bool isStatic, Allocation *vtx, Allocation *loop, float q);
+
+    ~Path();
+
+    void render(Context *);
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const;
+
+private:
+
+
+    typedef struct {
+        float x[4];
+        float y[4];
+    } BezierSegment_t;
+
+    bool subdivideCheck(const BezierSegment_t *s, float u1, float u2);
+
+    void rasterize(const BezierSegment_t *s, uint32_t num, Allocation *alloc);
+
+
+};
+
+}
+}
+#endif //ANDROID_RS_PATH_H
+
+
+
diff --git a/libs/rs/rsRuntime.h b/libs/rs/rsRuntime.h
index cb962a8..3bded62 100644
--- a/libs/rs/rsRuntime.h
+++ b/libs/rs/rsRuntime.h
@@ -30,6 +30,8 @@
 //////////////////////////////////////////////////////////////////////////////
 
 void rsrBindTexture(Context *, Script *, ProgramFragment *, uint32_t slot, Allocation *);
+void rsrBindConstant(Context *, Script *, ProgramFragment *, uint32_t slot, Allocation *);
+void rsrBindConstant(Context *, Script *, ProgramVertex*, uint32_t slot, Allocation *);
 void rsrBindSampler(Context *, Script *, ProgramFragment *, uint32_t slot, Sampler *);
 void rsrBindProgramStore(Context *, Script *, ProgramStore *);
 void rsrBindProgramFragment(Context *, Script *, ProgramFragment *);
@@ -68,6 +70,7 @@
 void rsrDrawSpriteScreenspace(Context *, Script *,
                               float x, float y, float z, float w, float h);
 void rsrDrawRect(Context *, Script *, float x1, float y1, float x2, float y2, float z);
+void rsrDrawPath(Context *, Script *, Path *);
 void rsrDrawMesh(Context *, Script *, Mesh *);
 void rsrDrawMeshPrimitive(Context *, Script *, Mesh *, uint32_t primIndex);
 void rsrDrawMeshPrimitiveRange(Context *, Script *, Mesh *,
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index 7964792..97469d3 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -50,6 +50,18 @@
     pf->bindTexture(rsc, slot, a);
 }
 
+void rsrBindConstant(Context *rsc, Script *sc, ProgramFragment *pf, uint32_t slot, Allocation *a) {
+    CHECK_OBJ_OR_NULL(a);
+    CHECK_OBJ(pf);
+    pf->bindAllocation(rsc, a, slot);
+}
+
+void rsrBindConstant(Context *rsc, Script *sc, ProgramVertex *pv, uint32_t slot, Allocation *a) {
+    CHECK_OBJ_OR_NULL(a);
+    CHECK_OBJ(pv);
+    pv->bindAllocation(rsc, a, slot);
+}
+
 void rsrBindSampler(Context *rsc, Script *sc, ProgramFragment *pf, uint32_t slot, Sampler *s) {
     CHECK_OBJ_OR_NULL(vs);
     CHECK_OBJ(vpf);
@@ -200,6 +212,14 @@
     rsrDrawQuad(rsc, sc, x1, y2, z, x2, y2, z, x2, y1, z, x1, y1, z);
 }
 
+void rsrDrawPath(Context *rsc, Script *sc, Path *sm) {
+    CHECK_OBJ(sm);
+    if (!rsc->setupCheck()) {
+        return;
+    }
+    sm->render(rsc);
+}
+
 void rsrDrawMesh(Context *rsc, Script *sc, Mesh *sm) {
     CHECK_OBJ(sm);
     if (!rsc->setupCheck()) {
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index 7966470..70ab7b7 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -46,12 +46,8 @@
         delete [] mLODs;
         mLODs = NULL;
     }
-    mDimX = 0;
-    mDimY = 0;
-    mDimZ = 0;
-    mDimLOD = 0;
-    mFaces = false;
     mElement.clear();
+    memset(&mHal, 0, sizeof(mHal));
 }
 
 TypeState::TypeState() {
@@ -62,16 +58,16 @@
 }
 
 size_t Type::getOffsetForFace(uint32_t face) const {
-    rsAssert(mFaces);
+    rsAssert(mHal.state.faces);
     return 0;
 }
 
 void Type::compute() {
     uint32_t oldLODCount = mLODCount;
-    if (mDimLOD) {
-        uint32_t l2x = rsFindHighBit(mDimX) + 1;
-        uint32_t l2y = rsFindHighBit(mDimY) + 1;
-        uint32_t l2z = rsFindHighBit(mDimZ) + 1;
+    if (mHal.state.dimLOD) {
+        uint32_t l2x = rsFindHighBit(mHal.state.dimX) + 1;
+        uint32_t l2y = rsFindHighBit(mHal.state.dimY) + 1;
+        uint32_t l2z = rsFindHighBit(mHal.state.dimZ) + 1;
 
         mLODCount = rsMax(l2x, l2y);
         mLODCount = rsMax(mLODCount, l2z);
@@ -85,9 +81,9 @@
         mLODs = new LOD[mLODCount];
     }
 
-    uint32_t tx = mDimX;
-    uint32_t ty = mDimY;
-    uint32_t tz = mDimZ;
+    uint32_t tx = mHal.state.dimX;
+    uint32_t ty = mHal.state.dimY;
+    uint32_t tz = mHal.state.dimZ;
     size_t offset = 0;
     for (uint32_t lod=0; lod < mLODCount; lod++) {
         mLODs[lod].mX = tx;
@@ -103,10 +99,11 @@
     // At this point the offset is the size of a mipmap chain;
     mMipChainSizeBytes = offset;
 
-    if (mFaces) {
+    if (mHal.state.faces) {
         offset *= 6;
     }
     mTotalSizeBytes = offset;
+    mHal.state.element = mElement.get();
 }
 
 uint32_t Type::getLODOffset(uint32_t lod, uint32_t x) const {
@@ -127,7 +124,8 @@
     return offset;
 }
 
-uint32_t Type::getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face, uint32_t x, uint32_t y) const {
+uint32_t Type::getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face,
+                                uint32_t x, uint32_t y) const {
     uint32_t offset = mLODs[lod].mOffset;
     offset += (x + y * mLODs[lod].mX) * mElement->getSizeBytes();
 
@@ -141,7 +139,12 @@
 void Type::dumpLOGV(const char *prefix) const {
     char buf[1024];
     ObjectBase::dumpLOGV(prefix);
-    ALOGV("%s   Type: x=%zu y=%zu z=%zu mip=%i face=%i", prefix, mDimX, mDimY, mDimZ, mDimLOD, mFaces);
+    ALOGV("%s   Type: x=%u y=%u z=%u mip=%i face=%i", prefix,
+                                                      mHal.state.dimX,
+                                                      mHal.state.dimY,
+                                                      mHal.state.dimZ,
+                                                      mHal.state.dimLOD,
+                                                      mHal.state.faces);
     snprintf(buf, sizeof(buf), "%s element: ", prefix);
     mElement->dumpLOGV(buf);
 }
@@ -155,12 +158,12 @@
 
     mElement->serialize(stream);
 
-    stream->addU32(mDimX);
-    stream->addU32(mDimY);
-    stream->addU32(mDimZ);
+    stream->addU32(mHal.state.dimX);
+    stream->addU32(mHal.state.dimY);
+    stream->addU32(mHal.state.dimZ);
 
-    stream->addU8((uint8_t)(mDimLOD ? 1 : 0));
-    stream->addU8((uint8_t)(mFaces ? 1 : 0));
+    stream->addU8((uint8_t)(mHal.state.dimLOD ? 1 : 0));
+    stream->addU8((uint8_t)(mHal.state.faces ? 1 : 0));
 }
 
 Type *Type::createFromStream(Context *rsc, IStream *stream) {
@@ -232,11 +235,11 @@
     Type *nt = new Type(rsc);
     returnRef.set(nt);
     nt->mElement.set(e);
-    nt->mDimX = dimX;
-    nt->mDimY = dimY;
-    nt->mDimZ = dimZ;
-    nt->mDimLOD = dimLOD;
-    nt->mFaces = dimFaces;
+    nt->mHal.state.dimX = dimX;
+    nt->mHal.state.dimY = dimY;
+    nt->mHal.state.dimZ = dimZ;
+    nt->mHal.state.dimLOD = dimLOD;
+    nt->mHal.state.faces = dimFaces;
     nt->compute();
 
     ObjectBase::asyncLock();
@@ -248,14 +251,14 @@
 
 ObjectBaseRef<Type> Type::cloneAndResize1D(Context *rsc, uint32_t dimX) const {
     return getTypeRef(rsc, mElement.get(), dimX,
-                      mDimY, mDimZ, mDimLOD, mFaces);
+                      mHal.state.dimY, mHal.state.dimZ, mHal.state.dimLOD, mHal.state.faces);
 }
 
 ObjectBaseRef<Type> Type::cloneAndResize2D(Context *rsc,
                               uint32_t dimX,
                               uint32_t dimY) const {
     return getTypeRef(rsc, mElement.get(), dimX, dimY,
-                      mDimZ, mDimLOD, mFaces);
+                      mHal.state.dimZ, mHal.state.dimLOD, mHal.state.faces);
 }
 
 
@@ -276,8 +279,8 @@
 
 void rsaTypeGetNativeData(RsContext con, RsType type, uint32_t *typeData, uint32_t typeDataSize) {
     rsAssert(typeDataSize == 6);
-    // Pack the data in the follofing way mDimX; mDimY; mDimZ;
-    // mDimLOD; mDimFaces; mElement; into typeData
+    // Pack the data in the follofing way mHal.state.dimX; mHal.state.dimY; mHal.state.dimZ;
+    // mHal.state.dimLOD; mHal.state.faces; mElement; into typeData
     Type *t = static_cast<Type *>(type);
 
     (*typeData++) = t->getDimX();
diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h
index bc0d9ff..3878156 100644
--- a/libs/rs/rsType.h
+++ b/libs/rs/rsType.h
@@ -22,10 +22,35 @@
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-
+/*****************************************************************************
+ * CAUTION
+ *
+ * Any layout changes for this class may require a corresponding change to be
+ * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains
+ * a partial copy of the information below.
+ *
+ *****************************************************************************/
 
 class Type : public ObjectBase {
 public:
+    struct Hal {
+        mutable void *drv;
+
+        struct State {
+            const Element * element;
+
+            // Size of the structure in the various dimensions.  A missing Dimension is
+            // specified as a 0 and not a 1.
+            uint32_t dimX;
+            uint32_t dimY;
+            uint32_t dimZ;
+            bool dimLOD;
+            bool faces;
+        };
+        State state;
+    };
+    Hal mHal;
+
     Type * createTex2D(const Element *, size_t w, size_t h, bool mip);
 
     size_t getOffsetForFace(uint32_t face) const;
@@ -34,22 +59,25 @@
     size_t getElementSizeBytes() const {return mElement->getSizeBytes();}
     const Element * getElement() const {return mElement.get();}
 
-    uint32_t getDimX() const {return mDimX;}
-    uint32_t getDimY() const {return mDimY;}
-    uint32_t getDimZ() const {return mDimZ;}
-    uint32_t getDimLOD() const {return mDimLOD;}
-    bool getDimFaces() const {return mFaces;}
+    uint32_t getDimX() const {return mHal.state.dimX;}
+    uint32_t getDimY() const {return mHal.state.dimY;}
+    uint32_t getDimZ() const {return mHal.state.dimZ;}
+    uint32_t getDimLOD() const {return mHal.state.dimLOD;}
+    bool getDimFaces() const {return mHal.state.faces;}
 
     uint32_t getLODDimX(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mX;}
     uint32_t getLODDimY(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mY;}
     uint32_t getLODDimZ(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mZ;}
 
-    uint32_t getLODOffset(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mOffset;}
+    uint32_t getLODOffset(uint32_t lod) const {
+        rsAssert(lod < mLODCount); return mLODs[lod].mOffset;
+    }
     uint32_t getLODOffset(uint32_t lod, uint32_t x) const;
     uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const;
     uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const;
 
-    uint32_t getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face, uint32_t x, uint32_t y) const;
+    uint32_t getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face,
+                              uint32_t x, uint32_t y) const;
 
     uint32_t getLODCount() const {return mLODCount;}
     bool getIsNp2() const;
@@ -95,14 +123,6 @@
 
     ObjectBaseRef<const Element> mElement;
 
-    // Size of the structure in the various dimensions.  A missing Dimension is
-    // specified as a 0 and not a 1.
-    size_t mDimX;
-    size_t mDimY;
-    size_t mDimZ;
-    bool mDimLOD;
-    bool mFaces;
-
     // count of mipmap levels, 0 indicates no mipmapping
 
     size_t mMipChainSizeBytes;
diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h
index 0afc94b..1e222e1 100644
--- a/libs/rs/rs_hal.h
+++ b/libs/rs/rs_hal.h
@@ -29,6 +29,7 @@
 class Allocation;
 class Script;
 class ScriptC;
+class Path;
 class Program;
 class ProgramStore;
 class ProgramRaster;
@@ -190,6 +191,13 @@
     } mesh;
 
     struct {
+        bool (*initStatic)(const Context *rsc, const Path *m, const Allocation *vtx, const Allocation *loops);
+        bool (*initDynamic)(const Context *rsc, const Path *m);
+        void (*draw)(const Context *rsc, const Path *m);
+        void (*destroy)(const Context *rsc, const Path *m);
+    } path;
+
+    struct {
         bool (*init)(const Context *rsc, const Sampler *m);
         void (*destroy)(const Context *rsc, const Sampler *m);
     } sampler;
diff --git a/libs/rs/scriptc/rs_allocation.rsh b/libs/rs/scriptc/rs_allocation.rsh
index 9ec03bf..a2f69d9 100644
--- a/libs/rs/scriptc/rs_allocation.rsh
+++ b/libs/rs/scriptc/rs_allocation.rsh
@@ -168,5 +168,135 @@
 extern const void * __attribute__((overloadable))
     rsGetElementAt(rs_allocation, uint32_t x, uint32_t y, uint32_t z);
 
+/**
+ * @param a allocation to get data from
+ * @return element describing allocation layout
+ */
+extern rs_element __attribute__((overloadable))
+    rsAllocationGetElement(rs_allocation a);
+
+/**
+ * @param m mesh to get data from
+ * @return number of allocations in the mesh that contain vertex
+ *         data
+ */
+extern uint32_t __attribute__((overloadable))
+    rsMeshGetVertexAllocationCount(rs_mesh m);
+
+/**
+ * @param m mesh to get data from
+ * @return number of primitive groups in the mesh. This would
+ *         include simple primitives as well as allocations
+ *         containing index data
+ */
+extern uint32_t __attribute__((overloadable))
+    rsMeshGetPrimitiveCount(rs_mesh m);
+
+/**
+ * @param m mesh to get data from
+ * @param index index of the vertex allocation
+ * @return allocation containing vertex data
+ */
+extern rs_allocation __attribute__((overloadable))
+    rsMeshGetVertexAllocation(rs_mesh m, uint32_t index);
+
+/**
+ * @param m mesh to get data from
+ * @param index index of the index allocation
+ * @return allocation containing index data
+ */
+extern rs_allocation __attribute__((overloadable))
+    rsMeshGetIndexAllocation(rs_mesh m, uint32_t index);
+
+/**
+ * @param m mesh to get data from
+ * @param index index of the primitive
+ * @return primitive describing how the mesh is rendered
+ */
+extern rs_primitive __attribute__((overloadable))
+    rsMeshGetPrimitive(rs_mesh m, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @return number of sub-elements in this element
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementCount(rs_element e);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element to return
+ * @return sub-element in this element at given index
+ */
+extern rs_element __attribute__((overloadable))
+    rsElementGetSubElement(rs_element, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element to return
+ * @return length of the sub-element name including the null
+ *         terminator (size of buffer needed to write the name)
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementNameLength(rs_element e, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element
+ * @param name array to store the name into
+ * @param nameLength length of the provided name array
+ * @return number of characters actually written, excluding the
+ *         null terminator
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementName(rs_element e, uint32_t index, char *name, uint32_t nameLength);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element
+ * @return array size of sub-element in this element at given
+ *         index
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementArraySize(rs_element e, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element
+ * @return offset in bytes of sub-element in this element at
+ *         given index
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementOffsetBytes(rs_element e, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @return total size of the element in bytes
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSizeBytes(rs_element e);
+
+/**
+ * @param e element to get data from
+ * @return element's data type
+ */
+extern rs_data_type __attribute__((overloadable))
+    rsElementGetDataType(rs_element e);
+
+/**
+ * @param e element to get data from
+ * @return element's data size
+ */
+extern rs_data_kind __attribute__((overloadable))
+    rsElementGetDataKind(rs_element e);
+
+/**
+ * @param e element to get data from
+ * @return length of the element vector (for float2, float3,
+ *         etc.)
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetVectorSize(rs_element e);
+
 #endif
 
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
index 2581953..7fdebdc 100644
--- a/libs/rs/scriptc/rs_graphics.rsh
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -22,6 +22,66 @@
  */
 #ifndef __RS_GRAPHICS_RSH__
 #define __RS_GRAPHICS_RSH__
+
+// These are API 15 once it get official
+typedef enum {
+    RS_DEPTH_FUNC_ALWAYS,
+    RS_DEPTH_FUNC_LESS,
+    RS_DEPTH_FUNC_LEQUAL,
+    RS_DEPTH_FUNC_GREATER,
+    RS_DEPTH_FUNC_GEQUAL,
+    RS_DEPTH_FUNC_EQUAL,
+    RS_DEPTH_FUNC_NOTEQUAL,
+
+    RS_DEPTH_FUNC_INVALID = 100,
+} rs_depth_func;
+
+typedef enum {
+    RS_BLEND_SRC_ZERO,                  // 0
+    RS_BLEND_SRC_ONE,                   // 1
+    RS_BLEND_SRC_DST_COLOR,             // 2
+    RS_BLEND_SRC_ONE_MINUS_DST_COLOR,   // 3
+    RS_BLEND_SRC_SRC_ALPHA,             // 4
+    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA,   // 5
+    RS_BLEND_SRC_DST_ALPHA,             // 6
+    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA,   // 7
+    RS_BLEND_SRC_SRC_ALPHA_SATURATE,    // 8
+
+    RS_BLEND_SRC_INVALID = 100,
+} rs_blend_src_func;
+
+typedef enum {
+    RS_BLEND_DST_ZERO,                  // 0
+    RS_BLEND_DST_ONE,                   // 1
+    RS_BLEND_DST_SRC_COLOR,             // 2
+    RS_BLEND_DST_ONE_MINUS_SRC_COLOR,   // 3
+    RS_BLEND_DST_SRC_ALPHA,             // 4
+    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,   // 5
+    RS_BLEND_DST_DST_ALPHA,             // 6
+    RS_BLEND_DST_ONE_MINUS_DST_ALPHA,   // 7
+
+    RS_BLEND_DST_INVALID = 100,
+} rs_blend_dst_func;
+
+typedef enum {
+    RS_CULL_BACK,
+    RS_CULL_FRONT,
+    RS_CULL_NONE,
+
+    RS_CULL_INVALID = 100,
+} rs_cull_mode;
+
+typedef enum {
+    RS_SAMPLER_NEAREST,
+    RS_SAMPLER_LINEAR,
+    RS_SAMPLER_LINEAR_MIP_LINEAR,
+    RS_SAMPLER_WRAP,
+    RS_SAMPLER_CLAMP,
+    RS_SAMPLER_LINEAR_MIP_NEAREST,
+
+    RS_SAMPLER_INVALID = 100,
+} rs_sampler_value;
+
 #if (defined(RS_VERSION) && (RS_VERSION >= 14))
 /**
  * Set the color target used for all subsequent rendering calls
@@ -82,6 +142,88 @@
 extern void __attribute__((overloadable))
     rsgBindProgramStore(rs_program_store ps);
 
+
+/**
+ * @hide
+ * Get program store depth function
+ *
+ * @param ps
+ */
+extern rs_depth_func __attribute__((overloadable))
+    rsgProgramStoreGetDepthFunc(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store depth mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetDepthMask(rs_program_store ps);
+/**
+ * @hide
+ * Get program store red component color mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetColorMaskR(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store green component color mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetColorMaskG(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store blur component color mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetColorMaskB(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store alpha component color mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetColorMaskA(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store blend source function
+ *
+ * @param ps
+ */
+extern rs_blend_src_func __attribute__((overloadable))
+        rsgProgramStoreGetBlendSrcFunc(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store blend destination function
+ *
+ * @param ps
+ */
+extern rs_blend_dst_func __attribute__((overloadable))
+    rsgProgramStoreGetBlendDstFunc(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store dither state
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetDitherEnabled(rs_program_store ps);
+
+
 /**
  * Bind a new ProgramVertex to the rendering context.
  *
@@ -99,6 +241,24 @@
     rsgBindProgramRaster(rs_program_raster pr);
 
 /**
+ * @hide
+ * Get program raster point sprite state
+ *
+ * @param pr
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramRasterGetPointSpriteEnabled(rs_program_raster pr);
+
+/**
+ * @hide
+ * Get program raster cull mode
+ *
+ * @param pr
+ */
+extern rs_cull_mode __attribute__((overloadable))
+    rsgProgramRasterGetCullMode(rs_program_raster pr);
+
+/**
  * Bind a new Sampler object to a ProgramFragment.  The sampler will
  * operate on the texture bound at the matching slot.
  *
@@ -108,6 +268,51 @@
     rsgBindSampler(rs_program_fragment, uint slot, rs_sampler);
 
 /**
+ * @hide
+ * Get sampler minification value
+ *
+ * @param pr
+ */
+extern rs_sampler_value __attribute__((overloadable))
+    rsgSamplerGetMinification(rs_sampler s);
+
+/**
+ * @hide
+ * Get sampler magnification value
+ *
+ * @param pr
+ */
+extern rs_sampler_value __attribute__((overloadable))
+    rsgSamplerGetMagnification(rs_sampler s);
+
+/**
+ * @hide
+ * Get sampler wrap S value
+ *
+ * @param pr
+ */
+extern rs_sampler_value __attribute__((overloadable))
+    rsgSamplerGetWrapS(rs_sampler s);
+
+/**
+ * @hide
+ * Get sampler wrap T value
+ *
+ * @param pr
+ */
+extern rs_sampler_value __attribute__((overloadable))
+    rsgSamplerGetWrapT(rs_sampler s);
+
+/**
+ * @hide
+ * Get sampler anisotropy
+ *
+ * @param pr
+ */
+extern float __attribute__((overloadable))
+    rsgSamplerGetAnisotropy(rs_sampler s);
+
+/**
  * Bind a new Allocation object to a ProgramFragment.  The
  * Allocation must be a valid texture for the Program.  The sampling
  * of the texture will be controled by the Sampler bound at the
@@ -164,6 +369,28 @@
     rsgProgramFragmentConstantColor(rs_program_fragment pf, float r, float g, float b, float a);
 
 /**
+ * Bind a new Allocation object to a ProgramFragment.  The
+ * Allocation must be a valid constant input for the Program.
+ *
+ * @param ps program object
+ * @param slot index of the constant buffer on the program
+ * @param c constants to bind
+ */
+extern void __attribute__((overloadable))
+    rsgBindConstant(rs_program_fragment ps, uint slot, rs_allocation c);
+
+/**
+ * Bind a new Allocation object to a ProgramVertex.  The
+ * Allocation must be a valid constant input for the Program.
+ *
+ * @param pv program object
+ * @param slot index of the constant buffer on the program
+ * @param c constants to bind
+ */
+extern void __attribute__((overloadable))
+    rsgBindConstant(rs_program_vertex pv, uint slot, rs_allocation c);
+
+/**
  * Get the width of the current rendering surface.
  *
  * @return uint
@@ -288,6 +515,9 @@
 extern void __attribute__((overloadable))
     rsgDrawSpriteScreenspace(float x, float y, float z, float w, float h);
 
+extern void __attribute__((overloadable))
+    rsgDrawPath(rs_path p);
+
 /**
  * Draw a mesh using the current context state.  The whole mesh is
  * rendered.
diff --git a/libs/rs/scriptc/rs_object.rsh b/libs/rs/scriptc/rs_object.rsh
index a431219..1fc3f83 100644
--- a/libs/rs/scriptc/rs_object.rsh
+++ b/libs/rs/scriptc/rs_object.rsh
@@ -56,6 +56,11 @@
  * \overload
  */
 extern void __attribute__((overloadable))
+    rsSetObject(rs_path *dst, rs_path src);
+/**
+ * \overload
+ */
+extern void __attribute__((overloadable))
     rsSetObject(rs_mesh *dst, rs_mesh src);
 /**
  * \overload
@@ -114,6 +119,11 @@
  * \overload
  */
 extern void __attribute__((overloadable))
+    rsClearObject(rs_path *dst);
+/**
+ * \overload
+ */
+extern void __attribute__((overloadable))
     rsClearObject(rs_mesh *dst);
 /**
  * \overload
@@ -175,6 +185,11 @@
  * \overload
  */
 extern bool __attribute__((overloadable))
+    rsIsObject(rs_path);
+/**
+ * \overload
+ */
+extern bool __attribute__((overloadable))
     rsIsObject(rs_mesh);
 /**
  * \overload
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index 84bca9c..5345a48 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -138,6 +138,12 @@
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_mesh;
 /**
+ * \brief Opaque handle to a Renderscript Path object.
+ *
+ * See: android.renderscript.Path
+ */
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_path;
+/**
  * \brief Opaque handle to a Renderscript ProgramFragment object.
  *
  * See: android.renderscript.ProgramFragment
@@ -396,4 +402,96 @@
 
 #endif //defined(RS_VERSION) && (RS_VERSION >= 14)
 
+/**
+ * Describes the way mesh vertex data is interpreted when rendering
+ *
+ **/
+typedef enum {
+    RS_PRIMITIVE_POINT,
+    RS_PRIMITIVE_LINE,
+    RS_PRIMITIVE_LINE_STRIP,
+    RS_PRIMITIVE_TRIANGLE,
+    RS_PRIMITIVE_TRIANGLE_STRIP,
+    RS_PRIMITIVE_TRIANGLE_FAN,
+
+    RS_PRIMITIVE_INVALID = 100,
+} rs_primitive;
+
+/**
+ * \brief Enumeration for possible element data types
+ *
+ * DataType represents the basic type information for a basic element.  The
+ * naming convention follows.  For numeric types it is FLOAT,
+ * SIGNED, or UNSIGNED followed by the _BITS where BITS is the
+ * size of the data.  BOOLEAN is a true / false (1,0)
+ * represented in an 8 bit container.  The UNSIGNED variants
+ * with multiple bit definitions are for packed graphical data
+ * formats and represent vectors with per vector member sizes
+ * which are treated as a single unit for packing and alignment
+ * purposes.
+ *
+ * MATRIX the three matrix types contain FLOAT_32 elements and are treated
+ * as 32 bits for alignment purposes.
+ *
+ * RS_* objects.  32 bit opaque handles.
+ */
+typedef enum {
+    RS_TYPE_NONE,
+    //RS_TYPE_FLOAT_16,
+    RS_TYPE_FLOAT_32 = 2,
+    RS_TYPE_FLOAT_64,
+    RS_TYPE_SIGNED_8,
+    RS_TYPE_SIGNED_16,
+    RS_TYPE_SIGNED_32,
+    RS_TYPE_SIGNED_64,
+    RS_TYPE_UNSIGNED_8,
+    RS_TYPE_UNSIGNED_16,
+    RS_TYPE_UNSIGNED_32,
+    RS_TYPE_UNSIGNED_64,
+
+    RS_TYPE_BOOLEAN,
+
+    RS_TYPE_UNSIGNED_5_6_5,
+    RS_TYPE_UNSIGNED_5_5_5_1,
+    RS_TYPE_UNSIGNED_4_4_4_4,
+
+    RS_TYPE_MATRIX_4X4,
+    RS_TYPE_MATRIX_3X3,
+    RS_TYPE_MATRIX_2X2,
+
+    RS_TYPE_ELEMENT = 1000,
+    RS_TYPE_TYPE,
+    RS_TYPE_ALLOCATION,
+    RS_TYPE_SAMPLER,
+    RS_TYPE_SCRIPT,
+    RS_TYPE_MESH,
+    RS_TYPE_PROGRAM_FRAGMENT,
+    RS_TYPE_PROGRAM_VERTEX,
+    RS_TYPE_PROGRAM_RASTER,
+    RS_TYPE_PROGRAM_STORE,
+
+    RS_TYPE_INVALID = 10000,
+} rs_data_type;
+
+/**
+ * \brief Enumeration for possible element data kind
+ *
+ * The special interpretation of the data if required.  This is primarly
+ * useful for graphical data.  USER indicates no special interpretation is
+ * expected.  PIXEL is used in conjunction with the standard data types for
+ * representing texture formats.
+ */
+typedef enum {
+    RS_KIND_USER,
+
+    RS_KIND_PIXEL_L = 7,
+    RS_KIND_PIXEL_A,
+    RS_KIND_PIXEL_LA,
+    RS_KIND_PIXEL_RGB,
+    RS_KIND_PIXEL_RGBA,
+    RS_KIND_PIXEL_DEPTH,
+
+    RS_KIND_INVALID = 100,
+} rs_data_kind;
+
 #endif
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index df4fbb5..ceb87db 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -623,7 +623,7 @@
         newTrack->setVolume(leftVolume, rightVolume);
         newTrack->setLoop(0, frameCount, loop);
 
-        // From now on, AudioTrack callbacks recevieved with previous toggle value will be ignored.
+        // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
         mToggle = toggle;
         mAudioTrack = newTrack;
         mPos = 0;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index b74b3e3..a4068ff 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -304,10 +304,25 @@
     if (mActive == 0) {
         mActive = 1;
 
+        pid_t tid;
+        if (t != 0) {
+            mReadyToRun = WOULD_BLOCK;
+            t->run("ClientRecordThread", ANDROID_PRIORITY_AUDIO);
+            tid = t->getTid();  // pid_t is unknown until run()
+            ALOGV("getTid=%d", tid);
+            if (tid == -1) {
+                tid = 0;
+            }
+            // thread blocks in readyToRun()
+        } else {
+            tid = 0;    // not gettid()
+        }
+
         cblk->lock.lock();
         if (!(cblk->flags & CBLK_INVALID_MSK)) {
             cblk->lock.unlock();
-            ret = mAudioRecord->start();
+            ALOGV("mAudioRecord->start(tid=%d)", tid);
+            ret = mAudioRecord->start(tid);
             cblk->lock.lock();
             if (ret == DEAD_OBJECT) {
                 android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
@@ -322,7 +337,9 @@
             cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
             cblk->waitTimeMs = 0;
             if (t != 0) {
-                t->run("ClientRecordThread", ANDROID_PRIORITY_AUDIO);
+                // thread unblocks in readyToRun() and returns NO_ERROR
+                mReadyToRun = NO_ERROR;
+                mCondition.signal();
             } else {
                 mPreviousPriority = getpriority(PRIO_PROCESS, 0);
                 mPreviousSchedulingGroup = androidGetThreadSchedulingGroup(0);
@@ -330,6 +347,9 @@
             }
         } else {
             mActive = 0;
+            // thread unblocks in readyToRun() and returns NO_INIT
+            mReadyToRun = NO_INIT;
+            mCondition.signal();
         }
     }
 
@@ -522,7 +542,7 @@
                     ALOGW(   "obtainBuffer timed out (is the CPU pegged?) "
                             "user=%08x, server=%08x", cblk->user, cblk->server);
                     cblk->lock.unlock();
-                    result = mAudioRecord->start();
+                    result = mAudioRecord->start(0);    // callback thread hasn't changed
                     cblk->lock.lock();
                     if (result == DEAD_OBJECT) {
                         android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
@@ -760,7 +780,7 @@
         result = openRecord_l(cblk->sampleRate, mFormat, mChannelMask,
                 mFrameCount, mFlags, getInput_l());
         if (result == NO_ERROR) {
-            result = mAudioRecord->start();
+            result = mAudioRecord->start(0);    // callback thread hasn't changed
         }
         if (result != NO_ERROR) {
             mActive = false;
@@ -811,6 +831,15 @@
     return mReceiver.processAudioBuffer(this);
 }
 
+status_t AudioRecord::ClientRecordThread::readyToRun()
+{
+    AutoMutex(mReceiver.mLock);
+    while (mReceiver.mReadyToRun == WOULD_BLOCK) {
+        mReceiver.mCondition.wait(mReceiver.mLock);
+    }
+    return mReceiver.mReadyToRun;
+}
+
 // -------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 087d7b2..aead9a1 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1,4 +1,4 @@
-/* frameworks/base/media/libmedia/AudioTrack.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -362,18 +362,26 @@
         cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
         cblk->waitTimeMs = 0;
         android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
+        pid_t tid;
         if (t != 0) {
             t->run("AudioTrackThread", ANDROID_PRIORITY_AUDIO);
+            tid = t->getTid();  // pid_t is unknown until run()
+            ALOGV("getTid=%d", tid);
+            if (tid == -1) {
+                tid = 0;
+            }
         } else {
             mPreviousPriority = getpriority(PRIO_PROCESS, 0);
             mPreviousSchedulingGroup = androidGetThreadSchedulingGroup(0);
             androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
+            tid = 0;    // not gettid()
         }
 
         ALOGV("start %p before lock cblk %p", this, mCblk);
         if (!(cblk->flags & CBLK_INVALID_MSK)) {
             cblk->lock.unlock();
-            status = mAudioTrack->start();
+            ALOGV("mAudioTrack->start(tid=%d)", tid);
+            status = mAudioTrack->start(tid);
             cblk->lock.lock();
             if (status == DEAD_OBJECT) {
                 android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
@@ -591,26 +599,6 @@
     return NO_ERROR;
 }
 
-status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount) const
-{
-    AutoMutex lock(mLock);
-    if (loopStart != NULL) {
-        *loopStart = mCblk->loopStart;
-    }
-    if (loopEnd != NULL) {
-        *loopEnd = mCblk->loopEnd;
-    }
-    if (loopCount != NULL) {
-        if (mCblk->loopCount < 0) {
-            *loopCount = -1;
-        } else {
-            *loopCount = mCblk->loopCount;
-        }
-    }
-
-    return NO_ERROR;
-}
-
 status_t AudioTrack::setMarkerPosition(uint32_t marker)
 {
     if (mCbf == NULL) return INVALID_OPERATION;
@@ -784,7 +772,7 @@
                 }
             }
         } else {
-            // Ensure that buffer alignment matches channelcount
+            // Ensure that buffer alignment matches channelCount
             int channelCount = popcount(channelMask);
             if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) {
                 ALOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount);
@@ -895,7 +883,7 @@
                                 "user=%08x, server=%08x", this, cblk->user, cblk->server);
                         //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
                         cblk->lock.unlock();
-                        result = mAudioTrack->start();
+                        result = mAudioTrack->start(0); // callback thread hasn't changed
                         cblk->lock.lock();
                         if (result == DEAD_OBJECT) {
                             android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
@@ -927,7 +915,7 @@
     if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
         android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
         ALOGW("obtainBuffer() track %p disabled, restarting", this);
-        mAudioTrack->start();
+        mAudioTrack->start(0);  // callback thread hasn't changed
     }
 
     cblk->waitTimeMs = 0;
@@ -971,7 +959,8 @@
     if (mSharedBuffer != 0) return INVALID_OPERATION;
 
     if (ssize_t(userSize) < 0) {
-        // sanity-check. user is most-likely passing an error code.
+        // Sanity-check: user is most-likely passing an error code, and it would
+        // make the return value ambiguous (actualSize vs error).
         ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)",
                 buffer, userSize, userSize);
         return BAD_VALUE;
@@ -994,8 +983,6 @@
     do {
         audioBuffer.frameCount = userSize/frameSz;
 
-        // Calling obtainBuffer() with a negative wait count causes
-        // an (almost) infinite wait time.
         status_t err = obtainBuffer(&audioBuffer, -1);
         if (err < 0) {
             // out of buffers, return #bytes written
@@ -1085,6 +1072,9 @@
         frames = mRemainingFrames;
     }
 
+    // See description of waitCount parameter at declaration of obtainBuffer().
+    // The logic below prevents us from being stuck below at obtainBuffer()
+    // not being able to handle timed events (position, markers, loops).
     int32_t waitCount = -1;
     if (mUpdatePeriod || (!mMarkerReached && mMarkerPosition) || mLoopCount) {
         waitCount = 1;
@@ -1094,9 +1084,6 @@
 
         audioBuffer.frameCount = frames;
 
-        // Calling obtainBuffer() with a wait count of 1
-        // limits wait time to WAIT_PERIOD_MS. This prevents from being
-        // stuck here not being able to handle timed events (position, markers, loops).
         status_t err = obtainBuffer(&audioBuffer, waitCount);
         if (err < NO_ERROR) {
             if (err != TIMED_OUT) {
@@ -1218,7 +1205,7 @@
                 }
             }
             if (mActive) {
-                result = mAudioTrack->start();
+                result = mAudioTrack->start(0); // callback thread hasn't changed
                 ALOGW_IF(result != NO_ERROR, "restoreTrack_l() start() failed status %d", result);
             }
             if (fromStart && result == NO_ERROR) {
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index 8c7a960..6b473c9 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -42,10 +42,11 @@
     {
     }
     
-    virtual status_t start()
+    virtual status_t start(pid_t tid)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor());
+        data.writeInt32(tid);
         status_t status = remote()->transact(START, data, &reply);
         if (status == NO_ERROR) {
             status = reply.readInt32();
@@ -90,7 +91,7 @@
         } break;
         case START: {
             CHECK_INTERFACE(IAudioRecord, data, reply);
-            reply->writeInt32(start());
+            reply->writeInt32(start(data.readInt32()));
             return NO_ERROR;
         } break;
         case STOP: {
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index e618619..a7958de 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -1,4 +1,4 @@
-/* //device/extlibs/pv/android/IAudioTrack.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -58,10 +58,11 @@
         return cblk;
     }
 
-    virtual status_t start()
+    virtual status_t start(pid_t tid)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+        data.writeInt32(tid);
         status_t status = remote()->transact(START, data, &reply);
         if (status == NO_ERROR) {
             status = reply.readInt32();
@@ -130,7 +131,7 @@
         } break;
         case START: {
             CHECK_INTERFACE(IAudioTrack, data, reply);
-            reply->writeInt32(start());
+            reply->writeInt32(start(data.readInt32()));
             return NO_ERROR;
         } break;
         case STOP: {
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 6cb10aa..54eb98a 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -791,7 +791,7 @@
 //        generators, instantiates output audio track.
 //
 //    Input:
-//        streamType:        Type of stream used for tone playback (enum AudioTrack::stream_type)
+//        streamType:        Type of stream used for tone playback
 //        volume:            volume applied to tone (0.0 to 1.0)
 //
 //    Output:
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index f1c47dd..250425b 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -1,4 +1,4 @@
-/* mediaplayer.cpp
+/*
 **
 ** Copyright 2006, The Android Open Source Project
 **
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 06be2ef..0b1016c 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -118,12 +118,6 @@
 
 // ----------------------------------------------------------------------------
 
-Loader::entry_t::entry_t(int dpy, int impl, const char* tag)
-    : dpy(dpy), impl(impl), tag(tag) {
-}
-
-// ----------------------------------------------------------------------------
-
 Loader::Loader()
 {
     char line[256];
@@ -131,8 +125,9 @@
 
     /* Special case for GLES emulation */
     if (checkGlesEmulationStatus() == 0) {
-        ALOGD("Emulator without GPU support detected. Fallback to software renderer.");
-        gConfig.add( entry_t(0, 0, "android") );
+        ALOGD("Emulator without GPU support detected. "
+              "Fallback to software renderer.");
+        mDriverTag.setTo("android");
         return;
     }
 
@@ -141,14 +136,16 @@
     if (cfg == NULL) {
         // default config
         ALOGD("egl.cfg not found, using default config");
-        gConfig.add( entry_t(0, 0, "android") );
+        mDriverTag.setTo("android");
     } else {
         while (fgets(line, 256, cfg)) {
-            int dpy;
-            int impl;
+            int dpy, impl;
             if (sscanf(line, "%u %u %s", &dpy, &impl, tag) == 3) {
                 //ALOGD(">>> %u %u %s", dpy, impl, tag);
-                gConfig.add( entry_t(dpy, impl, tag) );
+                // We only load the h/w accelerated implementation
+                if (tag != String8("android")) {
+                    mDriverTag = tag;
+                }
             }
         }
         fclose(cfg);
@@ -160,30 +157,12 @@
     GLTrace_stop();
 }
 
-const char* Loader::getTag(int dpy, int impl)
+void* Loader::open(egl_connection_t* cnx)
 {
-    const Vector<entry_t>& cfgs(gConfig);    
-    const size_t c = cfgs.size();
-    for (size_t i=0 ; i<c ; i++) {
-        if (dpy == cfgs[i].dpy)
-            if (impl == cfgs[i].impl)
-                return cfgs[i].tag.string();
-    }
-    return 0;
-}
-
-void* Loader::open(EGLNativeDisplayType display, int impl, egl_connection_t* cnx)
-{
-    /*
-     * TODO: if we don't find display/0, then use 0/0
-     * (0/0 should always work)
-     */
-    
     void* dso;
-    int index = int(display);
     driver_t* hnd = 0;
     
-    char const* tag = getTag(index, impl);
+    char const* tag = mDriverTag.string();
     if (tag) {
         dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2);
         if (dso) {
@@ -193,16 +172,14 @@
             dso = load_driver("EGL", tag, cnx, EGL);
             if (dso) {
                 hnd = new driver_t(dso);
-
                 // TODO: make this more automated
                 hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM );
-
-                hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 );
+                hnd->set( load_driver("GLESv2",    tag, cnx, GLESv2),    GLESv2 );
             }
         }
     }
 
-    LOG_FATAL_IF(!index && !impl && !hnd, 
+    LOG_FATAL_IF(!index && !hnd,
             "couldn't find the default OpenGL ES implementation "
             "for default display");
     
@@ -221,7 +198,7 @@
         __eglMustCastToProperFunctionPointerType* curr, 
         getProcAddressType getProcAddress) 
 {
-    const size_t SIZE = 256;
+    const ssize_t SIZE = 256;
     char scrap[SIZE];
     while (*api) {
         char const * name = *api;
@@ -326,14 +303,14 @@
     if (mask & GLESv1_CM) {
         init_api(dso, gl_names,
             (__eglMustCastToProperFunctionPointerType*)
-                &cnx->hooks[GLESv1_INDEX]->gl,
+                &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,
             getProcAddress);
     }
 
     if (mask & GLESv2) {
       init_api(dso, gl_names,
             (__eglMustCastToProperFunctionPointerType*)
-                &cnx->hooks[GLESv2_INDEX]->gl,
+                &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
             getProcAddress);
     }
     
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 580d6e4..30773cb 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -24,7 +24,6 @@
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 #include <utils/String8.h>
-#include <utils/Vector.h>
 
 #include <EGL/egl.h>
 
@@ -53,23 +52,13 @@
         void* dso[3];
     };
     
-    struct entry_t {
-        entry_t() { }
-        entry_t(int dpy, int impl, const char* tag);
-        int dpy;
-        int impl;
-        String8 tag;
-    };
-
-    Vector<entry_t> gConfig;    
+    String8 mDriverTag;
     getProcAddressType getProcAddress;
     
-    const char* getTag(int dpy, int impl);
-
 public:
     ~Loader();
     
-    void* open(EGLNativeDisplayType display, int impl, egl_connection_t* cnx);
+    void* open(egl_connection_t* cnx);
     status_t close(void* driver);
     
 private:
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 83933e5..eec5ce1 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -48,8 +48,8 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
-gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
+egl_connection_t gEGLImpl;
+gl_hooks_t gHooks[2];
 gl_hooks_t gHooksNoContext;
 pthread_key_t gGLWrapperKey = -1;
 
@@ -187,16 +187,13 @@
     return dp;
 }
 
-egl_connection_t* validate_display_config(EGLDisplay dpy, EGLConfig config,
+egl_connection_t* validate_display_config(EGLDisplay dpy, EGLConfig,
         egl_display_t const*& dp) {
     dp = validate_display(dpy);
     if (!dp)
         return (egl_connection_t*) NULL;
 
-    if (intptr_t(config) >= dp->numTotalConfigs) {
-        return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
-    }
-    egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(config)].impl];
+    egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso == 0) {
         return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
     }
@@ -205,34 +202,6 @@
 
 // ----------------------------------------------------------------------------
 
-EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image)
-{
-    EGLContext context = egl_tls_t::getContext();
-    if (context == EGL_NO_CONTEXT || image == EGL_NO_IMAGE_KHR)
-        return EGL_NO_IMAGE_KHR;
-
-    egl_context_t const * const c = get_context(context);
-    if (c == NULL) // this should never happen, by construction
-        return EGL_NO_IMAGE_KHR;
-
-    egl_display_t* display = egl_display_t::get(c->dpy);
-    if (display == NULL) // this should never happen, by construction
-        return EGL_NO_IMAGE_KHR;
-
-    ImageRef _i(display, image);
-    if (!_i.get())
-        return EGL_NO_IMAGE_KHR;
-
-    // here we don't validate the context because if it's been marked for
-    // termination, this call should still succeed since it's internal to
-    // EGL.
-
-    egl_image_t const * const i = get_image(image);
-    return i->images[c->impl];
-}
-
-// ----------------------------------------------------------------------------
-
 const GLubyte * egl_get_string_for_current_context(GLenum name) {
     // NOTE: returning NULL here will fall-back to the default
     // implementation.
@@ -266,34 +235,17 @@
     // get our driver loader
     Loader& loader(Loader::getInstance());
 
-    // dynamically load all our EGL implementations
-    egl_connection_t* cnx;
-
-    cnx = &gEGLImpl[IMPL_SOFTWARE];
+    // dynamically load our EGL implementation
+    egl_connection_t* cnx = &gEGLImpl;
     if (cnx->dso == 0) {
-        cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_SOFTWARE];
-        cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_SOFTWARE];
-        cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 0, cnx);
+        cnx->hooks[egl_connection_t::GLESv1_INDEX] =
+                &gHooks[egl_connection_t::GLESv1_INDEX];
+        cnx->hooks[egl_connection_t::GLESv2_INDEX] =
+                &gHooks[egl_connection_t::GLESv2_INDEX];
+        cnx->dso = loader.open(cnx);
     }
 
-    cnx = &gEGLImpl[IMPL_HARDWARE];
-    if (cnx->dso == 0) {
-        char value[PROPERTY_VALUE_MAX];
-        property_get("debug.egl.hw", value, "1");
-        if (atoi(value) != 0) {
-            cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_HARDWARE];
-            cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_HARDWARE];
-            cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 1, cnx);
-        } else {
-            ALOGD("3D hardware acceleration is disabled");
-        }
-    }
-
-    if (!gEGLImpl[IMPL_SOFTWARE].dso && !gEGLImpl[IMPL_HARDWARE].dso) {
-        return EGL_FALSE;
-    }
-
-    return EGL_TRUE;
+    return cnx->dso ? EGL_TRUE : EGL_FALSE;
 }
 
 static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 73aab26..a5dc832 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -44,6 +44,7 @@
 #include "egl_impl.h"
 #include "egl_object.h"
 #include "egl_tls.h"
+#include "egldefs.h"
 
 using namespace android;
 
@@ -88,24 +89,6 @@
 
 // ----------------------------------------------------------------------------
 
-template<typename T>
-static __attribute__((noinline))
-int binarySearch(T const sortedArray[], int first, int last, T key) {
-    while (first <= last) {
-        int mid = (first + last) / 2;
-        if (sortedArray[mid] < key) {
-            first = mid + 1;
-        } else if (key < sortedArray[mid]) {
-            last = mid - 1;
-        } else {
-            return mid;
-        }
-    }
-    return -1;
-}
-
-// ----------------------------------------------------------------------------
-
 namespace android {
 extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
 extern EGLBoolean egl_init_drivers();
@@ -183,21 +166,20 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    GLint numConfigs = dp->numTotalConfigs;
-    if (!configs) {
-        *num_config = numConfigs;
-        return EGL_TRUE;
+    if (num_config==0) {
+        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
     }
 
-    GLint n = 0;
-    for (intptr_t i=0 ; i<dp->numTotalConfigs && config_size ; i++) {
-        *configs++ = EGLConfig(i);
-        config_size--;
-        n++;
+    EGLBoolean res = EGL_FALSE;
+    *num_config = 0;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        res = cnx->egl.eglGetConfigs(
+                dp->disp.dpy, configs, config_size, num_config);
     }
-    
-    *num_config = n;
-    return EGL_TRUE;
+
+    return res;
 }
 
 EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
@@ -213,105 +195,13 @@
         return setError(EGL_BAD_PARAMETER, EGL_FALSE);
     }
 
-    EGLint n;
     EGLBoolean res = EGL_FALSE;
     *num_config = 0;
 
-    
-    // It is unfortunate, but we need to remap the EGL_CONFIG_IDs, 
-    // to do this, we have to go through the attrib_list array once
-    // to figure out both its size and if it contains an EGL_CONFIG_ID
-    // key. If so, the full array is copied and patched.
-    // NOTE: we assume that there can be only one occurrence
-    // of EGL_CONFIG_ID.
-    
-    EGLint patch_index = -1;
-    GLint attr;
-    size_t size = 0;
-    if (attrib_list) {
-        while ((attr=attrib_list[size]) != EGL_NONE) {
-            if (attr == EGL_CONFIG_ID)
-                patch_index = size;
-            size += 2;
-        }
-    }
-    if (patch_index >= 0) {
-        size += 2; // we need copy the sentinel as well
-        EGLint* new_list = (EGLint*)malloc(size*sizeof(EGLint));
-        if (new_list == 0)
-            return setError(EGL_BAD_ALLOC, EGL_FALSE);
-        memcpy(new_list, attrib_list, size*sizeof(EGLint));
-
-        // patch the requested EGL_CONFIG_ID
-        bool found = false;
-        EGLConfig ourConfig(0);
-        EGLint& configId(new_list[patch_index+1]);
-        for (intptr_t i=0 ; i<dp->numTotalConfigs ; i++) {
-            if (dp->configs[i].configId == configId) {
-                ourConfig = EGLConfig(i);
-                configId = dp->configs[i].implConfigId;
-                found = true;
-                break;
-            }
-        }
-
-        egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(ourConfig)].impl];
-        if (found && cnx->dso) {
-            // and switch to the new list
-            attrib_list = const_cast<const EGLint *>(new_list);
-
-            // At this point, the only configuration that can match is
-            // dp->configs[i][index], however, we don't know if it would be
-            // rejected because of the other attributes, so we do have to call
-            // cnx->egl.eglChooseConfig() -- but we don't have to loop
-            // through all the EGLimpl[].
-            // We also know we can only get a single config back, and we know
-            // which one.
-
-            res = cnx->egl.eglChooseConfig(
-                    dp->disp[ dp->configs[intptr_t(ourConfig)].impl ].dpy,
-                    attrib_list, configs, config_size, &n);
-            if (res && n>0) {
-                // n has to be 0 or 1, by construction, and we already know
-                // which config it will return (since there can be only one).
-                if (configs) {
-                    configs[0] = ourConfig;
-                }
-                *num_config = 1;
-            }
-        }
-
-        free(const_cast<EGLint *>(attrib_list));
-        return res;
-    }
-
-
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglChooseConfig(
-                    dp->disp[i].dpy, attrib_list, configs, config_size, &n)) {
-                if (configs) {
-                    // now we need to convert these client EGLConfig to our
-                    // internal EGLConfig format.
-                    // This is done in O(n Log(n)) time.
-                    for (int j=0 ; j<n ; j++) {
-                        egl_config_t key(i, configs[j]);
-                        intptr_t index = binarySearch<egl_config_t>(
-                                dp->configs, 0, dp->numTotalConfigs, key);
-                        if (index >= 0) {
-                            configs[j] = EGLConfig(index);
-                        } else {
-                            return setError(EGL_BAD_CONFIG, EGL_FALSE);
-                        }
-                    }
-                    configs += n;
-                    config_size -= n;
-                }
-                *num_config += n;
-                res = EGL_TRUE;
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        res = cnx->egl.eglChooseConfig(
+                dp->disp.dpy, attrib_list, configs, config_size, num_config);
     }
     return res;
 }
@@ -325,13 +215,8 @@
     egl_connection_t* cnx = validate_display_config(dpy, config, dp);
     if (!cnx) return EGL_FALSE;
     
-    if (attribute == EGL_CONFIG_ID) {
-        *value = dp->configs[intptr_t(config)].configId;
-        return EGL_TRUE;
-    }
     return cnx->egl.eglGetConfigAttrib(
-            dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-            dp->configs[intptr_t(config)].config, attribute, value);
+            dp->disp.dpy, config, attribute, value);
 }
 
 // ----------------------------------------------------------------------------
@@ -347,8 +232,7 @@
     egl_display_t const* dp = 0;
     egl_connection_t* cnx = validate_display_config(dpy, config, dp);
     if (cnx) {
-        EGLDisplay iDpy = dp->disp[ dp->configs[intptr_t(config)].impl ].dpy;
-        EGLConfig iConfig = dp->configs[intptr_t(config)].config;
+        EGLDisplay iDpy = dp->disp.dpy;
         EGLint format;
 
         if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) {
@@ -359,7 +243,7 @@
 
         // set the native window's buffers format to match this config
         if (cnx->egl.eglGetConfigAttrib(iDpy,
-                iConfig, EGL_NATIVE_VISUAL_ID, &format)) {
+                config, EGL_NATIVE_VISUAL_ID, &format)) {
             if (format != 0) {
                 int err = native_window_set_buffers_format(window, format);
                 if (err != 0) {
@@ -377,10 +261,9 @@
         anw->setSwapInterval(anw, 1);
 
         EGLSurface surface = cnx->egl.eglCreateWindowSurface(
-                iDpy, iConfig, window, attrib_list);
+                iDpy, config, window, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, window, surface,
-                    dp->configs[intptr_t(config)].impl, cnx);
+            egl_surface_t* s = new egl_surface_t(dpy, config, window, surface, cnx);
             return s;
         }
 
@@ -401,11 +284,9 @@
     egl_connection_t* cnx = validate_display_config(dpy, config, dp);
     if (cnx) {
         EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
-                dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-                dp->configs[intptr_t(config)].config, pixmap, attrib_list);
+                dp->disp.dpy, config, pixmap, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface,
-                    dp->configs[intptr_t(config)].impl, cnx);
+            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx);
             return s;
         }
     }
@@ -421,11 +302,9 @@
     egl_connection_t* cnx = validate_display_config(dpy, config, dp);
     if (cnx) {
         EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
-                dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-                dp->configs[intptr_t(config)].config, attrib_list);
+                dp->disp.dpy, config, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface,
-                    dp->configs[intptr_t(config)].impl, cnx);
+            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx);
             return s;
         }
     }
@@ -444,8 +323,7 @@
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
     egl_surface_t * const s = get_surface(surface);
-    EGLBoolean result = s->cnx->egl.eglDestroySurface(
-            dp->disp[s->impl].dpy, s->surface);
+    EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
     if (result == EGL_TRUE) {
         _s.terminate();
     }
@@ -465,16 +343,8 @@
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
-    EGLBoolean result(EGL_TRUE);
-    if (attribute == EGL_CONFIG_ID) {
-        // We need to remap EGL_CONFIG_IDs
-        *value = dp->configs[intptr_t(s->config)].configId;
-    } else {
-        result = s->cnx->egl.eglQuerySurface(
-                dp->disp[s->impl].dpy, s->surface, attribute, value);
-    }
-
-    return result;
+    return s->cnx->egl.eglQuerySurface(
+            dp->disp.dpy, s->surface, attribute, value);
 }
 
 void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
@@ -514,9 +384,7 @@
             share_list = c->context;
         }
         EGLContext context = cnx->egl.eglCreateContext(
-                dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-                dp->configs[intptr_t(config)].config,
-                share_list, attrib_list);
+                dp->disp.dpy, config, share_list, attrib_list);
         if (context != EGL_NO_CONTEXT) {
             // figure out if it's a GLESv1 or GLESv2
             int version = 0;
@@ -526,15 +394,14 @@
                     GLint value = *attrib_list++;
                     if (attr == EGL_CONTEXT_CLIENT_VERSION) {
                         if (value == 1) {
-                            version = GLESv1_INDEX;
+                            version = egl_connection_t::GLESv1_INDEX;
                         } else if (value == 2) {
-                            version = GLESv2_INDEX;
+                            version = egl_connection_t::GLESv2_INDEX;
                         }
                     }
                 };
             }
-            egl_context_t* c = new egl_context_t(dpy, context, config,
-                    dp->configs[intptr_t(config)].impl, cnx, version);
+            egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
 #if EGL_TRACE
             if (gEGLDebugLevel > 0)
                 GLTrace_eglCreateContext(version, c);
@@ -558,8 +425,7 @@
         return setError(EGL_BAD_CONTEXT, EGL_FALSE);
     
     egl_context_t * const c = get_context(ctx);
-    EGLBoolean result = c->cnx->egl.eglDestroyContext(
-            dp->disp[c->impl].dpy, c->context);
+    EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
     if (result == EGL_TRUE) {
         _c.terminate();
     }
@@ -625,20 +491,12 @@
     // retrieve the underlying implementation's draw EGLSurface
     if (draw != EGL_NO_SURFACE) {
         d = get_surface(draw);
-        // make sure the EGLContext and EGLSurface passed in are for
-        // the same driver
-        if (c && d->impl != c->impl)
-            return setError(EGL_BAD_MATCH, EGL_FALSE);
         impl_draw = d->surface;
     }
 
     // retrieve the underlying implementation's read EGLSurface
     if (read != EGL_NO_SURFACE) {
         r = get_surface(read);
-        // make sure the EGLContext and EGLSurface passed in are for
-        // the same driver
-        if (c && r->impl != c->impl)
-            return setError(EGL_BAD_MATCH, EGL_FALSE);
         impl_read = r->surface;
     }
 
@@ -682,17 +540,9 @@
     if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
 
     egl_context_t * const c = get_context(ctx);
+    return c->cnx->egl.eglQueryContext(
+            dp->disp.dpy, c->context, attribute, value);
 
-    EGLBoolean result(EGL_TRUE);
-    if (attribute == EGL_CONFIG_ID) {
-        *value = dp->configs[intptr_t(c->config)].configId;
-    } else {
-        // We need to remap EGL_CONFIG_IDs
-        result = c->cnx->egl.eglQueryContext(
-                dp->disp[c->impl].dpy, c->context, attribute, value);
-    }
-
-    return result;
 }
 
 EGLContext eglGetCurrentContext(void)
@@ -744,87 +594,37 @@
 
 EGLBoolean eglWaitGL(void)
 {
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would return GL_TRUE, which isn't wrong.
-
     clearError();
 
-    EGLBoolean res = EGL_TRUE;
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        if (uint32_t(c->impl)>=2)
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        egl_connection_t* const cnx = &gEGLImpl[c->impl];
-        if (!cnx->dso) 
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        res = cnx->egl.eglWaitGL();
-    }
-    return res;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+
+    return cnx->egl.eglWaitGL();
 }
 
 EGLBoolean eglWaitNative(EGLint engine)
 {
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would return GL_TRUE, which isn't wrong.
-
     clearError();
 
-    EGLBoolean res = EGL_TRUE;
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        if (uint32_t(c->impl)>=2)
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        egl_connection_t* const cnx = &gEGLImpl[c->impl];
-        if (!cnx->dso) 
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        res = cnx->egl.eglWaitNative(engine);
-    }
-    return res;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+
+    return cnx->egl.eglWaitNative(engine);
 }
 
 EGLint eglGetError(void)
 {
-    EGLint result = EGL_SUCCESS;
-    EGLint err;
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        err = EGL_SUCCESS;
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso)
-            err = cnx->egl.eglGetError();
-        if (err!=EGL_SUCCESS && result==EGL_SUCCESS)
-            result = err;
+    EGLint err = EGL_SUCCESS;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        err = cnx->egl.eglGetError();
     }
-    err = egl_tls_t::getError();
-    if (result == EGL_SUCCESS)
-        result = err;
-    return result;
-}
-
-// Note: Similar implementations of these functions also exist in
-// gl2.cpp and gl.cpp, and are used by applications that call the
-// exported entry points directly.
-typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image);
-typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image);
-
-static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES_impl = NULL;
-static PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES_impl = NULL;
-
-static void glEGLImageTargetTexture2DOES_wrapper(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage =
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    glEGLImageTargetTexture2DOES_impl(target, implImage);
-}
-
-static void glEGLImageTargetRenderbufferStorageOES_wrapper(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage =
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    glEGLImageTargetRenderbufferStorageOES_impl(target, implImage);
+    if (err == EGL_SUCCESS) {
+        err = egl_tls_t::getError();
+    }
+    return err;
 }
 
 __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
@@ -885,32 +685,22 @@
 
         if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
             bool found = false;
-            for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-                egl_connection_t* const cnx = &gEGLImpl[i];
-                if (cnx->dso && cnx->egl.eglGetProcAddress) {
-                    found = true;
-                    // Extensions are independent of the bound context
-                    cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] =
-                    cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] =
+
+            egl_connection_t* const cnx = &gEGLImpl;
+            if (cnx->dso && cnx->egl.eglGetProcAddress) {
+                found = true;
+                // Extensions are independent of the bound context
+                cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] =
 #if EGL_TRACE
-                    debugHooks->ext.extensions[slot] =
-                    gHooksTrace.ext.extensions[slot] =
+                debugHooks->ext.extensions[slot] =
+                gHooksTrace.ext.extensions[slot] =
 #endif
-                            cnx->egl.eglGetProcAddress(procname);
-                }
+                        cnx->egl.eglGetProcAddress(procname);
             }
+
             if (found) {
                 addr = gExtensionForwarders[slot];
-
-                if (!strcmp(procname, "glEGLImageTargetTexture2DOES")) {
-                    glEGLImageTargetTexture2DOES_impl = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)addr;
-                    addr = (__eglMustCastToProperFunctionPointerType)glEGLImageTargetTexture2DOES_wrapper;
-                }
-                if (!strcmp(procname, "glEGLImageTargetRenderbufferStorageOES")) {
-                    glEGLImageTargetRenderbufferStorageOES_impl = (PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC)addr;
-                    addr = (__eglMustCastToProperFunctionPointerType)glEGLImageTargetRenderbufferStorageOES_wrapper;
-                }
-
                 sGLExtentionMap.add(name, addr);
                 sGLExtentionSlot++;
             }
@@ -937,7 +727,7 @@
 #endif
 
     egl_surface_t const * const s = get_surface(draw);
-    return s->cnx->egl.eglSwapBuffers(dp->disp[s->impl].dpy, s->surface);
+    return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
 }
 
 EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
@@ -953,8 +743,7 @@
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
-    return s->cnx->egl.eglCopyBuffers(
-            dp->disp[s->impl].dpy, s->surface, target);
+    return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
 }
 
 const char* eglQueryString(EGLDisplay dpy, EGLint name)
@@ -973,12 +762,8 @@
             return dp->getExtensionString();
         case EGL_CLIENT_APIS:
             return dp->getClientApiString();
-        case EGL_VERSION_HW_ANDROID: {
-            if (gEGLImpl[IMPL_HARDWARE].dso) {
-                return dp->disp[IMPL_HARDWARE].queryString.version;
-            }
-            return dp->disp[IMPL_SOFTWARE].queryString.version;
-        }
+        case EGL_VERSION_HW_ANDROID:
+            return dp->disp.queryString.version;
     }
     return setError(EGL_BAD_PARAMETER, (const char *)0);
 }
@@ -1003,7 +788,7 @@
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglSurfaceAttrib) {
         return s->cnx->egl.eglSurfaceAttrib(
-                dp->disp[s->impl].dpy, s->surface, attribute, value);
+                dp->disp.dpy, s->surface, attribute, value);
     }
     return setError(EGL_BAD_SURFACE, EGL_FALSE);
 }
@@ -1023,7 +808,7 @@
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglBindTexImage) {
         return s->cnx->egl.eglBindTexImage(
-                dp->disp[s->impl].dpy, s->surface, buffer);
+                dp->disp.dpy, s->surface, buffer);
     }
     return setError(EGL_BAD_SURFACE, EGL_FALSE);
 }
@@ -1043,7 +828,7 @@
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglReleaseTexImage) {
         return s->cnx->egl.eglReleaseTexImage(
-                dp->disp[s->impl].dpy, s->surface, buffer);
+                dp->disp.dpy, s->surface, buffer);
     }
     return setError(EGL_BAD_SURFACE, EGL_FALSE);
 }
@@ -1056,17 +841,11 @@
     if (!dp) return EGL_FALSE;
 
     EGLBoolean res = EGL_TRUE;
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglSwapInterval) {
-                if (cnx->egl.eglSwapInterval(
-                        dp->disp[i].dpy, interval) == EGL_FALSE) {
-                    res = EGL_FALSE;
-                }
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglSwapInterval) {
+        res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
     }
+
     return res;
 }
 
@@ -1079,23 +858,15 @@
 {
     clearError();
 
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would return GL_TRUE, which isn't wrong.
-    EGLBoolean res = EGL_TRUE;
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        if (uint32_t(c->impl)>=2)
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        egl_connection_t* const cnx = &gEGLImpl[c->impl];
-        if (!cnx->dso) 
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        if (cnx->egl.eglWaitClient) {
-            res = cnx->egl.eglWaitClient();
-        } else {
-            res = cnx->egl.eglWaitGL();
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+
+    EGLBoolean res;
+    if (cnx->egl.eglWaitClient) {
+        res = cnx->egl.eglWaitClient();
+    } else {
+        res = cnx->egl.eglWaitGL();
     }
     return res;
 }
@@ -1110,15 +881,9 @@
 
     // bind this API on all EGLs
     EGLBoolean res = EGL_TRUE;
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglBindAPI) {
-                if (cnx->egl.eglBindAPI(api) == EGL_FALSE) {
-                    res = EGL_FALSE;
-                }
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglBindAPI) {
+        res = cnx->egl.eglBindAPI(api);
     }
     return res;
 }
@@ -1131,16 +896,11 @@
         return setError(EGL_BAD_PARAMETER, EGL_FALSE);
     }
 
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglQueryAPI) {
-                // the first one we find is okay, because they all
-                // should be the same
-                return cnx->egl.eglQueryAPI();
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryAPI) {
+        return cnx->egl.eglQueryAPI();
     }
+
     // or, it can only be OpenGL ES
     return EGL_OPENGL_ES_API;
 }
@@ -1152,14 +912,11 @@
     // If there is context bound to the thread, release it
     egl_display_t::loseCurrent(get_context(getContext()));
 
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglReleaseThread) {
-                cnx->egl.eglReleaseThread();
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglReleaseThread) {
+        cnx->egl.eglReleaseThread();
     }
+
     egl_tls_t::clearTLS();
 #if EGL_TRACE
     if (gEGLDebugLevel > 0)
@@ -1179,9 +936,7 @@
     if (!cnx) return EGL_FALSE;
     if (cnx->egl.eglCreatePbufferFromClientBuffer) {
         return cnx->egl.eglCreatePbufferFromClientBuffer(
-                dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-                buftype, buffer,
-                dp->configs[intptr_t(config)].config, attrib_list);
+                dp->disp.dpy, buftype, buffer, config, attrib_list);
     }
     return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
 }
@@ -1205,7 +960,7 @@
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglLockSurfaceKHR) {
         return s->cnx->egl.eglLockSurfaceKHR(
-                dp->disp[s->impl].dpy, s->surface, attrib_list);
+                dp->disp.dpy, s->surface, attrib_list);
     }
     return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 }
@@ -1223,8 +978,7 @@
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglUnlockSurfaceKHR) {
-        return s->cnx->egl.eglUnlockSurfaceKHR(
-                dp->disp[s->impl].dpy, s->surface);
+        return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
     }
     return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 }
@@ -1237,67 +991,18 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_NO_IMAGE_KHR;
 
-    if (ctx != EGL_NO_CONTEXT) {
-        ContextRef _c(dp, ctx);
-        if (!_c.get())
-            return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
-        egl_context_t * const c = get_context(ctx);
-        // since we have an EGLContext, we know which implementation to use
-        EGLImageKHR image = c->cnx->egl.eglCreateImageKHR(
-                dp->disp[c->impl].dpy, c->context, target, buffer, attrib_list);
-        if (image == EGL_NO_IMAGE_KHR)
-            return image;
-            
-        egl_image_t* result = new egl_image_t(dpy, ctx);
-        result->images[c->impl] = image;
-        return (EGLImageKHR)result;
-    } else {
-        // EGL_NO_CONTEXT is a valid parameter
+    ContextRef _c(dp, ctx);
+    egl_context_t * const c = _c.get();
 
-        /* Since we don't have a way to know which implementation to call,
-         * we're calling all of them. If at least one of the implementation
-         * succeeded, this is a success.
-         */
-
-        EGLint currentError = eglGetError();
-
-        EGLImageKHR implImages[IMPL_NUM_IMPLEMENTATIONS];
-        bool success = false;
-        for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-            egl_connection_t* const cnx = &gEGLImpl[i];
-            implImages[i] = EGL_NO_IMAGE_KHR;
-            if (cnx->dso) {
-                if (cnx->egl.eglCreateImageKHR) {
-                    implImages[i] = cnx->egl.eglCreateImageKHR(
-                            dp->disp[i].dpy, ctx, target, buffer, attrib_list);
-                    if (implImages[i] != EGL_NO_IMAGE_KHR) {
-                        success = true;
-                    }
-                }
-            }
-        }
-
-        if (!success) {
-            // failure, if there was an error when we entered this function,
-            // the error flag must not be updated.
-            // Otherwise, the error is whatever happened in the implementation
-            // that faulted.
-            if (currentError != EGL_SUCCESS) {
-                setError(currentError, EGL_NO_IMAGE_KHR);
-            }
-            return EGL_NO_IMAGE_KHR;
-        } else {
-            // In case of success, we need to clear all error flags
-            // (especially those caused by the implementation that didn't
-            // succeed). TODO: we could avoid this if we knew this was
-            // a "full" success (all implementation succeeded).
-            eglGetError();
-        }
-
-        egl_image_t* result = new egl_image_t(dpy, ctx);
-        memcpy(result->images, implImages, sizeof(implImages));
-        return (EGLImageKHR)result;
+    EGLImageKHR result = EGL_NO_IMAGE_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateImageKHR) {
+        result = cnx->egl.eglCreateImageKHR(
+                dp->disp.dpy,
+                c ? c->context : EGL_NO_CONTEXT,
+                target, buffer, attrib_list);
     }
+    return result;
 }
 
 EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
@@ -1307,29 +1012,10 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    ImageRef _i(dp, img);
-    if (!_i.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-
-    egl_image_t* image = get_image(img);
-    bool success = false;
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (image->images[i] != EGL_NO_IMAGE_KHR) {
-            if (cnx->dso) {
-                if (cnx->egl.eglDestroyImageKHR) {
-                    if (cnx->egl.eglDestroyImageKHR(
-                            dp->disp[i].dpy, image->images[i])) {
-                        success = true;
-                    }
-                }
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDestroyImageKHR) {
+        cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img);
     }
-    if (!success)
-        return EGL_FALSE;
-
-    _i.terminate();
-
     return EGL_TRUE;
 }
 
@@ -1345,21 +1031,12 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_NO_SYNC_KHR;
 
-    EGLContext ctx = eglGetCurrentContext();
-    ContextRef _c(dp, ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_NO_SYNC_KHR);
-
-    egl_context_t * const c = get_context(ctx);
     EGLSyncKHR result = EGL_NO_SYNC_KHR;
-    if (c->cnx->egl.eglCreateSyncKHR) {
-        EGLSyncKHR sync = c->cnx->egl.eglCreateSyncKHR(
-                dp->disp[c->impl].dpy, type, attrib_list);
-        if (sync == EGL_NO_SYNC_KHR)
-            return sync;
-        result = (egl_sync_t*)new egl_sync_t(dpy, ctx, sync);
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateSyncKHR) {
+        result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list);
     }
-    return (EGLSyncKHR)result;
+    return result;
 }
 
 EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
@@ -1369,75 +1046,46 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SyncRef _s(dp, sync);
-    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    egl_sync_t* syncObject = get_sync(sync);
-
-    EGLContext ctx = syncObject->context;
-    ContextRef _c(dp, ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-
     EGLBoolean result = EGL_FALSE;
-    egl_context_t * const c = get_context(ctx);
-    if (c->cnx->egl.eglDestroySyncKHR) {
-        result = c->cnx->egl.eglDestroySyncKHR(
-                dp->disp[c->impl].dpy, syncObject->sync);
-        if (result)
-            _s.terminate();
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDestroySyncKHR) {
+        result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync);
     }
     return result;
 }
 
-EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync,
+        EGLint flags, EGLTimeKHR timeout)
 {
     clearError();
 
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SyncRef _s(dp, sync);
-    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    egl_sync_t* syncObject = get_sync(sync);
-
-    EGLContext ctx = syncObject->context;
-    ContextRef _c(dp, ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-
-    egl_context_t * const c = get_context(ctx);
-    if (c->cnx->egl.eglClientWaitSyncKHR) {
-        return c->cnx->egl.eglClientWaitSyncKHR(
-                dp->disp[c->impl].dpy, syncObject->sync, flags, timeout);
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
+        result = cnx->egl.eglClientWaitSyncKHR(
+                dp->disp.dpy, sync, flags, timeout);
     }
-
-    return EGL_FALSE;
+    return result;
 }
 
-EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value)
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
+        EGLint attribute, EGLint *value)
 {
     clearError();
 
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SyncRef _s(dp, sync);
-    if (!_s.get())
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-
-    egl_sync_t* syncObject = get_sync(sync);
-    EGLContext ctx = syncObject->context;
-    ContextRef _c(dp, ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-
-    egl_context_t * const c = get_context(ctx);
-    if (c->cnx->egl.eglGetSyncAttribKHR) {
-        return c->cnx->egl.eglGetSyncAttribKHR(
-                dp->disp[c->impl].dpy, syncObject->sync, attribute, value);
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) {
+        result = cnx->egl.eglGetSyncAttribKHR(
+                dp->disp.dpy, sync, attribute, value);
     }
-
-    return EGL_FALSE;
+    return result;
 }
 
 // ----------------------------------------------------------------------------
@@ -1458,12 +1106,10 @@
     }
 
     EGLuint64NV ret = 0;
-    egl_connection_t* const cnx = &gEGLImpl[IMPL_HARDWARE];
+    egl_connection_t* const cnx = &gEGLImpl;
 
-    if (cnx->dso) {
-        if (cnx->egl.eglGetSystemTimeFrequencyNV) {
-            return cnx->egl.eglGetSystemTimeFrequencyNV();
-        }
+    if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
+        return cnx->egl.eglGetSystemTimeFrequencyNV();
     }
 
     return setErrorQuiet(EGL_BAD_DISPLAY, 0);
@@ -1478,12 +1124,10 @@
     }
 
     EGLuint64NV ret = 0;
-    egl_connection_t* const cnx = &gEGLImpl[IMPL_HARDWARE];
+    egl_connection_t* const cnx = &gEGLImpl;
 
-    if (cnx->dso) {
-        if (cnx->egl.eglGetSystemTimeNV) {
-            return cnx->egl.eglGetSystemTimeNV();
-        }
+    if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
+        return cnx->egl.eglGetSystemTimeNV();
     }
 
     return setErrorQuiet(EGL_BAD_DISPLAY, 0);
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 7fd6519..c79fb5f 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -83,39 +83,39 @@
 
 void egl_cache_t::initialize(egl_display_t *display) {
     Mutex::Autolock lock(mMutex);
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
-            const char* exts = display->disp[i].queryString.extensions;
-            size_t bcExtLen = strlen(BC_EXT_STR);
-            size_t extsLen = strlen(exts);
-            bool equal = !strcmp(BC_EXT_STR, exts);
-            bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
-            bool atEnd = (bcExtLen+1) < extsLen &&
-                    !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
-            bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
-            if (equal || atStart || atEnd || inMiddle) {
-                PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
-                eglSetBlobCacheFuncsANDROID =
-                        reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
+        const char* exts = display->disp.queryString.extensions;
+        size_t bcExtLen = strlen(BC_EXT_STR);
+        size_t extsLen = strlen(exts);
+        bool equal = !strcmp(BC_EXT_STR, exts);
+        bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
+        bool atEnd = (bcExtLen+1) < extsLen &&
+                !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
+        bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
+        if (equal || atStart || atEnd || inMiddle) {
+            PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
+            eglSetBlobCacheFuncsANDROID =
+                    reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
                             cnx->egl.eglGetProcAddress(
                                     "eglSetBlobCacheFuncsANDROID"));
-                if (eglSetBlobCacheFuncsANDROID == NULL) {
-                    ALOGE("EGL_ANDROID_blob_cache advertised by display %d, "
-                            "but unable to get eglSetBlobCacheFuncsANDROID", i);
-                    continue;
-                }
+            if (eglSetBlobCacheFuncsANDROID == NULL) {
+                ALOGE("EGL_ANDROID_blob_cache advertised, "
+                        "but unable to get eglSetBlobCacheFuncsANDROID");
+                return;
+            }
 
-                eglSetBlobCacheFuncsANDROID(display->disp[i].dpy,
-                        android::setBlob, android::getBlob);
-                EGLint err = cnx->egl.eglGetError();
-                if (err != EGL_SUCCESS) {
-                    ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
-                            "%#x", err);
-                }
+            eglSetBlobCacheFuncsANDROID(display->disp.dpy,
+                    android::setBlob, android::getBlob);
+            EGLint err = cnx->egl.eglGetError();
+            if (err != EGL_SUCCESS) {
+                ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
+                        "%#x", err);
             }
         }
     }
+
     mInitialized = true;
 }
 
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 6b2ae51..c85b4ce 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -60,18 +60,12 @@
 extern void initEglTraceLevel();
 extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
 
-static int cmp_configs(const void* a, const void *b) {
-    const egl_config_t& c0 = *(egl_config_t const *)a;
-    const egl_config_t& c1 = *(egl_config_t const *)b;
-    return c0<c1 ? -1 : (c1<c0 ? 1 : 0);
-}
-
 // ----------------------------------------------------------------------------
 
 egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
 
 egl_display_t::egl_display_t() :
-    magic('_dpy'), numTotalConfigs(0), configs(0), refs(0) {
+    magic('_dpy'), refs(0) {
 }
 
 egl_display_t::~egl_display_t() {
@@ -119,15 +113,13 @@
     // get our driver loader
     Loader& loader(Loader::getInstance());
 
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso && disp[i].dpy == EGL_NO_DISPLAY) {
-            EGLDisplay dpy = cnx->egl.eglGetDisplay(display);
-            disp[i].dpy = dpy;
-            if (dpy == EGL_NO_DISPLAY) {
-                loader.close(cnx->dso);
-                cnx->dso = NULL;
-            }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) {
+        EGLDisplay dpy = cnx->egl.eglGetDisplay(display);
+        disp.dpy = dpy;
+        if (dpy == EGL_NO_DISPLAY) {
+            loader.close(cnx->dso);
+            cnx->dso = NULL;
         }
     }
 
@@ -160,12 +152,11 @@
     // initialize each EGL and
     // build our own extension string first, based on the extension we know
     // and the extension supported by our client implementation
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        cnx->major = -1;
-        cnx->minor = -1;
-        if (!cnx->dso)
-            continue;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    cnx->major = -1;
+    cnx->minor = -1;
+    if (cnx->dso) {
 
 #if defined(ADRENO130)
 #warning "Adreno-130 eglInitialize() workaround"
@@ -177,31 +168,30 @@
          * eglGetDisplay() before calling eglInitialize();
          */
         if (i == IMPL_HARDWARE) {
-            disp[i].dpy =
-            cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+            disp[i].dpy = cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
         }
 #endif
 
-        EGLDisplay idpy = disp[i].dpy;
+        EGLDisplay idpy = disp.dpy;
         if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
-            //ALOGD("initialized %d dpy=%p, ver=%d.%d, cnx=%p",
-            //        i, idpy, cnx->major, cnx->minor, cnx);
+            //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
+            //        idpy, cnx->major, cnx->minor, cnx);
 
             // display is now initialized
-            disp[i].state = egl_display_t::INITIALIZED;
+            disp.state = egl_display_t::INITIALIZED;
 
             // get the query-strings for this display for each implementation
-            disp[i].queryString.vendor = cnx->egl.eglQueryString(idpy,
+            disp.queryString.vendor = cnx->egl.eglQueryString(idpy,
                     EGL_VENDOR);
-            disp[i].queryString.version = cnx->egl.eglQueryString(idpy,
+            disp.queryString.version = cnx->egl.eglQueryString(idpy,
                     EGL_VERSION);
-            disp[i].queryString.extensions = cnx->egl.eglQueryString(idpy,
+            disp.queryString.extensions = cnx->egl.eglQueryString(idpy,
                     EGL_EXTENSIONS);
-            disp[i].queryString.clientApi = cnx->egl.eglQueryString(idpy,
+            disp.queryString.clientApi = cnx->egl.eglQueryString(idpy,
                     EGL_CLIENT_APIS);
 
         } else {
-            ALOGW("%d: eglInitialize(%p) failed (%s)", i, idpy,
+            ALOGW("eglInitialize(%p) failed (%s)", idpy,
                     egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
         }
     }
@@ -211,7 +201,7 @@
     mVersionString.setTo(sVersionString);
     mClientApiString.setTo(sClientApiString);
 
-    // we only add extensions that exist in at least one implementation
+    // we only add extensions that exist in the implementation
     char const* start = sExtensionString;
     char const* end;
     do {
@@ -223,15 +213,13 @@
             if (len) {
                 // NOTE: we could avoid the copy if we had strnstr.
                 const String8 ext(start, len);
-                // now go through all implementations and look for this extension
-                for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-                    if (disp[i].queryString.extensions) {
-                        // if we find it, add this extension string to our list
-                        // (and don't forget the space)
-                        const char* match = strstr(disp[i].queryString.extensions, ext.string());
-                        if (match && (match[len] == ' ' || match[len] == 0)) {
-                            mExtensionString.append(start, len+1);
-                        }
+                // now look for this extension
+                if (disp.queryString.extensions) {
+                    // if we find it, add this extension string to our list
+                    // (and don't forget the space)
+                    const char* match = strstr(disp.queryString.extensions, ext.string());
+                    if (match && (match[len] == ' ' || match[len] == 0)) {
+                        mExtensionString.append(start, len+1);
                     }
                 }
             }
@@ -242,52 +230,12 @@
 
     egl_cache_t::get()->initialize(this);
 
-    EGLBoolean res = EGL_FALSE;
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
-            EGLint n;
-            if (cnx->egl.eglGetConfigs(disp[i].dpy, 0, 0, &n)) {
-                disp[i].config = (EGLConfig*) malloc(sizeof(EGLConfig) * n);
-                if (disp[i].config) {
-                    if (cnx->egl.eglGetConfigs(disp[i].dpy, disp[i].config, n,
-                            &disp[i].numConfigs)) {
-                        numTotalConfigs += n;
-                        res = EGL_TRUE;
-                    }
-                }
-            }
-        }
-    }
-
-    if (res == EGL_TRUE) {
-        configs = new egl_config_t[numTotalConfigs];
-        for (int i = 0, k = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-            egl_connection_t* const cnx = &gEGLImpl[i];
-            if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
-                for (int j = 0; j < disp[i].numConfigs; j++) {
-                    configs[k].impl = i;
-                    configs[k].config = disp[i].config[j];
-                    configs[k].configId = k + 1; // CONFIG_ID start at 1
-                    // store the implementation's CONFIG_ID
-                    cnx->egl.eglGetConfigAttrib(disp[i].dpy, disp[i].config[j],
-                            EGL_CONFIG_ID, &configs[k].implConfigId);
-                    k++;
-                }
-            }
-        }
-
-        // sort our configurations so we can do binary-searches
-        qsort(configs, numTotalConfigs, sizeof(egl_config_t), cmp_configs);
-
-        refs++;
-        if (major != NULL)
-            *major = VERSION_MAJOR;
-        if (minor != NULL)
-            *minor = VERSION_MINOR;
-        return EGL_TRUE;
-    }
-    return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
+    refs++;
+    if (major != NULL)
+        *major = VERSION_MAJOR;
+    if (minor != NULL)
+        *minor = VERSION_MINOR;
+    return EGL_TRUE;
 }
 
 EGLBoolean egl_display_t::terminate() {
@@ -305,22 +253,15 @@
     }
 
     EGLBoolean res = EGL_FALSE;
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso && disp[i].state == egl_display_t::INITIALIZED) {
-            if (cnx->egl.eglTerminate(disp[i].dpy) == EGL_FALSE) {
-                ALOGW("%d: eglTerminate(%p) failed (%s)", i, disp[i].dpy,
-                        egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
-            }
-            // REVISIT: it's unclear what to do if eglTerminate() fails
-            free(disp[i].config);
-
-            disp[i].numConfigs = 0;
-            disp[i].config = 0;
-            disp[i].state = egl_display_t::TERMINATED;
-
-            res = EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
+        if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
+            ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
+                    egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
         }
+        // REVISIT: it's unclear what to do if eglTerminate() fails
+        disp.state = egl_display_t::TERMINATED;
+        res = EGL_TRUE;
     }
 
     // Mark all objects remaining in the list as terminated, unless
@@ -337,8 +278,6 @@
     objects.clear();
 
     refs--;
-    numTotalConfigs = 0;
-    delete[] configs;
     return res;
 }
 
@@ -390,13 +329,13 @@
         Mutex::Autolock _l(lock);
         if (c) {
             result = c->cnx->egl.eglMakeCurrent(
-                    disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
+                    disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 c->onMakeCurrent(draw, read);
             }
         } else {
             result = cur_c->cnx->egl.eglMakeCurrent(
-                    disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
+                    disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 cur_c->onLooseCurrent();
             }
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index f3c4ddf..6348228 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -44,23 +44,6 @@
 
 // ----------------------------------------------------------------------------
 
-struct egl_config_t {
-    egl_config_t() {}
-    egl_config_t(int impl, EGLConfig config)
-        : impl(impl), config(config), configId(0), implConfigId(0) { }
-    int         impl;           // the implementation this config is for
-    EGLConfig   config;         // the implementation's EGLConfig
-    EGLint      configId;       // our CONFIG_ID
-    EGLint      implConfigId;   // the implementation's CONFIG_ID
-    inline bool operator < (const egl_config_t& rhs) const {
-        if (impl < rhs.impl) return true;
-        if (impl > rhs.impl) return false;
-        return config < rhs.config;
-    }
-};
-
-// ----------------------------------------------------------------------------
-
 class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
     static egl_display_t sDisplay[NUM_DISPLAYS];
     EGLDisplay getDisplay(EGLNativeDisplayType display);
@@ -113,12 +96,9 @@
     };
 
     struct DisplayImpl {
-        DisplayImpl() : dpy(EGL_NO_DISPLAY), config(0),
-                        state(NOT_INITIALIZED), numConfigs(0) { }
+        DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) { }
         EGLDisplay  dpy;
-        EGLConfig*  config;
         EGLint      state;
-        EGLint      numConfigs;
         strings_t   queryString;
     };
 
@@ -126,9 +106,7 @@
     uint32_t        magic;
 
 public:
-    DisplayImpl     disp[IMPL_NUM_IMPLEMENTATIONS];
-    EGLint          numTotalConfigs;
-    egl_config_t*   configs;
+    DisplayImpl     disp;
 
 private:
             uint32_t                    refs;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index b660c53..d0cbb31 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -64,9 +64,9 @@
 // ----------------------------------------------------------------------------
 
 egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-        int impl, egl_connection_t const* cnx, int version) :
+        egl_connection_t const* cnx, int version) :
     egl_object_t(get_display(dpy)), dpy(dpy), context(context),
-            config(config), read(0), draw(0), impl(impl), cnx(cnx),
+            config(config), read(0), draw(0), cnx(cnx),
             version(version)
 {
 }
@@ -87,7 +87,7 @@
 
     if (gl_extensions.isEmpty()) {
         // call the implementation's glGetString(GL_EXTENSIONS)
-        const char* exts = (const char *)gEGLImpl[impl].hooks[version]->gl.glGetString(GL_EXTENSIONS);
+        const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
         gl_extensions.setTo(exts);
         if (gl_extensions.find("GL_EXT_debug_marker") < 0) {
             String8 temp("GL_EXT_debug_marker ");
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index abd4cbb..4d91f54 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -125,7 +125,7 @@
 
 // ----------------------------------------------------------------------------
 
-class egl_surface_t: public egl_object_t {
+class egl_surface_t : public egl_object_t {
 protected:
     ~egl_surface_t() {
         ANativeWindow* const window = win.get();
@@ -140,15 +140,14 @@
     typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
 
     egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win,
-            EGLSurface surface, int impl, egl_connection_t const* cnx) :
+            EGLSurface surface, egl_connection_t const* cnx) :
         egl_object_t(get_display(dpy)), dpy(dpy), surface(surface),
-                config(config), win(win), impl(impl), cnx(cnx) {
+                config(config), win(win), cnx(cnx) {
     }
     EGLDisplay dpy;
     EGLSurface surface;
     EGLConfig config;
     sp<ANativeWindow> win;
-    int impl;
     egl_connection_t const* cnx;
 };
 
@@ -159,7 +158,7 @@
     typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
 
     egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-            int impl, egl_connection_t const* cnx, int version);
+            egl_connection_t const* cnx, int version);
 
     void onLooseCurrent();
     void onMakeCurrent(EGLSurface draw, EGLSurface read);
@@ -169,47 +168,15 @@
     EGLConfig config;
     EGLSurface read;
     EGLSurface draw;
-    int impl;
     egl_connection_t const* cnx;
     int version;
     String8 gl_extensions;
 };
 
-class egl_image_t: public egl_object_t {
-protected:
-    ~egl_image_t() {}
-public:
-    typedef egl_object_t::LocalRef<egl_image_t, EGLImageKHR> Ref;
-
-    egl_image_t(EGLDisplay dpy, EGLContext context) :
-        egl_object_t(get_display(dpy)), dpy(dpy), context(context) {
-        memset(images, 0, sizeof(images));
-    }
-    EGLDisplay dpy;
-    EGLContext context;
-    EGLImageKHR images[IMPL_NUM_IMPLEMENTATIONS];
-};
-
-class egl_sync_t: public egl_object_t {
-protected:
-    ~egl_sync_t() {}
-public:
-    typedef egl_object_t::LocalRef<egl_sync_t, EGLSyncKHR> Ref;
-
-    egl_sync_t(EGLDisplay dpy, EGLContext context, EGLSyncKHR sync) :
-        egl_object_t(get_display(dpy)), dpy(dpy), context(context), sync(sync) {
-    }
-    EGLDisplay dpy;
-    EGLContext context;
-    EGLSyncKHR sync;
-};
-
 // ----------------------------------------------------------------------------
 
 typedef egl_surface_t::Ref  SurfaceRef;
 typedef egl_context_t::Ref  ContextRef;
-typedef egl_image_t::Ref    ImageRef;
-typedef egl_sync_t::Ref     SyncRef;
 
 // ----------------------------------------------------------------------------
 
@@ -228,16 +195,6 @@
     return egl_to_native_cast<egl_context_t>(context);
 }
 
-static inline
-egl_image_t* get_image(EGLImageKHR image) {
-    return egl_to_native_cast<egl_image_t>(image);
-}
-
-static inline
-egl_sync_t* get_sync(EGLSyncKHR sync) {
-    return egl_to_native_cast<egl_sync_t>(sync);
-}
-
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index ff20957..c900c1c 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -19,31 +19,24 @@
 
 #include "hooks.h"
 
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 4
+
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
 
-#define VERSION_MAJOR 1
-#define VERSION_MINOR 4
-
 //  EGLDisplay are global, not attached to a given thread
 const unsigned int NUM_DISPLAYS = 1;
 
-enum {
-    IMPL_HARDWARE = 0,
-    IMPL_SOFTWARE,
-    IMPL_NUM_IMPLEMENTATIONS
-};
-
-enum {
-    GLESv1_INDEX = 0,
-    GLESv2_INDEX = 1,
-};
-
 // ----------------------------------------------------------------------------
 
-struct egl_connection_t
-{
+struct egl_connection_t {
+    enum {
+        GLESv1_INDEX = 0,
+        GLESv2_INDEX = 1
+    };
+
     inline egl_connection_t() : dso(0) { }
     void *              dso;
     gl_hooks_t *        hooks[2];
@@ -54,7 +47,7 @@
 
 // ----------------------------------------------------------------------------
 
-extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
+extern gl_hooks_t gHooks[2];
 extern gl_hooks_t gHooksNoContext;
 extern pthread_key_t gGLWrapperKey;
 extern "C" void gl_unimplemented();
@@ -63,7 +56,7 @@
 extern char const * const gl_names[];
 extern char const * const egl_names[];
 
-extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
+extern egl_connection_t gEGLImpl;
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 79aa3cd..4345c2b 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -124,27 +124,3 @@
     }
     return ret;
 }
-
-/*
- * These GL calls are special because they need to EGL to retrieve some
- * informations before they can execute.
- */
-
-extern "C" void __glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image);
-extern "C" void __glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image);
-
-
-void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage = 
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    __glEGLImageTargetTexture2DOES(target, implImage);
-}
-
-void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage = 
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    __glEGLImageTargetRenderbufferStorageOES(target, implImage);
-}
-
diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in
index a8907fd..c381075 100644
--- a/opengl/libs/GLES2/gl2ext_api.in
+++ b/opengl/libs/GLES2/gl2ext_api.in
@@ -1,7 +1,7 @@
-void API_ENTRY(__glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
+void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
     CALL_GL_API(glEGLImageTargetTexture2DOES, target, image);
 }
-void API_ENTRY(__glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
+void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
     CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image);
 }
 void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary) {
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index adeaa5b..adcb60d 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -179,27 +179,3 @@
     }
     return ret;
 }
-
-/*
- * These GL calls are special because they need to EGL to retrieve some
- * informations before they can execute.
- */
-
-extern "C" void __glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image);
-extern "C" void __glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image);
-
-
-void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage = 
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    __glEGLImageTargetTexture2DOES(target, implImage);
-}
-
-void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage = 
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    __glEGLImageTargetRenderbufferStorageOES(target, implImage);
-}
-
diff --git a/opengl/libs/GLES_CM/glext_api.in b/opengl/libs/GLES_CM/glext_api.in
index 268a535..7cd6cb5 100644
--- a/opengl/libs/GLES_CM/glext_api.in
+++ b/opengl/libs/GLES_CM/glext_api.in
@@ -31,10 +31,10 @@
 void API_ENTRY(glDrawTexfvOES)(const GLfloat *coords) {
     CALL_GL_API(glDrawTexfvOES, coords);
 }
-void API_ENTRY(__glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
+void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
     CALL_GL_API(glEGLImageTargetTexture2DOES, target, image);
 }
-void API_ENTRY(__glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
+void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
     CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image);
 }
 void API_ENTRY(glAlphaFuncxOES)(GLenum func, GLclampx ref) {
diff --git a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
index 871b5dc..3e185bc 100644
--- a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
@@ -15,9 +15,13 @@
  */
 
 #include <cutils/log.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
 #include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
 
 #include "gltrace.pb.h"
+#include "gltrace_api.h"
 #include "gltrace_context.h"
 #include "gltrace_fixup.h"
 
@@ -198,6 +202,20 @@
     arg_strpp->add_charvalue(src);
 }
 
+void fixup_glUniformGenericInteger(int argIndex, int nIntegers, GLMessage *glmsg) {
+    /* void glUniform?iv(GLint location, GLsizei count, const GLint *value); */
+    GLMessage_DataType *arg_values = glmsg->mutable_args(argIndex);
+    GLint *src = (GLint*)arg_values->intvalue(0);
+
+    arg_values->set_type(GLMessage::DataType::INT);
+    arg_values->set_isarray(true);
+    arg_values->clear_intvalue();
+
+    for (int i = 0; i < nIntegers; i++) {
+        arg_values->add_intvalue(*src++);
+    }
+}
+
 void fixup_glUniformGeneric(int argIndex, int nFloats, GLMessage *glmsg) {
     GLMessage_DataType *arg_values = glmsg->mutable_args(argIndex);
     GLfloat *src = (GLfloat*)arg_values->intvalue(0);
@@ -223,6 +241,10 @@
     GLMessage_DataType *arg_intarray = glmsg->mutable_args(argIndex);
     GLint *intp = (GLint *)arg_intarray->intvalue(0);
 
+    if (intp == NULL) {
+        return;
+    }
+
     arg_intarray->set_type(GLMessage::DataType::INT);
     arg_intarray->set_isarray(true);
     arg_intarray->clear_intvalue();
@@ -232,6 +254,15 @@
     }
 }
 
+void fixup_GenericEnumArray(int argIndex, int nEnums, GLMessage *glmsg) {
+    // fixup as if they were ints
+    fixup_GenericIntArray(argIndex, nEnums, glmsg);
+
+    // and then set the data type to be enum
+    GLMessage_DataType *arg_enumarray = glmsg->mutable_args(argIndex);
+    arg_enumarray->set_type(GLMessage::DataType::ENUM);
+}
+
 void fixup_glGenGeneric(GLMessage *glmsg) {
     /* void glGen*(GLsizei n, GLuint * buffers); */
     GLMessage_DataType arg_n  = glmsg->args(0);
@@ -270,6 +301,84 @@
     arg_params->add_floatvalue(*src);
 }
 
+void fixup_glLinkProgram(GLMessage *glmsg) {
+    /* void glLinkProgram(GLuint program); */
+    GLuint program = glmsg->args(0).intvalue(0);
+
+    /* We don't have to fixup this call, but as soon as a program is linked,
+       we obtain information about all active attributes and uniforms to
+       pass on to the debugger. Note that in order to pass this info to
+       the debugger, all we need to do is call the trace versions of the
+       necessary calls. */
+
+    GLint n, maxNameLength;
+    GLchar *name;
+    GLint size;
+    GLenum type;
+
+    // obtain info regarding active attributes
+    GLTrace_glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &n);
+    GLTrace_glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameLength);
+
+    name = (GLchar *) malloc(maxNameLength);
+    for (int i = 0; i < n; i++) {
+        GLTrace_glGetActiveAttrib(program, i, maxNameLength, NULL, &size, &type, name);
+    }
+    free(name);
+
+    // obtain info regarding active uniforms
+    GLTrace_glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &n);
+    GLTrace_glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength);
+
+    name = (GLchar *) malloc(maxNameLength);
+    for (int i = 0; i < n; i++) {
+        GLTrace_glGetActiveUniform(program, i, maxNameLength, NULL, &size, &type, name);
+    }
+    free(name);
+}
+
+/** Given a glGetActive[Uniform|Attrib] call, obtain the location
+ *  of the variable in the call.
+ */
+int getShaderVariableLocation(GLTraceContext *context, GLMessage *glmsg) {
+    GLMessage_Function func = glmsg->function();
+    if (func != GLMessage::glGetActiveAttrib && func != GLMessage::glGetActiveUniform) {
+        return -1;
+    }
+
+    int program = glmsg->args(0).intvalue(0);
+    GLchar *name = (GLchar*) glmsg->args(6).intvalue(0);
+
+    if (func == GLMessage::glGetActiveAttrib) {
+        return context->hooks->gl.glGetAttribLocation(program, name);
+    } else {
+        return context->hooks->gl.glGetUniformLocation(program, name);
+    }
+}
+
+void fixup_glGetActiveAttribOrUniform(GLMessage *glmsg, int location) {
+    /* void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize,
+                GLsizei* length, GLint* size, GLenum* type, GLchar* name); */
+    /* void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize,
+                GLsizei* length, GLint* size, GLenum* type, GLchar* name) */
+
+    fixup_GenericIntArray(3, 1, glmsg);     // length
+    fixup_GenericIntArray(4, 1, glmsg);     // size
+    fixup_GenericEnumArray(5, 1, glmsg);    // type
+    fixup_CStringPtr(6, glmsg);             // name
+
+    // The index argument in the glGetActive[Attrib|Uniform] functions
+    // does not correspond to the actual location index as used in
+    // glUniform*() or glVertexAttrib*() to actually upload the data.
+    // In order to make things simpler for the debugger, we also pass
+    // a hidden location argument that stores the actual location.
+    // append the location value to the end of the argument list
+    GLMessage_DataType *arg_location = glmsg->add_args();
+    arg_location->set_isarray(false);
+    arg_location->set_type(GLMessage::DataType::INT);
+    arg_location->add_intvalue(location);
+}
+
 void fixupGLMessage(GLTraceContext *context, nsecs_t start, nsecs_t end, GLMessage *glmsg) {
     // for all messages, set the current context id
     glmsg->set_context_id(context->getId());
@@ -292,6 +401,19 @@
     case GLMessage::glGenTextures:       /* void glGenTextures(GLsizei n, GLuint *textures); */
         fixup_glGenGeneric(glmsg);
         break;
+    case GLMessage::glLinkProgram:       /* void glLinkProgram(GLuint program); */
+        fixup_glLinkProgram(glmsg);
+        break;
+    case GLMessage::glGetActiveAttrib:
+        fixup_glGetActiveAttribOrUniform(glmsg, getShaderVariableLocation(context, glmsg));
+        break;
+    case GLMessage::glGetActiveUniform:
+        fixup_glGetActiveAttribOrUniform(glmsg, getShaderVariableLocation(context, glmsg));
+        break;
+    case GLMessage::glBindAttribLocation:
+        /* void glBindAttribLocation(GLuint program, GLuint index, const GLchar* name); */
+        fixup_CStringPtr(2, glmsg);
+        break;
     case GLMessage::glGetAttribLocation:  
     case GLMessage::glGetUniformLocation: 
         /* int glGetAttribLocation(GLuint program, const GLchar* name) */
@@ -331,6 +453,38 @@
     case GLMessage::glShaderSource:
         fixup_glShaderSource(glmsg);
         break;
+    case GLMessage::glUniform1iv:
+        /* void glUniform1iv(GLint location, GLsizei count, const GLint *value); */
+        fixup_glUniformGenericInteger(2, 1, glmsg);
+        break;
+    case GLMessage::glUniform2iv:
+        /* void glUniform2iv(GLint location, GLsizei count, const GLint *value); */
+        fixup_glUniformGenericInteger(2, 2, glmsg);
+        break;
+    case GLMessage::glUniform3iv:
+        /* void glUniform3iv(GLint location, GLsizei count, const GLint *value); */
+        fixup_glUniformGenericInteger(2, 3, glmsg);
+        break;
+    case GLMessage::glUniform4iv:
+        /* void glUniform4iv(GLint location, GLsizei count, const GLint *value); */
+        fixup_glUniformGenericInteger(2, 4, glmsg);
+        break;
+    case GLMessage::glUniform1fv:
+        /* void glUniform1fv(GLint location, GLsizei count, const GLfloat *value); */
+        fixup_glUniformGeneric(2, 1, glmsg);
+        break;
+    case GLMessage::glUniform2fv:
+        /* void glUniform2fv(GLint location, GLsizei count, const GLfloat *value); */
+        fixup_glUniformGeneric(2, 2, glmsg);
+        break;
+    case GLMessage::glUniform3fv:
+        /* void glUniform3fv(GLint location, GLsizei count, const GLfloat *value); */
+        fixup_glUniformGeneric(2, 3, glmsg);
+        break;
+    case GLMessage::glUniform4fv:
+        /* void glUniform4fv(GLint location, GLsizei count, const GLfloat *value); */
+        fixup_glUniformGeneric(2, 4, glmsg);
+        break;
     case GLMessage::glUniformMatrix2fv:
         /* void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose,
                                                                     const GLfloat* value) */
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index 8ff51ec..cb0e908 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -30,7 +30,6 @@
 // ----------------------------------------------------------------------------
 
 EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
-EGLAPI EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image);
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen
index 9be40cf..4d8334f 100755
--- a/opengl/libs/tools/glapigen
+++ b/opengl/libs/tools/glapigen
@@ -37,12 +37,6 @@
   #printf("%s", $line);
   
   my $prefix = "";
-  if ($name eq "glEGLImageTargetTexture2DOES") {
-    $prefix = "__";
-  }
-  if ($name eq "glEGLImageTargetRenderbufferStorageOES") {
-    $prefix = "__";
-  }
   if ($name eq "glGetString") {
     $prefix = "__";
   }
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index 2d76455..aa27861 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -58,6 +58,7 @@
             android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
             android:scaleType="centerInside"
             android:adjustViewBounds="true"
+            android:visibility="invisible"
         />
 
         <TextView android:id="@+id/app_label"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index b653fcd..bc389f9 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -81,6 +81,7 @@
             android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
             android:scaleType="centerInside"
             android:adjustViewBounds="true"
+            android:visibility="invisible"
         />
 
         <TextView android:id="@+id/app_description"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index cb26db00..333fcda 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -65,6 +65,7 @@
         android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
         android:scaleType="centerInside"
         android:adjustViewBounds="true"
+        android:visibility="invisible"
     />
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 14ce266..cd8bd4e 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -236,6 +236,10 @@
             public void onAnimationEnd(Animator animation) {
                 mCallback.onChildDismissed(view);
                 animView.setLayerType(View.LAYER_TYPE_NONE, null);
+                // Restore the alpha/translation parameters to what they were before swiping
+                // (for when these items are recycled)
+                animView.setAlpha(1f);
+                setTranslation(animView, 0f);
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index ad38a11..dc2f0be 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -134,6 +134,9 @@
 
     void jumpTo(boolean appearing) {
         mContentView.setTranslationY(appearing ? 0 : mPanelHeight);
+        if (mScrimView.getBackground() != null) {
+            mScrimView.getBackground().setAlpha(appearing ? 255 : 0);
+        }
     }
 
     public void setPanelHeight(int h) {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 4145fc4..92f4ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -16,12 +16,6 @@
 
 package com.android.systemui.recent;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -36,15 +30,17 @@
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Process;
-import android.os.SystemClock;
-import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.LruCache;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.tablet.TabletStatusBar;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
 public class RecentTasksLoader {
     static final String TAG = "RecentTasksLoader";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
@@ -55,11 +51,14 @@
     private Context mContext;
     private RecentsPanelView mRecentsPanel;
 
-    private AsyncTask<Void, Integer, Void> mThumbnailLoader;
+    private AsyncTask<Void, ArrayList<TaskDescription>, Void> mTaskLoader;
+    private AsyncTask<Void, TaskDescription, Void> mThumbnailLoader;
     private final Handler mHandler;
 
     private int mIconDpi;
     private Bitmap mDefaultThumbnailBackground;
+    private Bitmap mDefaultIconBackground;
+    private int mNumTasksInFirstScreenful;
 
     public RecentTasksLoader(Context context) {
         mContext = context;
@@ -76,12 +75,20 @@
             mIconDpi = res.getDisplayMetrics().densityDpi;
         }
 
+        // Render default icon (just a blank image)
+        int defaultIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.app_icon_size);
+        int iconSize = (int) (defaultIconSize * mIconDpi / res.getDisplayMetrics().densityDpi);
+        mDefaultIconBackground = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+
         // Render the default thumbnail background
-        int width = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
-        int height = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+        int thumbnailWidth =
+                (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+        int thumbnailHeight =
+                (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
         int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
 
-        mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        mDefaultThumbnailBackground =
+                Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
         Canvas c = new Canvas(mDefaultThumbnailBackground);
         c.drawColor(color);
 
@@ -95,12 +102,17 @@
 
     public void setRecentsPanel(RecentsPanelView recentsPanel) {
         mRecentsPanel = recentsPanel;
+        mNumTasksInFirstScreenful = mRecentsPanel.numItemsInOneScreenful();
     }
 
     public Bitmap getDefaultThumbnail() {
         return mDefaultThumbnailBackground;
     }
 
+    public Bitmap getDefaultIcon() {
+        return mDefaultIconBackground;
+    }
+
     // Create an TaskDescription, returning null if the title or icon is null, or if it's the
     // home activity
     TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
@@ -114,6 +126,12 @@
             homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
             .resolveActivityInfo(pm, 0);
         }
+        // Don't load the current home activity.
+        if (homeInfo != null
+            && homeInfo.packageName.equals(intent.getComponent().getPackageName())
+            && homeInfo.name.equals(intent.getComponent().getClassName())) {
+            return null;
+        }
 
         intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                 | Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -121,9 +139,8 @@
         if (resolveInfo != null) {
             final ActivityInfo info = resolveInfo.activityInfo;
             final String title = info.loadLabel(pm).toString();
-            Drawable icon = getFullResIcon(resolveInfo, pm);
 
-            if (title != null && title.length() > 0 && icon != null) {
+            if (title != null && title.length() > 0) {
                 if (DEBUG) Log.v(TAG, "creating activity desc for id="
                         + persistentTaskId + ", label=" + title);
 
@@ -131,14 +148,6 @@
                         persistentTaskId, resolveInfo, baseIntent, info.packageName,
                         description);
                 item.setLabel(title);
-                item.setIcon(icon);
-
-                // Don't load the current home activity.
-                if (homeInfo != null
-                        && homeInfo.packageName.equals(intent.getComponent().getPackageName())
-                        && homeInfo.name.equals(intent.getComponent().getClassName())) {
-                    return null;
-                }
 
                 return item;
             } else {
@@ -148,10 +157,12 @@
         return null;
     }
 
-    void loadThumbnail(TaskDescription td) {
+    void loadThumbnailAndIcon(TaskDescription td) {
         final ActivityManager am = (ActivityManager)
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        final PackageManager pm = mContext.getPackageManager();
         ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(td.persistentTaskId);
+        Drawable icon = getFullResIcon(td.resolveInfo, pm);
 
         if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
                 + td + ": " + thumbs.mainThumbnail);
@@ -161,6 +172,10 @@
             } else {
                 td.setThumbnail(mDefaultThumbnailBackground);
             }
+            if (icon != null) {
+                td.setIcon(icon);
+            }
+            td.setLoaded(true);
         }
     }
 
@@ -194,111 +209,149 @@
         return getFullResDefaultActivityIcon();
     }
 
-    public void cancelLoadingThumbnails() {
+    public void cancelLoadingThumbnailsAndIcons() {
+        if (mTaskLoader != null) {
+            mTaskLoader.cancel(false);
+            mTaskLoader = null;
+        }
         if (mThumbnailLoader != null) {
             mThumbnailLoader.cancel(false);
             mThumbnailLoader = null;
         }
     }
 
-    // return a snapshot of the current list of recent apps
-    ArrayList<TaskDescription> getRecentTasks() {
-        cancelLoadingThumbnails();
-
-        ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
-        final PackageManager pm = mContext.getPackageManager();
-        final ActivityManager am = (ActivityManager)
+    public void loadTasksInBackground() {
+        // cancel all previous loading of tasks and thumbnails
+        cancelLoadingThumbnailsAndIcons();
+        final LinkedBlockingQueue<TaskDescription> tasksWaitingForThumbnails =
+                new LinkedBlockingQueue<TaskDescription>();
+        final ArrayList<TaskDescription> taskDescriptionsWaitingToLoad =
+                new ArrayList<TaskDescription>();
+        mTaskLoader = new AsyncTask<Void, ArrayList<TaskDescription>, Void>() {
+            @Override
+            protected void onProgressUpdate(ArrayList<TaskDescription>... values) {
+                if (!isCancelled()) {
+                    ArrayList<TaskDescription> newTasks = values[0];
+                    // do a callback to RecentsPanelView to let it know we have more values
+                    // how do we let it know we're all done? just always call back twice
+                    mRecentsPanel.onTasksLoaded(newTasks);
+                }
+            }
+            @Override
+            protected Void doInBackground(Void... params) {
+                // We load in two stages: first, we update progress with just the first screenful
+                // of items. Then, we update with the rest of the items
+                final int origPri = Process.getThreadPriority(Process.myTid());
+                Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                final PackageManager pm = mContext.getPackageManager();
+                final ActivityManager am = (ActivityManager)
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
-        final List<ActivityManager.RecentTaskInfo> recentTasks =
-                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+                final List<ActivityManager.RecentTaskInfo> recentTasks =
+                        am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+                int numTasks = recentTasks.size();
+                ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
+                        .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
 
-        ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
-                    .resolveActivityInfo(pm, 0);
+                boolean firstScreenful = true;
+                ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
 
-        HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>();
-        int numTasks = recentTasks.size();
-
-        // skip the first task - assume it's either the home screen or the current activity.
-        final int first = 1;
-        recentTasksToKeepInCache.add(recentTasks.get(0).persistentId);
-        for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
-            final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
-
-            TaskDescription item = createTaskDescription(recentInfo.id,
-                    recentInfo.persistentId, recentInfo.baseIntent,
-                    recentInfo.origActivity, recentInfo.description, homeInfo);
-
-            if (item != null) {
-                tasks.add(item);
-                ++index;
-            }
-        }
-
-        // when we're not using the TaskDescription cache, we load the thumbnails in the
-        // background
-        loadThumbnailsInBackground(new ArrayList<TaskDescription>(tasks));
-        return tasks;
-    }
-
-    private void loadThumbnailsInBackground(final ArrayList<TaskDescription> descriptions) {
-        if (descriptions.size() > 0) {
-            if (DEBUG) Log.v(TAG, "Showing " + descriptions.size() + " tasks");
-            loadThumbnail(descriptions.get(0));
-            if (descriptions.size() > 1) {
-                mThumbnailLoader = new AsyncTask<Void, Integer, Void>() {
-                    @Override
-                    protected void onProgressUpdate(Integer... values) {
-                        final TaskDescription td = descriptions.get(values[0]);
-                        if (!isCancelled()) {
-                            mRecentsPanel.onTaskThumbnailLoaded(td);
-                        }
-                        // This is to prevent the loader thread from getting ahead
-                        // of our UI updates.
-                        mHandler.post(new Runnable() {
-                            @Override public void run() {
-                                synchronized (td) {
-                                    td.notifyAll();
-                                }
-                            }
-                        });
+                // skip the first task - assume it's either the home screen or the current activity.
+                final int first = 1;
+                for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+                    if (isCancelled()) {
+                        break;
                     }
+                    final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
+                    TaskDescription item = createTaskDescription(recentInfo.id,
+                            recentInfo.persistentId, recentInfo.baseIntent,
+                            recentInfo.origActivity, recentInfo.description, homeInfo);
 
-                    @Override
-                    protected Void doInBackground(Void... params) {
-                        final int origPri = Process.getThreadPriority(Process.myTid());
-                        Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
-                        long nextTime = SystemClock.uptimeMillis();
-                        for (int i=1; i<descriptions.size(); i++) {
-                            TaskDescription td = descriptions.get(i);
-                            loadThumbnail(td);
-                            long now = SystemClock.uptimeMillis();
-                            nextTime += 0;
-                            if (nextTime > now) {
-                                try {
-                                    Thread.sleep(nextTime-now);
-                                } catch (InterruptedException e) {
-                                }
-                            }
-
-                            if (isCancelled()) {
+                    if (item != null) {
+                        while (true) {
+                            try {
+                                tasksWaitingForThumbnails.put(item);
                                 break;
-                            }
-                            synchronized (td) {
-                                publishProgress(i);
-                                try {
-                                    td.wait(500);
-                                } catch (InterruptedException e) {
-                                }
+                            } catch (InterruptedException e) {
                             }
                         }
-                        Process.setThreadPriority(origPri);
-                        return null;
+                        tasks.add(item);
+                        if (firstScreenful && tasks.size() == mNumTasksInFirstScreenful) {
+                            publishProgress(tasks);
+                            tasks = new ArrayList<TaskDescription>();
+                            firstScreenful = false;
+                            //break;
+                        }
+                        ++index;
                     }
-                };
-                mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+                }
+
+                if (!isCancelled()) {
+                    publishProgress(tasks);
+                    if (firstScreenful) {
+                        // always should publish two updates
+                        publishProgress(new ArrayList<TaskDescription>());
+                    }
+                }
+
+                while (true) {
+                    try {
+                        tasksWaitingForThumbnails.put(new TaskDescription());
+                        break;
+                    } catch (InterruptedException e) {
+                    }
+                }
+
+                Process.setThreadPriority(origPri);
+                return null;
             }
-        }
+        };
+        mTaskLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        loadThumbnailsAndIconsInBackground(tasksWaitingForThumbnails);
     }
 
+    private void loadThumbnailsAndIconsInBackground(
+            final BlockingQueue<TaskDescription> tasksWaitingForThumbnails) {
+        // continually read items from tasksWaitingForThumbnails and load
+        // thumbnails and icons for them. finish thread when cancelled or there
+        // is a null item in tasksWaitingForThumbnails
+        mThumbnailLoader = new AsyncTask<Void, TaskDescription, Void>() {
+            @Override
+            protected void onProgressUpdate(TaskDescription... values) {
+                if (!isCancelled()) {
+                    TaskDescription td = values[0];
+                    mRecentsPanel.onTaskThumbnailLoaded(td);
+                }
+            }
+            @Override
+            protected Void doInBackground(Void... params) {
+                final int origPri = Process.getThreadPriority(Process.myTid());
+                Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
+
+                while (true) {
+                    if (isCancelled()) {
+                        break;
+                    }
+                    TaskDescription td = null;
+                    while (td == null) {
+                        try {
+                            td = tasksWaitingForThumbnails.take();
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                    if (td.isNull()) {
+                        break;
+                    }
+                    loadThumbnailAndIcon(td);
+                    synchronized(td) {
+                        publishProgress(td);
+                    }
+                }
+
+                Process.setThreadPriority(origPri);
+                return null;
+            }
+        };
+        mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index f971d2d..4718a17 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -22,11 +22,18 @@
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.FloatMath;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
+import android.view.View.MeasureSpec;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
 import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
 
@@ -34,6 +41,8 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
 
+import java.util.ArrayList;
+
 public class RecentsHorizontalScrollView extends HorizontalScrollView
     implements SwipeHelper.Callback {
     private static final String TAG = RecentsPanelView.TAG;
@@ -44,6 +53,8 @@
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
     private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+    private ArrayList<View> mRecycledViews;
+    private int mNumItemsInOneScreenful;
 
     public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
@@ -51,6 +62,7 @@
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, densityScale, pagingTouchSlop);
         mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, false);
+        mRecycledViews = new ArrayList<View>();
     }
 
     private int scrollPositionOfMostRecent() {
@@ -58,9 +70,23 @@
     }
 
     private void update() {
+        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+            View v = mLinearLayout.getChildAt(i);
+            mRecycledViews.add(v);
+            mAdapter.recycleView(v);
+        }
+        LayoutTransition transitioner = getLayoutTransition();
+        setLayoutTransition(null);
+
         mLinearLayout.removeAllViews();
         for (int i = 0; i < mAdapter.getCount(); i++) {
-            final View view = mAdapter.getView(i, null, mLinearLayout);
+            View old = null;
+            if (mRecycledViews.size() != 0) {
+                old = mRecycledViews.remove(mRecycledViews.size() - 1);
+                old.setVisibility(VISIBLE);
+            }
+
+            final View view = mAdapter.getView(i, old, mLinearLayout);
 
             if (mPerformanceHelper != null) {
                 mPerformanceHelper.addViewCallback(view);
@@ -87,7 +113,8 @@
                 }
             };
 
-            final View thumbnailView = view.findViewById(R.id.app_thumbnail);
+            RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) view.getTag();
+            final View thumbnailView = holder.thumbnailView;
             OnLongClickListener longClickListener = new OnLongClickListener() {
                 public boolean onLongClick(View v) {
                     final View anchorView = view.findViewById(R.id.app_description);
@@ -107,13 +134,21 @@
             appTitle.setOnTouchListener(noOpListener);
             mLinearLayout.addView(view);
         }
+        setLayoutTransition(transitioner);
+
         // Scroll to end after layout.
-        post(new Runnable() {
-            public void run() {
-                mLastScrollPosition = scrollPositionOfMostRecent();
-                scrollTo(mLastScrollPosition, 0);
-            }
-        });
+        final ViewTreeObserver observer = getViewTreeObserver();
+
+        final OnGlobalLayoutListener updateScroll = new OnGlobalLayoutListener() {
+                public void onGlobalLayout() {
+                    mLastScrollPosition = scrollPositionOfMostRecent();
+                    scrollTo(mLastScrollPosition, 0);
+                    if (observer.isAlive()) {
+                        observer.removeOnGlobalLayoutListener(this);
+                    }
+                }
+            };
+        observer.addOnGlobalLayoutListener(updateScroll);
     }
 
     @Override
@@ -142,8 +177,10 @@
     }
 
     public void onChildDismissed(View v) {
+        mRecycledViews.add(v);
         mLinearLayout.removeView(v);
         mCallback.handleSwipe(v);
+        v.setActivated(false);
     }
 
     public void onBeginDrag(View v) {
@@ -315,6 +352,24 @@
                 update();
             }
         });
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        int childWidthMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.AT_MOST);
+        int childheightMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.AT_MOST);
+        View child = mAdapter.createView(mLinearLayout);
+        child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+        mNumItemsInOneScreenful =
+                (int) FloatMath.ceil(dm.widthPixels / (float) child.getMeasuredWidth());
+        mRecycledViews.add(child);
+
+        for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
+            mRecycledViews.add(mAdapter.createView(mLinearLayout));
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        return mNumItemsInOneScreenful;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index a10e363..7896720 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -27,6 +27,7 @@
 import android.graphics.Matrix;
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -36,18 +37,18 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 import android.widget.PopupMenu;
-import android.widget.RelativeLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
@@ -59,7 +60,7 @@
 
 import java.util.ArrayList;
 
-public class RecentsPanelView extends RelativeLayout implements OnItemClickListener, RecentsCallback,
+public class RecentsPanelView extends FrameLayout implements OnItemClickListener, RecentsCallback,
         StatusBarPanel, Animator.AnimatorListener, View.OnTouchListener {
     static final String TAG = "RecentsPanelView";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
@@ -71,6 +72,10 @@
     private StatusBarTouchProxy mStatusBarTouchProxy;
 
     private boolean mShowing;
+    private boolean mWaitingToShow;
+    private boolean mWaitingToShowAnimated;
+    private boolean mReadyToShow;
+    private int mNumItemsWaitingForThumbnailsAndIcons;
     private Choreographer mChoreo;
     OnRecentsPanelVisibilityChangedListener mVisibilityChangedListener;
 
@@ -104,6 +109,7 @@
         TextView labelView;
         TextView descriptionView;
         TaskDescription taskDescription;
+        boolean loadedThumbnailAndIcon;
     }
 
     /* package */ final class TaskDescriptionAdapter extends BaseAdapter {
@@ -125,42 +131,82 @@
             return position; // we just need something unique for this position
         }
 
-        public View getView(int position, View convertView, ViewGroup parent) {
-            ViewHolder holder;
-            if (convertView == null) {
-                convertView = mInflater.inflate(R.layout.status_bar_recent_item, parent, false);
-                holder = new ViewHolder();
-                holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
-                holder.thumbnailViewImage = (ImageView) convertView.findViewById(
-                        R.id.app_thumbnail_image);
-                // If we set the default thumbnail now, we avoid an onLayout when we update
-                // the thumbnail later (if they both have the same dimensions)
+        public View createView(ViewGroup parent) {
+            View convertView = mInflater.inflate(R.layout.status_bar_recent_item, parent, false);
+            ViewHolder holder = new ViewHolder();
+            holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
+            holder.thumbnailViewImage =
+                    (ImageView) convertView.findViewById(R.id.app_thumbnail_image);
+            // If we set the default thumbnail now, we avoid an onLayout when we update
+            // the thumbnail later (if they both have the same dimensions)
+            if (mRecentTasksLoader != null) {
                 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
-
-                holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
-                holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
-                holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
-
-                convertView.setTag(holder);
-            } else {
-                holder = (ViewHolder) convertView.getTag();
             }
+            holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
+            if (mRecentTasksLoader != null) {
+                holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
+            }
+            holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
+            holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
+
+            convertView.setTag(holder);
+            return convertView;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = createView(parent);
+            }
+            ViewHolder holder = (ViewHolder) convertView.getTag();
 
             // index is reverse since most recent appears at the bottom...
             final int index = mRecentTaskDescriptions.size() - position - 1;
 
             final TaskDescription td = mRecentTaskDescriptions.get(index);
-            holder.iconView.setImageDrawable(td.getIcon());
+
             holder.labelView.setText(td.getLabel());
             holder.thumbnailView.setContentDescription(td.getLabel());
-            updateThumbnail(holder, td.getThumbnail(), true, false);
+            holder.loadedThumbnailAndIcon = td.isLoaded();
+            if (td.isLoaded()) {
+                updateThumbnail(holder, td.getThumbnail(), true, false);
+                updateIcon(holder, td.getIcon(), true, false);
+                mNumItemsWaitingForThumbnailsAndIcons--;
+            }
 
             holder.thumbnailView.setTag(td);
             holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
             holder.taskDescription = td;
-
             return convertView;
         }
+
+        public void recycleView(View v) {
+            ViewHolder holder = (ViewHolder) v.getTag();
+            updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
+            holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
+            holder.iconView.setVisibility(INVISIBLE);
+            holder.labelView.setText(null);
+            holder.thumbnailView.setContentDescription(null);
+            holder.thumbnailView.setTag(null);
+            holder.thumbnailView.setOnLongClickListener(null);
+            holder.thumbnailView.setVisibility(INVISIBLE);
+            holder.taskDescription = null;
+            holder.loadedThumbnailAndIcon = false;
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        if (mRecentsContainer instanceof RecentsHorizontalScrollView){
+            RecentsHorizontalScrollView scrollView
+                    = (RecentsHorizontalScrollView) mRecentsContainer;
+            return scrollView.numItemsInOneScreenful();
+        } else if (mRecentsContainer instanceof RecentsVerticalScrollView){
+            RecentsVerticalScrollView scrollView
+                    = (RecentsVerticalScrollView) mRecentsContainer;
+            return scrollView.numItemsInOneScreenful();
+        }
+        else {
+            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
+        }
     }
 
     @Override
@@ -192,15 +238,32 @@
     }
 
     public void show(boolean show, boolean animate) {
-        show(show, animate, null);
+        if (show) {
+            refreshRecentTasksList(null, true);
+            mWaitingToShow = true;
+            mWaitingToShowAnimated = animate;
+            showIfReady();
+        } else {
+            show(show, animate, null, false);
+        }
+    }
+
+    private void showIfReady() {
+        // mWaitingToShow = there was a touch up on the recents button
+        // mReadyToShow = we've created views for the first screenful of items
+        if (mWaitingToShow && mReadyToShow) { // && mNumItemsWaitingForThumbnailsAndIcons <= 0
+            show(true, mWaitingToShowAnimated, null, false);
+        }
     }
 
     public void show(boolean show, boolean animate,
-            ArrayList<TaskDescription> recentTaskDescriptions) {
+            ArrayList<TaskDescription> recentTaskDescriptions, boolean firstScreenful) {
+        // For now, disable animations. We may want to re-enable in the future
+        animate = false;
         if (show) {
             // Need to update list of recent apps before we set visibility so this view's
             // content description is updated before it gets focus for TalkBack mode
-            refreshRecentTasksList(recentTaskDescriptions);
+            refreshRecentTasksList(recentTaskDescriptions, firstScreenful);
 
             // if there are no apps, either bring up a "No recent apps" message, or just
             // quit early
@@ -209,19 +272,24 @@
                 mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
             } else {
                 if (noApps) {
-                    if (DEBUG) Log.v(TAG, "Nothing to show");
+                   if (DEBUG) Log.v(TAG, "Nothing to show");
                     // Need to set recent tasks to dirty so that next time we load, we
                     // refresh the list of tasks
-                    mRecentTasksLoader.cancelLoadingThumbnails();
+                    mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
                     mRecentTasksDirty = true;
+
+                    mWaitingToShow = false;
+                    mReadyToShow = false;
                     return;
                 }
             }
         } else {
             // Need to set recent tasks to dirty so that next time we load, we
             // refresh the list of tasks
-            mRecentTasksLoader.cancelLoadingThumbnails();
+            mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
             mRecentTasksDirty = true;
+            mWaitingToShow = false;
+            mReadyToShow = false;
         }
         if (animate) {
             if (mShowing != show) {
@@ -385,7 +453,6 @@
             throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
         }
 
-
         mRecentsScrim = findViewById(R.id.recents_bg_protect);
         mRecentsNoApps = findViewById(R.id.recents_no_apps);
         mChoreo = new Choreographer(this, mRecentsScrim, mRecentsContainer, mRecentsNoApps, this);
@@ -425,6 +492,20 @@
         }
     }
 
+
+    private void updateIcon(ViewHolder h, Drawable icon, boolean show, boolean anim) {
+        if (icon != null) {
+            h.iconView.setImageDrawable(icon);
+            if (show && h.iconView.getVisibility() != View.VISIBLE) {
+                if (anim) {
+                    h.iconView.setAnimation(
+                            AnimationUtils.loadAnimation(mContext, R.anim.recent_appear));
+                }
+                h.iconView.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
     private void updateThumbnail(ViewHolder h, Bitmap thumbnail, boolean show, boolean anim) {
         if (thumbnail != null) {
             // Should remove the default image in the frame
@@ -458,31 +539,36 @@
         }
     }
 
-    void onTaskThumbnailLoaded(TaskDescription ad) {
-        synchronized (ad) {
+    void onTaskThumbnailLoaded(TaskDescription td) {
+        synchronized (td) {
             if (mRecentsContainer != null) {
                 ViewGroup container = mRecentsContainer;
                 if (container instanceof HorizontalScrollView
                         || container instanceof ScrollView) {
-                    container = (ViewGroup)container.findViewById(
+                    container = (ViewGroup) container.findViewById(
                             R.id.recents_linear_layout);
                 }
                 // Look for a view showing this thumbnail, to update.
-                for (int i=0; i<container.getChildCount(); i++) {
+                for (int i=0; i < container.getChildCount(); i++) {
                     View v = container.getChildAt(i);
                     if (v.getTag() instanceof ViewHolder) {
                         ViewHolder h = (ViewHolder)v.getTag();
-                        if (h.taskDescription == ad) {
+                        if (!h.loadedThumbnailAndIcon && h.taskDescription == td) {
                             // only fade in the thumbnail if recents is already visible-- we
                             // show it immediately otherwise
-                            boolean animateShow = mShowing &&
-                                mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
-                            updateThumbnail(h, ad.getThumbnail(), true, animateShow);
+                            //boolean animateShow = mShowing &&
+                            //    mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
+                            boolean animateShow = false;
+                            updateIcon(h, td.getIcon(), true, animateShow);
+                            updateThumbnail(h, td.getThumbnail(), true, animateShow);
+                            h.loadedThumbnailAndIcon = true;
+                            mNumItemsWaitingForThumbnailsAndIcons--;
                         }
                     }
                 }
             }
-        }
+            }
+        showIfReady();
     }
 
     // additional optimization when we have sofware system buttons - start loading the recent
@@ -516,7 +602,7 @@
     public void clearRecentTasksList() {
         // Clear memory used by screenshots
         if (mRecentTaskDescriptions != null) {
-            mRecentTasksLoader.cancelLoadingThumbnails();
+            mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
             mRecentTaskDescriptions.clear();
             mListAdapter.notifyDataSetInvalidated();
             mRecentTasksDirty = true;
@@ -524,26 +610,50 @@
     }
 
     public void refreshRecentTasksList() {
-        refreshRecentTasksList(null);
+        refreshRecentTasksList(null, false);
     }
 
-    private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
+    private void refreshRecentTasksList(
+            ArrayList<TaskDescription> recentTasksList, boolean firstScreenful) {
         if (mRecentTasksDirty) {
             if (recentTasksList != null) {
-                mRecentTaskDescriptions = recentTasksList;
+                mFirstScreenful = true;
+                onTasksLoaded(recentTasksList);
             } else {
-                mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
+                mFirstScreenful = true;
+                mRecentTasksLoader.loadTasksInBackground();
             }
-            mListAdapter.notifyDataSetInvalidated();
-            updateUiElements(getResources().getConfiguration());
             mRecentTasksDirty = false;
         }
     }
 
+    boolean mFirstScreenful;
+    public void onTasksLoaded(ArrayList<TaskDescription> tasks) {
+        if (!mFirstScreenful && tasks.size() == 0) {
+            return;
+        }
+        mNumItemsWaitingForThumbnailsAndIcons =
+                mFirstScreenful ? tasks.size() : mRecentTaskDescriptions.size();
+        if (mRecentTaskDescriptions == null) {
+            mRecentTaskDescriptions = new ArrayList(tasks);
+        } else {
+            mRecentTaskDescriptions.addAll(tasks);
+        }
+        mListAdapter.notifyDataSetInvalidated();
+        updateUiElements(getResources().getConfiguration());
+        mReadyToShow = true;
+        mFirstScreenful = false;
+        showIfReady();
+    }
+
     public ArrayList<TaskDescription> getRecentTasksList() {
         return mRecentTaskDescriptions;
     }
 
+    public boolean getFirstScreenful() {
+        return mFirstScreenful;
+    }
+
     private void updateUiElements(Configuration config) {
         final int items = mRecentTaskDescriptions.size();
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index dc13092..0605c4c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -22,10 +22,18 @@
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.FloatMath;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.View.OnTouchListener;
 import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
@@ -33,6 +41,8 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
 
+import java.util.ArrayList;
+
 public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper.Callback {
     private static final String TAG = RecentsPanelView.TAG;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
@@ -42,6 +52,8 @@
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
     private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+    private ArrayList<View> mRecycledViews;
+    private int mNumItemsInOneScreenful;
 
     public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
@@ -50,6 +62,7 @@
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
 
         mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, true);
+        mRecycledViews = new ArrayList<View>();
     }
 
     private int scrollPositionOfMostRecent() {
@@ -57,77 +70,91 @@
     }
 
     private void update() {
+        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+            View v = mLinearLayout.getChildAt(i);
+            mRecycledViews.add(v);
+            mAdapter.recycleView(v);
+        }
+        LayoutTransition transitioner = getLayoutTransition();
+        setLayoutTransition(null);
+
         mLinearLayout.removeAllViews();
         // Once we can clear the data associated with individual item views,
         // we can get rid of the removeAllViews() and the code below will
         // recycle them.
         for (int i = 0; i < mAdapter.getCount(); i++) {
             View old = null;
-            if (i < mLinearLayout.getChildCount()) {
-                old = mLinearLayout.getChildAt(i);
-                old.setVisibility(View.VISIBLE);
+            if (mRecycledViews.size() != 0) {
+                old = mRecycledViews.remove(mRecycledViews.size() - 1);
+                old.setVisibility(VISIBLE);
             }
+
             final View view = mAdapter.getView(i, old, mLinearLayout);
 
             if (mPerformanceHelper != null) {
                 mPerformanceHelper.addViewCallback(view);
             }
 
-            if (old == null) {
-                OnTouchListener noOpListener = new OnTouchListener() {
-                    @Override
-                    public boolean onTouch(View v, MotionEvent event) {
-                        return true;
-                    }
-                };
+            OnTouchListener noOpListener = new OnTouchListener() {
+                @Override
+                public boolean onTouch(View v, MotionEvent event) {
+                    return true;
+                }
+            };
 
-                view.setOnClickListener(new OnClickListener() {
-                    public void onClick(View v) {
-                        mCallback.dismiss();
-                    }
-                });
-                // We don't want a click sound when we dimiss recents
-                view.setSoundEffectsEnabled(false);
+            view.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    mCallback.dismiss();
+                }
+            });
+            // We don't want a click sound when we dimiss recents
+            view.setSoundEffectsEnabled(false);
 
-                OnClickListener launchAppListener = new OnClickListener() {
-                    public void onClick(View v) {
-                        mCallback.handleOnClick(view);
-                    }
-                };
+            OnClickListener launchAppListener = new OnClickListener() {
+                public void onClick(View v) {
+                    mCallback.handleOnClick(view);
+                }
+            };
 
-                final View thumbnailView = view.findViewById(R.id.app_thumbnail);
-                OnLongClickListener longClickListener = new OnLongClickListener() {
-                    public boolean onLongClick(View v) {
-                        final View anchorView = view.findViewById(R.id.app_description);
-                        mCallback.handleLongPress(view, anchorView, thumbnailView);
-                        return true;
-                    }
-                };
-                thumbnailView.setClickable(true);
-                thumbnailView.setOnClickListener(launchAppListener);
-                thumbnailView.setOnLongClickListener(longClickListener);
+            RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) view.getTag();
+            final View thumbnailView = holder.thumbnailView;
+            OnLongClickListener longClickListener = new OnLongClickListener() {
+                public boolean onLongClick(View v) {
+                    final View anchorView = view.findViewById(R.id.app_description);
+                    mCallback.handleLongPress(view, anchorView, thumbnailView);
+                    return true;
+                }
+            };
+            thumbnailView.setClickable(true);
+            thumbnailView.setOnClickListener(launchAppListener);
+            thumbnailView.setOnLongClickListener(longClickListener);
 
-                // We don't want to dismiss recents if a user clicks on the app title
-                // (we also don't want to launch the app either, though, because the
-                // app title is a small target and doesn't have great click feedback)
-                final View appTitle = view.findViewById(R.id.app_label);
-                appTitle.setContentDescription(" ");
-                appTitle.setOnTouchListener(noOpListener);
-                final View calloutLine = view.findViewById(R.id.recents_callout_line);
-                calloutLine.setOnTouchListener(noOpListener);
-                mLinearLayout.addView(view);
-            }
+            // We don't want to dismiss recents if a user clicks on the app title
+            // (we also don't want to launch the app either, though, because the
+            // app title is a small target and doesn't have great click feedback)
+            final View appTitle = view.findViewById(R.id.app_label);
+            appTitle.setContentDescription(" ");
+            appTitle.setOnTouchListener(noOpListener);
+            final View calloutLine = view.findViewById(R.id.recents_callout_line);
+            calloutLine.setOnTouchListener(noOpListener);
+
+            mLinearLayout.addView(view);
         }
-        for (int i = mAdapter.getCount(); i < mLinearLayout.getChildCount(); i++) {
-            mLinearLayout.getChildAt(i).setVisibility(View.GONE);
-        }
+        setLayoutTransition(transitioner);
+
         // Scroll to end after layout.
-        post(new Runnable() {
-            public void run() {
-                mLastScrollPosition = scrollPositionOfMostRecent();
-                scrollTo(0, mLastScrollPosition);
-            }
-        });
+        final ViewTreeObserver observer = getViewTreeObserver();
+
+        final OnGlobalLayoutListener updateScroll = new OnGlobalLayoutListener() {
+                public void onGlobalLayout() {
+                    mLastScrollPosition = scrollPositionOfMostRecent();
+                    scrollTo(0, mLastScrollPosition);
+                    if (observer.isAlive()) {
+                        observer.removeOnGlobalLayoutListener(this);
+                    }
+                }
+            };
+        observer.addOnGlobalLayoutListener(updateScroll);
     }
 
     @Override
@@ -156,8 +183,10 @@
     }
 
     public void onChildDismissed(View v) {
+        mRecycledViews.add(v);
         mLinearLayout.removeView(v);
         mCallback.handleSwipe(v);
+        v.setActivated(false);
     }
 
     public void onBeginDrag(View v) {
@@ -330,6 +359,25 @@
                 update();
             }
         });
+
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        int childWidthMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.AT_MOST);
+        int childheightMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.AT_MOST);
+        View child = mAdapter.createView(mLinearLayout);
+        child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+        mNumItemsInOneScreenful =
+                (int) FloatMath.ceil(dm.heightPixels / (float) child.getMeasuredHeight());
+        mRecycledViews.add(child);
+
+        for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
+            mRecycledViews.add(mAdapter.createView(mLinearLayout));
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        return mNumItemsInOneScreenful;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
index dcfd6d8..7e979b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
@@ -32,6 +32,7 @@
     private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
     private Drawable mIcon; // application package icon
     private CharSequence mLabel; // application package label
+    private boolean mLoaded;
 
     public TaskDescription(int _taskId, int _persistentTaskId,
             ResolveInfo _resolveInfo, Intent _intent,
@@ -45,6 +46,28 @@
         packageName = _packageName;
     }
 
+    public TaskDescription() {
+        resolveInfo = null;
+        intent = null;
+        taskId = -1;
+        persistentTaskId = -1;
+
+        description = null;
+        packageName = null;
+    }
+
+    public void setLoaded(boolean loaded) {
+        mLoaded = loaded;
+    }
+
+    public boolean isLoaded() {
+        return mLoaded;
+    }
+
+    public boolean isNull() {
+        return resolveInfo == null;
+    }
+
     // mark all these as locked?
     public CharSequence getLabel() {
         return mLabel;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 5a1e3f4..b3cef90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -393,7 +393,7 @@
         }
         lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
         lp.setTitle("RecentsPanel");
-        lp.windowAnimations = R.style.Animation_RecentPanel;
+        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
         lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
         | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
         return lp;
@@ -403,11 +403,13 @@
         // Recents Panel
         boolean visible = false;
         ArrayList<TaskDescription> recentTasksList = null;
+        boolean firstScreenful = false;
         if (mRecentsPanel != null) {
             visible = mRecentsPanel.isShowing();
             WindowManagerImpl.getDefault().removeView(mRecentsPanel);
             if (visible) {
                 recentTasksList = mRecentsPanel.getRecentTasksList();
+                firstScreenful = mRecentsPanel.getFirstScreenful();
             }
         }
 
@@ -425,7 +427,7 @@
         WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
         mRecentsPanel.setBar(this);
         if (visible) {
-            mRecentsPanel.show(true, false, recentTasksList);
+            mRecentsPanel.show(true, false, recentTasksList, firstScreenful);
         }
 
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index d787e10..c59290c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -88,6 +88,7 @@
     int mLastSignalLevel;
     boolean mShowPhoneRSSIForData = false;
     boolean mShowAtLeastThreeGees = false;
+    boolean mAlwaysShowCdmaRssi = false;
 
     String mContentDescriptionPhoneSignal;
     String mContentDescriptionWifi;
@@ -156,7 +157,7 @@
     IBatteryStats mBatteryStats;
 
     public interface SignalCluster {
-        void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon, 
+        void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon,
                 String contentDescription);
         void setMobileDataIndicators(boolean visible, int strengthIcon, int activityIcon,
                 int typeIcon, String contentDescription, String typeContentDescription);
@@ -176,6 +177,8 @@
 
         mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData);
         mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G);
+        mAlwaysShowCdmaRssi = res.getBoolean(
+                com.android.internal.R.bool.config_alwaysUseCdmaRssi);
 
         // set up the default wifi icon, used when no radios have ever appeared
         updateWifiIcons();
@@ -287,7 +290,7 @@
             // wimax is special
             cluster.setMobileDataIndicators(
                     true,
-                    mWimaxIconId,
+                    mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId,
                     mMobileActivityIconId,
                     mDataTypeIconId,
                     mContentDescriptionWimax,
@@ -351,7 +354,7 @@
         @Override
         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
             if (DEBUG) {
-                Slog.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength + 
+                Slog.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
                     ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
             }
             mSignalStrength = signalStrength;
@@ -469,7 +472,15 @@
             } else {
                 int iconLevel;
                 int[] iconList;
-                mLastSignalLevel = iconLevel = mSignalStrength.getLevel();
+                if (isCdma() && mAlwaysShowCdmaRssi) {
+                    mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel();
+                    if(DEBUG) Slog.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi
+                            + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel()
+                            + " instead of level=" + mSignalStrength.getLevel());
+                } else {
+                    mLastSignalLevel = iconLevel = mSignalStrength.getLevel();
+                }
+
                 if (isCdma()) {
                     if (isCdmaEri()) {
                         iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
@@ -487,7 +498,6 @@
                 mPhoneSignalIconId = iconList[iconLevel];
                 mContentDescriptionPhoneSignal = mContext.getString(
                         AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]);
-
                 mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
             }
         }
@@ -914,7 +924,7 @@
                     mobileLabel = "";
                 }
             } else {
-                mobileLabel 
+                mobileLabel
                     = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
             }
 
@@ -1190,7 +1200,7 @@
                 v.setText(wifiLabel);
             }
         }
-        
+
         // mobile label
         N = mMobileLabelViews.size();
         for (int i=0; i<N; i++) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 5c964b2..0248687 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioFlinger.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -540,7 +540,7 @@
 
     Mutex::Autolock _l(mLock);
     mMasterVolume = value;
-    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+    for (size_t i = 0; i < mPlaybackThreads.size(); i++)
        mPlaybackThreads.valueAt(i)->setMasterVolume(value);
 
     return NO_ERROR;
@@ -572,7 +572,7 @@
     if (NO_ERROR == ret) {
         Mutex::Autolock _l(mLock);
         mMode = mode;
-        for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+        for (size_t i = 0; i < mPlaybackThreads.size(); i++)
            mPlaybackThreads.valueAt(i)->setMode(mode);
     }
 
@@ -621,8 +621,9 @@
     }
 
     Mutex::Autolock _l(mLock);
+    // This is an optimization, so PlaybackThread doesn't have to look at the one from AudioFlinger
     mMasterMute = muted;
-    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+    for (size_t i = 0; i < mPlaybackThreads.size(); i++)
        mPlaybackThreads.valueAt(i)->setMasterMute(muted);
 
     return NO_ERROR;
@@ -665,7 +666,7 @@
     mStreamTypes[stream].volume = value;
 
     if (thread == NULL) {
-        for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
+        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
            mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
         }
     } else {
@@ -711,7 +712,7 @@
         }
         volume = thread->streamVolume(stream);
     } else {
-        volume = mStreamTypes[stream].volume;
+        volume = streamVolume_l(stream);
     }
 
     return volume;
@@ -723,7 +724,8 @@
         return true;
     }
 
-    return mStreamTypes[stream].mute;
+    AutoMutex lock(mLock);
+    return streamMute_l(stream);
 }
 
 status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
@@ -928,7 +930,7 @@
 {
     Mutex::Autolock _l(mLock);
 
-    int index = mNotificationClients.indexOfKey(pid);
+    ssize_t index = mNotificationClients.indexOfKey(pid);
     if (index >= 0) {
         sp <NotificationClient> client = mNotificationClients.valueFor(pid);
         ALOGV("removeNotificationClient() %p, pid %d", client.get(), pid);
@@ -936,9 +938,9 @@
     }
 
     ALOGV("%d died, releasing its sessions", pid);
-    int num = mAudioSessionRefs.size();
+    size_t num = mAudioSessionRefs.size();
     bool removed = false;
-    for (int i = 0; i< num; i++) {
+    for (size_t i = 0; i< num; ) {
         AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
         ALOGV(" pid %d @ %d", ref->pid, i);
         if (ref->pid == pid) {
@@ -946,8 +948,9 @@
             mAudioSessionRefs.removeAt(i);
             delete ref;
             removed = true;
-            i--;
             num--;
+        } else {
+            i++;
         }
     }
     if (removed) {
@@ -1238,7 +1241,7 @@
 
 void AudioFlinger::ThreadBase::checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain)
 {
-    int index = mSuspendedSessions.indexOfKey(chain->sessionId());
+    ssize_t index = mSuspendedSessions.indexOfKey(chain->sessionId());
     if (index < 0) {
         return;
     }
@@ -1264,7 +1267,7 @@
                                                          bool suspend,
                                                          int sessionId)
 {
-    int index = mSuspendedSessions.indexOfKey(sessionId);
+    ssize_t index = mSuspendedSessions.indexOfKey(sessionId);
 
     KeyedVector <int, sp<SuspendedSessionDesc> > sessionEffects;
 
@@ -1375,11 +1378,13 @@
     // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
     for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT;
             stream = (audio_stream_type_t) (stream + 1)) {
-        mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
-        mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
+        mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
+        mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
         // initialized by stream_type_t default constructor
         // mStreamTypes[stream].valid = true;
     }
+    // mStreamTypes[AUDIO_STREAM_CNT] exists but isn't explicitly initialized here,
+    // because mAudioFlinger doesn't have one to copy from
 }
 
 AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -1573,40 +1578,36 @@
     }
 }
 
-status_t AudioFlinger::PlaybackThread::setMasterVolume(float value)
+void AudioFlinger::PlaybackThread::setMasterVolume(float value)
 {
+    Mutex::Autolock _l(mLock);
     mMasterVolume = value;
-    return NO_ERROR;
 }
 
-status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted)
+void AudioFlinger::PlaybackThread::setMasterMute(bool muted)
 {
-    mMasterMute = muted;
-    return NO_ERROR;
+    Mutex::Autolock _l(mLock);
+    setMasterMute_l(muted);
 }
 
-status_t AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
+void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
 {
+    Mutex::Autolock _l(mLock);
     mStreamTypes[stream].volume = value;
-    return NO_ERROR;
 }
 
-status_t AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
+void AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
 {
+    Mutex::Autolock _l(mLock);
     mStreamTypes[stream].mute = muted;
-    return NO_ERROR;
 }
 
 float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) const
 {
+    Mutex::Autolock _l(mLock);
     return mStreamTypes[stream].volume;
 }
 
-bool AudioFlinger::PlaybackThread::streamMute(audio_stream_type_t stream) const
-{
-    return mStreamTypes[stream].mute;
-}
-
 // addTrack_l() must be called with ThreadBase::mLock held
 status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
 {
@@ -1936,7 +1937,7 @@
                         property_get("ro.audio.silent", value, "0");
                         if (atoi(value)) {
                             ALOGD("Silence is golden");
-                            setMasterMute(true);
+                            setMasterMute_l(true);
                         }
                     }
 
@@ -2637,7 +2638,7 @@
                         property_get("ro.audio.silent", value, "0");
                         if (atoi(value)) {
                             ALOGD("Silence is golden");
-                            setMasterMute(true);
+                            setMasterMute_l(true);
                         }
                     }
 
@@ -3033,7 +3034,7 @@
                         property_get("ro.audio.silent", value, "0");
                         if (atoi(value)) {
                             ALOGD("Silence is golden");
-                            setMasterMute(true);
+                            setMasterMute_l(true);
                         }
                     }
 
@@ -3121,6 +3122,7 @@
 
 void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
 {
+    // FIXME explain this formula
     int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate();
     OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread,
                                             this,
@@ -3392,7 +3394,7 @@
 {
     // NOTE: destroyTrack_l() can remove a strong reference to this Track
     // by removing it from mTracks vector, so there is a risk that this Tracks's
-    // desctructor is called. As the destructor needs to lock mLock,
+    // destructor is called. As the destructor needs to lock mLock,
     // we must acquire a strong reference on this Track before locking mLock
     // here so that the destructor is called only when exiting this function.
     // On the other hand, as long as Track::destroy() is only called by
@@ -3494,11 +3496,11 @@
     return false;
 }
 
-status_t AudioFlinger::PlaybackThread::Track::start()
+status_t AudioFlinger::PlaybackThread::Track::start(pid_t tid)
 {
     status_t status = NO_ERROR;
-    ALOGV("start(%d), calling pid %d session %d",
-            mName, IPCThreadState::self()->getCallingPid(), mSessionId);
+    ALOGV("start(%d), calling pid %d session %d tid %d",
+            mName, IPCThreadState::self()->getCallingPid(), mSessionId, tid);
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0) {
         Mutex::Autolock _l(thread->mLock);
@@ -3717,12 +3719,12 @@
     return NOT_ENOUGH_DATA;
 }
 
-status_t AudioFlinger::RecordThread::RecordTrack::start()
+status_t AudioFlinger::RecordThread::RecordTrack::start(pid_t tid)
 {
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0) {
         RecordThread *recordThread = (RecordThread *)thread.get();
-        return recordThread->start(this);
+        return recordThread->start(this, tid);
     } else {
         return BAD_VALUE;
     }
@@ -3789,9 +3791,9 @@
     clearBufferQueue();
 }
 
-status_t AudioFlinger::PlaybackThread::OutputTrack::start()
+status_t AudioFlinger::PlaybackThread::OutputTrack::start(pid_t tid)
 {
-    status_t status = Track::start();
+    status_t status = Track::start(tid);
     if (status != NO_ERROR) {
         return status;
     }
@@ -3821,7 +3823,7 @@
     uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
 
     if (!mActive && frames != 0) {
-        start();
+        start(0);
         sp<ThreadBase> thread = mThread.promote();
         if (thread != 0) {
             MixerThread *mixerThread = (MixerThread *)thread.get();
@@ -3998,6 +4000,7 @@
 AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid)
     :   RefBase(),
         mAudioFlinger(audioFlinger),
+        // FIXME should be a "k" constant not hard-coded, in .h or ro. property, see 4 lines below
         mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")),
         mPid(pid)
 {
@@ -4056,8 +4059,8 @@
     return mTrack->getCblk();
 }
 
-status_t AudioFlinger::TrackHandle::start() {
-    return mTrack->start();
+status_t AudioFlinger::TrackHandle::start(pid_t tid) {
+    return mTrack->start(tid);
 }
 
 void AudioFlinger::TrackHandle::stop() {
@@ -4179,9 +4182,9 @@
     return mRecordTrack->getCblk();
 }
 
-status_t AudioFlinger::RecordHandle::start() {
+status_t AudioFlinger::RecordHandle::start(pid_t tid) {
     ALOGV("RecordHandle::start()");
-    return mRecordTrack->start();
+    return mRecordTrack->start(tid);
 }
 
 void AudioFlinger::RecordHandle::stop() {
@@ -4473,9 +4476,9 @@
     return track;
 }
 
-status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack)
+status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack, pid_t tid)
 {
-    ALOGV("RecordThread::start");
+    ALOGV("RecordThread::start tid=%d", tid);
     sp <ThreadBase> strongMe = this;
     status_t status = NO_ERROR;
     {
@@ -4659,7 +4662,7 @@
         }
         if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
             // do not accept frame count changes if tracks are open as the track buffer
-            // size depends on frame count and correct behavior would not be garantied
+            // size depends on frame count and correct behavior would not be guaranteed
             // if frame count is changed after track creation
             if (mActiveTrack != 0) {
                 status = INVALID_OPERATION;
@@ -5176,8 +5179,8 @@
     Mutex::Autolock _l(mLock);
     pid_t caller = IPCThreadState::self()->getCallingPid();
     ALOGV("acquiring %d from %d", audioSession, caller);
-    int num = mAudioSessionRefs.size();
-    for (int i = 0; i< num; i++) {
+    size_t num = mAudioSessionRefs.size();
+    for (size_t i = 0; i< num; i++) {
         AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
         if (ref->sessionid == audioSession && ref->pid == caller) {
             ref->cnt++;
@@ -5194,8 +5197,8 @@
     Mutex::Autolock _l(mLock);
     pid_t caller = IPCThreadState::self()->getCallingPid();
     ALOGV("releasing %d from %d", audioSession, caller);
-    int num = mAudioSessionRefs.size();
-    for (int i = 0; i< num; i++) {
+    size_t num = mAudioSessionRefs.size();
+    for (size_t i = 0; i< num; i++) {
         AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
         if (ref->sessionid == audioSession && ref->pid == caller) {
             ref->cnt--;
@@ -6107,7 +6110,6 @@
     status_t status;
 
     Mutex::Autolock _l(mLock);
-    // First handle in mHandles has highest priority and controls the effect module
     int priority = handle->priority();
     size_t size = mHandles.size();
     sp<EffectHandle> h;
@@ -7177,12 +7179,12 @@
         // Reject insertion if an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is
         // already present
 
-        int size = (int)mEffects.size();
-        int idx_insert = size;
-        int idx_insert_first = -1;
-        int idx_insert_last = -1;
+        size_t size = mEffects.size();
+        size_t idx_insert = size;
+        ssize_t idx_insert_first = -1;
+        ssize_t idx_insert_last = -1;
 
-        for (int i = 0; i < size; i++) {
+        for (size_t i = 0; i < size; i++) {
             effect_descriptor_t d = mEffects[i]->desc();
             uint32_t iMode = d.flags & EFFECT_FLAG_TYPE_MASK;
             uint32_t iPref = d.flags & EFFECT_FLAG_INSERT_MASK;
@@ -7251,11 +7253,10 @@
 size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
 {
     Mutex::Autolock _l(mLock);
-    int size = (int)mEffects.size();
-    int i;
+    size_t size = mEffects.size();
     uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;
 
-    for (i = 0; i < size; i++) {
+    for (size_t i = 0; i < size; i++) {
         if (effect == mEffects[i]) {
             // calling stop here will remove pre-processing effect from the audio HAL.
             // This is safe as we hold the EffectChain mutex which guarantees that we are not in
@@ -7402,7 +7403,7 @@
     sp<SuspendedEffectDesc> desc;
     // use effect type UUID timelow as key as there is no real risk of identical
     // timeLow fields among effect type UUIDs.
-    int index = mSuspendedEffects.indexOfKey(type->timeLow);
+    ssize_t index = mSuspendedEffects.indexOfKey(type->timeLow);
     if (suspend) {
         if (index >= 0) {
             desc = mSuspendedEffects.valueAt(index);
@@ -7452,7 +7453,7 @@
 {
     sp<SuspendedEffectDesc> desc;
 
-    int index = mSuspendedEffects.indexOfKey((int)kKeyForSuspendAll);
+    ssize_t index = mSuspendedEffects.indexOfKey((int)kKeyForSuspendAll);
     if (suspend) {
         if (index >= 0) {
             desc = mSuspendedEffects.valueAt(index);
@@ -7534,7 +7535,7 @@
 void AudioFlinger::EffectChain::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
                                                             bool enabled)
 {
-    int index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
+    ssize_t index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
     if (enabled) {
         if (index < 0) {
             // if the effect is not suspend check if all effects are suspended
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index fdcd916..aa0b8f8 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioFlinger.h
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -290,6 +290,8 @@
             enum track_state {
                 IDLE,
                 TERMINATED,
+                // These are order-sensitive; do not change order without reviewing the impact.
+                // In particular there are assumptions about > STOPPED.
                 STOPPED,
                 RESUMING,
                 ACTIVE,
@@ -314,7 +316,7 @@
                                         int sessionId);
             virtual             ~TrackBase();
 
-            virtual status_t    start() = 0;
+            virtual status_t    start(pid_t tid) = 0;
             virtual void        stop() = 0;
                     sp<IMemory> getCblk() const { return mCblkMemory; }
                     audio_track_cblk_t* cblk() const { return mCblk; }
@@ -586,7 +588,7 @@
             virtual             ~Track();
 
                     void        dump(char* buffer, size_t size);
-            virtual status_t    start();
+            virtual status_t    start(pid_t tid);
             virtual void        stop();
                     void        pause();
 
@@ -668,7 +670,7 @@
                                         int frameCount);
             virtual             ~OutputTrack();
 
-            virtual status_t    start();
+            virtual status_t    start(pid_t tid);
             virtual void        stop();
                     bool        write(int16_t* data, uint32_t frames);
                     bool        bufferQueueEmpty() const { return (mBufferQueue.size() == 0) ? true : false; }
@@ -707,17 +709,13 @@
 
         virtual     uint32_t    latency() const;
 
-        virtual     status_t    setMasterVolume(float value);
-        virtual     status_t    setMasterMute(bool muted);
+                    void        setMasterVolume(float value);
+                    void        setMasterMute(bool muted);
 
-        virtual     float       masterVolume() const { return mMasterVolume; }
-        virtual     bool        masterMute() const { return mMasterMute; }
+                    void        setStreamVolume(audio_stream_type_t stream, float value);
+                    void        setStreamMute(audio_stream_type_t stream, bool muted);
 
-        virtual     status_t    setStreamVolume(audio_stream_type_t stream, float value);
-        virtual     status_t    setStreamMute(audio_stream_type_t stream, bool muted);
-
-        virtual     float       streamVolume(audio_stream_type_t stream) const;
-        virtual     bool        streamMute(audio_stream_type_t stream) const;
+                    float       streamVolume(audio_stream_type_t stream) const;
 
                     sp<Track>   createTrack_l(
                                     const sp<AudioFlinger::Client>& client,
@@ -760,7 +758,11 @@
         int                             mSuspended;
         int                             mBytesWritten;
     private:
+        // mMasterMute is in both PlaybackThread and in AudioFlinger.  When a
+        // PlaybackThread needs to find out if master-muted, it checks it's local
+        // copy rather than the one in AudioFlinger.  This optimization saves a lock.
         bool                            mMasterMute;
+                    void        setMasterMute_l(bool muted) { mMasterMute = muted; }
     protected:
         SortedVector< wp<Track> >       mActiveTracks;
 
@@ -853,6 +855,8 @@
     private:
         void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
 
+        // volumes last sent to audio HAL with stream->set_volume()
+        // FIXME use standard representation and names
         float mLeftVolFloat;
         float mRightVolFloat;
         uint16_t mLeftVolShort;
@@ -884,7 +888,11 @@
               PlaybackThread *checkPlaybackThread_l(audio_io_handle_t output) const;
               MixerThread *checkMixerThread_l(audio_io_handle_t output) const;
               RecordThread *checkRecordThread_l(audio_io_handle_t input) const;
-              float streamVolumeInternal(audio_stream_type_t stream) const
+              // no range check, AudioFlinger::mLock held
+              bool streamMute_l(audio_stream_type_t stream) const
+                                { return mStreamTypes[stream].mute; }
+              // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
+              float streamVolume_l(audio_stream_type_t stream) const
                                 { return mStreamTypes[stream].volume; }
               void audioConfigChanged_l(int event, audio_io_handle_t ioHandle, void *param2);
 
@@ -900,12 +908,13 @@
 
     friend class AudioBuffer;
 
+    // server side of the client's IAudioTrack
     class TrackHandle : public android::BnAudioTrack {
     public:
                             TrackHandle(const sp<PlaybackThread::Track>& track);
         virtual             ~TrackHandle();
         virtual sp<IMemory> getCblk() const;
-        virtual status_t    start();
+        virtual status_t    start(pid_t tid);
         virtual void        stop();
         virtual void        flush();
         virtual void        mute(bool);
@@ -943,7 +952,7 @@
                                         int sessionId);
             virtual             ~RecordTrack();
 
-            virtual status_t    start();
+            virtual status_t    start(pid_t tid);
             virtual void        stop();
 
                     bool        overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; }
@@ -988,6 +997,7 @@
                         status_t *status);
 
                 status_t    start(RecordTrack* recordTrack);
+                status_t    start(RecordTrack* recordTrack, pid_t tid);
                 void        stop(RecordTrack* recordTrack);
                 status_t    dump(int fd, const Vector<String16>& args);
                 AudioStreamIn* getInput() const;
@@ -1023,12 +1033,13 @@
                 ssize_t                             mBytesRead;
     };
 
+    // server side of the client's IAudioRecord
     class RecordHandle : public android::BnAudioRecord {
     public:
         RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack);
         virtual             ~RecordHandle();
         virtual sp<IMemory> getCblk() const;
-        virtual status_t    start();
+        virtual status_t    start(pid_t tid);
         virtual void        stop();
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
@@ -1151,6 +1162,7 @@
         status_t            mStatus;    // initialization status
         effect_state        mState;     // current activation state
         Vector< wp<EffectHandle> > mHandles;    // list of client handles
+                    // First handle in mHandles has highest priority and controls the effect module
         uint32_t mMaxDisableWaitCnt;    // maximum grace period before forcing an effect off after
                                         // sending disable command.
         uint32_t mDisableWaitCnt;       // current process() calls count during disable period.
@@ -1376,6 +1388,7 @@
             hwDev(dev), stream(in) {}
     };
 
+    // for mAudioSessionRefs only
     struct AudioSessionRef {
         // FIXME rename parameter names when fields get "m" prefix
         AudioSessionRef(int sessionid_, pid_t pid_) :
@@ -1431,10 +1444,11 @@
                 DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> >    mRecordThreads;
 
                 DefaultKeyedVector< pid_t, sp<NotificationClient> >    mNotificationClients;
-                volatile int32_t                    mNextUniqueId;
+                volatile int32_t                    mNextUniqueId;  // updated by android_atomic_inc
                 audio_mode_t                        mMode;
                 bool                                mBtNrecIsOff;
 
+                // protected by mLock
                 Vector<AudioSessionRef*> mAudioSessionRefs;
 
                 float       masterVolume_l() const  { return mMasterVolume; }
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 191520a..cb7678b 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioMixer.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -961,7 +961,12 @@
 // one track, 16 bits stereo without resampling is the most common case
 void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
 {
+    // This method is only called when state->enabledTracks has exactly
+    // one bit set.  The asserts below would verify this, but are commented out
+    // since the whole point of this method is to optimize performance.
+    //assert(0 != state->enabledTracks);
     const int i = 31 - __builtin_clz(state->enabledTracks);
+    //assert((1 << i) == state->enabledTracks);
     const track_t& t = state->tracks[i];
 
     AudioBufferProvider::Buffer& b(t.buffer);
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index c709686..c956918 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioMixer.h
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 21b5811..041b5a8 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -768,7 +768,7 @@
     snprintf(buffer, SIZE, "- Commands:\n");
     result = String8(buffer);
     result.append("   Command Time        Wait pParam\n");
-    for (int i = 0; i < (int)mAudioCommands.size(); i++) {
+    for (size_t i = 0; i < mAudioCommands.size(); i++) {
         mAudioCommands[i]->dump(buffer, SIZE);
         result.append(buffer);
     }
@@ -902,7 +902,7 @@
 // insertCommand_l() must be called with mLock held
 void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
 {
-    ssize_t i;
+    ssize_t i;  // not size_t because i will count down to -1
     Vector <AudioCommand *> removedCommands;
 
     command->mTime = systemTime() + milliseconds(delayMs);
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index d4e59d1..e6e28df 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -3105,7 +3105,7 @@
 
         sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
 
-        int32_t fd = inputChannel->getFd();
+        int fd = inputChannel->getFd();
         mConnectionsByFd.add(fd, connection);
 
         if (monitor) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 34a8a02..b3eceb1 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -768,7 +768,9 @@
                     long identity = Binder.clearCallingIdentity();
                     try {
                         r.statusBarKey = mStatusBar.addNotification(n);
-                        mAttentionLight.pulse();
+                        if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
+                            mAttentionLight.pulse();
+                        }
                     }
                     finally {
                         Binder.restoreCallingIdentity(identity);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ac311b8..9d5caae 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6995,7 +6995,7 @@
                         /* TODO: Send this to all users */
                         broadcastIntentLocked(null, null, intent, null, finisher,
                                 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
-                                Process.SYSTEM_UID);
+                                0 /* UserId zero */);
                         if (finisher != null) {
                             mWaitingUpdate = true;
                         }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 895963a..83c9c3d 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -903,9 +903,6 @@
         settings.setWorkersEnabled(false);
         settings.setXSSAuditorEnabled(false);
         settings.setPageCacheCapacity(0);
-        // this enables cpu upload path (as opposed to gpu upload path)
-        // and it's only meant to be a temporary workaround!
-        settings.setProperty("enable_cpu_upload_path", "true");
         settings.setProperty("use_minimal_memory", "false");
     }
 
diff --git a/tests/RenderScriptTests/ComputePerf/src/com/example/android/rs/computeperf/mandelbrot.rs b/tests/RenderScriptTests/ComputePerf/src/com/example/android/rs/computeperf/mandelbrot.rs
index a7987b3..0ffb0e5 100644
--- a/tests/RenderScriptTests/ComputePerf/src/com/example/android/rs/computeperf/mandelbrot.rs
+++ b/tests/RenderScriptTests/ComputePerf/src/com/example/android/rs/computeperf/mandelbrot.rs
@@ -25,13 +25,14 @@
     p.y = -1.f + ((float)y / gDimY) * 2.f;
 
     float2 t = 0;
+    float2 t2 = t * t;
     int iteration = 0;
-    while((t.x*t.x + t.y*t.y < 4.f) && (iteration < gMaxIteration)) {
-        float2 t2 = t * t;
+    while((t2.x + t2.y < 4.f) && (iteration < gMaxIteration)) {
         float xtemp = t2.x - t2.y + p.x;
         t.y = 2 * t.x * t.y + p.y;
         t.x = xtemp;
         iteration++;
+        t2 = t * t;
     }
 
     if(iteration >= gMaxIteration) {
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 3615f60..7368260 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -283,6 +283,9 @@
         mRadius = MAX_RADIUS;
         mScript.set_radius(mRadius);
 
+        mScript.invoke_filter();
+        mRS.finish();
+
         long t = java.lang.System.currentTimeMillis();
 
         mScript.invoke_filter();
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl b/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl
new file mode 100644
index 0000000..656961c
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl
@@ -0,0 +1,8 @@
+varying vec2 varTex0;
+
+void main() {
+   lowp vec3 col0 = texture2D(UNI_Tex0, varTex0).rgb;
+   gl_FragColor.xyz = col0 * UNI_modulate.rgb;
+   gl_FragColor.w = UNI_modulate.a;
+}
+
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java
index ba70c71..41f664a 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java
@@ -35,18 +35,22 @@
     // Custom shaders
     private ProgramFragment mProgFragmentMultitex;
     private ProgramFragment mProgFragmentSingletex;
+    private ProgramFragment mProgFragmentSingletexModulate;
     private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
     int mBenchmarkDimX;
     int mBenchmarkDimY;
 
     private ScriptC_fill_test mFillScript;
     ScriptField_TestScripts_s.Item[] mTests;
+    ScriptField_FillTestFragData_s mFragData;
 
     private final String[] mNames = {
         "Fill screen 10x singletexture",
         "Fill screen 10x 3tex multitexture",
         "Fill screen 10x blended singletexture",
-        "Fill screen 10x blended 3tex multitexture"
+        "Fill screen 10x blended 3tex multitexture",
+        "Fill screen 3x modulate blended singletexture",
+        "Fill screen 1x modulate blended singletexture",
     };
 
     public FillTest() {
@@ -88,6 +92,8 @@
         addTest(index++, 0 /*testId*/, 0 /*blend*/, 10 /*quadCount*/);
         addTest(index++, 1 /*testId*/, 1 /*blend*/, 10 /*quadCount*/);
         addTest(index++, 0 /*testId*/, 1 /*blend*/, 10 /*quadCount*/);
+        addTest(index++, 2 /*testId*/, 1 /*blend*/, 3 /*quadCount*/);
+        addTest(index++, 2 /*testId*/, 1 /*blend*/, 1 /*quadCount*/);
 
         return true;
     }
@@ -112,6 +118,14 @@
         pfbCustom.setShader(mRes, R.raw.singletexf);
         pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
         mProgFragmentSingletex = pfbCustom.create();
+
+        pfbCustom = new ProgramFragment.Builder(mRS);
+        pfbCustom.setShader(mRes, R.raw.singletexfm);
+        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
+        mFragData = new ScriptField_FillTestFragData_s(mRS, 1);
+        pfbCustom.addConstant(mFragData.getType());
+        mProgFragmentSingletexModulate = pfbCustom.create();
+        mProgFragmentSingletexModulate.bindConstants(mFragData.getAllocation(), 0);
     }
 
     private Allocation loadTextureARGB(int id) {
@@ -140,6 +154,7 @@
         mFillScript.set_gProgVertex(progVertex);
 
         mFillScript.set_gProgFragmentTexture(mProgFragmentSingletex);
+        mFillScript.set_gProgFragmentTextureModulate(mProgFragmentSingletexModulate);
         mFillScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
         mFillScript.set_gProgStoreBlendNone(ProgramStore.BLEND_NONE_DEPTH_NONE(mRS));
         mFillScript.set_gProgStoreBlendAlpha(ProgramStore.BLEND_ALPHA_DEPTH_NONE(mRS));
@@ -150,5 +165,7 @@
         mFillScript.set_gTexOpaque(loadTextureRGB(R.drawable.data));
         mFillScript.set_gTexTransparent(loadTextureARGB(R.drawable.leaf));
         mFillScript.set_gTexChecker(loadTextureRGB(R.drawable.checker));
+
+        mFillScript.bind_gFragData(mFragData);
     }
 }
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs
index 23832d3..281f830 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs
@@ -21,6 +21,7 @@
 
 rs_program_vertex gProgVertex;
 rs_program_fragment gProgFragmentTexture;
+rs_program_fragment gProgFragmentTextureModulate;
 rs_program_fragment gProgFragmentMultitex;
 
 rs_program_store gProgStoreBlendNone;
@@ -41,6 +42,11 @@
 } FillTestData;
 FillTestData *gData;
 
+typedef struct FillTestFragData_s {
+    float4 modulate;
+} FillTestFragData;
+FillTestFragData *gFragData;
+
 static float gDt = 0.0f;
 
 void init() {
@@ -58,7 +64,7 @@
     rsgProgramVertexLoadProjectionMatrix(&proj);
 }
 
-static void displaySingletexFill(bool blend, int quadCount) {
+static void displaySingletexFill(bool blend, int quadCount, bool modulate) {
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
     rsMatrixLoadIdentity(&matrix);
@@ -70,9 +76,21 @@
     } else {
         rsgBindProgramStore(gProgStoreBlendAlpha);
     }
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+    if (modulate) {
+        rsgBindProgramFragment(gProgFragmentTextureModulate);
+        rsgBindSampler(gProgFragmentTextureModulate, 0, gLinearClamp);
+        rsgBindTexture(gProgFragmentTextureModulate, 0, gTexOpaque);
+
+        gFragData->modulate.r = 0.8f;
+        gFragData->modulate.g = 0.7f;
+        gFragData->modulate.b = 0.8f;
+        gFragData->modulate.a = 0.5f;
+        rsgAllocationSyncAll(rsGetAllocation(gFragData));
+    } else {
+        rsgBindProgramFragment(gProgFragmentTexture);
+        rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+        rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+    }
 
     for (int i = 0; i < quadCount; i ++) {
         float startX = 5 * i, startY = 5 * i;
@@ -128,7 +146,10 @@
             displayMultitextureSample(gData->blend == 1 ? true : false, gData->quadCount);
             break;
         case 1:
-            displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount);
+            displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount, false);
+            break;
+        case 2:
+            displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount, true);
             break;
         default:
             rsDebug("Wrong test number", 0);
diff --git a/tests/RenderScriptTests/SceneGraph/Android.mk b/tests/RenderScriptTests/SceneGraph/Android.mk
new file mode 100644
index 0000000..ba4b3c5
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2011 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := SceneGraphTest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
new file mode 100644
index 0000000..e8d1e8e
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testapp">
+    <uses-permission
+        android:name="android.permission.INTERNET" />
+    <application android:label="SceneGraphTest">
+        <activity android:name="TestApp"
+                  android:label="SceneGraphTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="FileSelector"
+                  android:label="FileSelector"
+                  android:hardwareAccelerated="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/SceneGraph/assets/blue.jpg b/tests/RenderScriptTests/SceneGraph/assets/blue.jpg
new file mode 100644
index 0000000..494e77a
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/blue.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg b/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg
new file mode 100644
index 0000000..2fcecb0
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/green.jpg b/tests/RenderScriptTests/SceneGraph/assets/green.jpg
new file mode 100644
index 0000000..a86a754
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/green.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/grey.jpg b/tests/RenderScriptTests/SceneGraph/assets/grey.jpg
new file mode 100644
index 0000000..5870b1a
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/grey.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orange.jpg b/tests/RenderScriptTests/SceneGraph/assets/orange.jpg
new file mode 100644
index 0000000..7dbe942
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/orange.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d
new file mode 100644
index 0000000..07318ae
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae
new file mode 100644
index 0000000..7eef443f
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae
@@ -0,0 +1,1102 @@
+<?xml version="1.0" ?>
+<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
+    <asset>
+        <contributor>
+            <author>alexst</author>
+            <authoring_tool>OpenCOLLADA2010</authoring_tool>
+            <comments>ColladaMaya export options: bakeTransforms=0;relativePaths=0;copyTextures=0;exportTriangles=1;exportCgfxFileReferences=0; isSampling=0;curveConstrainSampling=0;removeStaticCurves=1;exportPolygonMeshes=1;exportLights=1; exportCameras=1;exportJointsAndSkin=1;exportAnimations=0;exportInvisibleNodes=0;exportDefaultCameras=0; exportTexCoords=1;exportNormals=1;exportNormalsPerVertex=1;exportVertexColors=0;exportVertexColorsPerVertex=0; exportTexTangents=0;exportTangents=0;exportReferencedMaterials=1;exportMaterialsOnly=0; exportXRefs=1;dereferenceXRefs=1;exportCameraAsLookat=0;cameraXFov=0;cameraYFov=1;doublePrecision=0</comments>
+            <source_data>file:///Volumes/Android/art/orientation_test.mb</source_data>
+        </contributor>
+        <created>2011-09-30T15:31:38</created>
+        <modified>2011-09-30T15:31:38</modified>
+        <unit meter="0.01" name="centimeter" />
+        <up_axis>Y_UP</up_axis>
+    </asset>
+    <library_cameras>
+        <camera id="cameraShape1" name="cameraShape1">
+            <optics>
+                <technique_common>
+                    <perspective>
+                        <yfov>37.8493</yfov>
+                        <aspect_ratio>1.5</aspect_ratio>
+                        <znear>1</znear>
+                        <zfar>400</zfar>
+                    </perspective>
+                </technique_common>
+            </optics>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <film_fit>0</film_fit>
+                    <film_fit_offset>0</film_fit_offset>
+                    <film_offsetX>0</film_offsetX>
+                    <film_offsetY>0</film_offsetY>
+                    <horizontal_aperture>3.599993</horizontal_aperture>
+                    <lens_squeeze>1</lens_squeeze>
+                    <originalMayaNodeId>cameraShape1</originalMayaNodeId>
+                    <vertical_aperture>2.399995</vertical_aperture>
+                </technique>
+            </extra>
+        </camera>
+        <camera id="CameraDistShape" name="CameraDistShape">
+            <optics>
+                <technique_common>
+                    <perspective>
+                        <yfov>37.8493</yfov>
+                        <aspect_ratio>1.5</aspect_ratio>
+                        <znear>1</znear>
+                        <zfar>1000</zfar>
+                    </perspective>
+                </technique_common>
+            </optics>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <film_fit>0</film_fit>
+                    <film_fit_offset>0</film_fit_offset>
+                    <film_offsetX>0</film_offsetX>
+                    <film_offsetY>0</film_offsetY>
+                    <horizontal_aperture>3.599993</horizontal_aperture>
+                    <lens_squeeze>1</lens_squeeze>
+                    <originalMayaNodeId>CameraDistShape</originalMayaNodeId>
+                    <vertical_aperture>2.399995</vertical_aperture>
+                </technique>
+            </extra>
+        </camera>
+    </library_cameras>
+    <library_materials>
+        <material id="Paint1" name="Paint1">
+            <instance_effect url="#Paint1-fx" />
+        </material>
+        <material id="lambert2" name="lambert2">
+            <instance_effect url="#lambert2-fx" />
+        </material>
+        <material id="Plastic" name="Plastic">
+            <instance_effect url="#Plastic-fx" />
+        </material>
+        <material id="Metal" name="Metal">
+            <instance_effect url="#Metal-fx" />
+        </material>
+        <material id="PlasticCenter" name="PlasticCenter">
+            <instance_effect url="#PlasticCenter-fx" />
+        </material>
+        <material id="PlasticRed" name="PlasticRed">
+            <instance_effect url="#PlasticRed-fx" />
+        </material>
+        <material id="lambert10" name="lambert10">
+            <instance_effect url="#lambert10-fx" />
+        </material>
+        <material id="lambert11" name="lambert11">
+            <instance_effect url="#lambert11-fx" />
+        </material>
+    </library_materials>
+    <library_effects>
+        <effect id="Metal-fx">
+            <profile_COMMON>
+                <newparam sid="file23-surface">
+                    <surface type="2D">
+                        <init_from>file23</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file23-sampler">
+                    <sampler2D>
+                        <source>file23-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file23-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="Paint1-fx">
+            <profile_COMMON>
+                <newparam sid="file25-surface">
+                    <surface type="2D">
+                        <init_from>file25</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file25-sampler">
+                    <sampler2D>
+                        <source>file25-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file25-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="Plastic-fx">
+            <profile_COMMON>
+                <newparam sid="file24-surface">
+                    <surface type="2D">
+                        <init_from>file24</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file24-sampler">
+                    <sampler2D>
+                        <source>file24-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file24-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="PlasticCenter-fx">
+            <profile_COMMON>
+                <newparam sid="file24-surface">
+                    <surface type="2D">
+                        <init_from>file24</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file24-sampler">
+                    <sampler2D>
+                        <source>file24-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file24-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="PlasticRed-fx">
+            <profile_COMMON>
+                <newparam sid="file23-surface">
+                    <surface type="2D">
+                        <init_from>file23</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file23-sampler">
+                    <sampler2D>
+                        <source>file23-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file23-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="lambert10-fx">
+            <profile_COMMON>
+                <newparam sid="file28-surface">
+                    <surface type="2D">
+                        <init_from>file28</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file28-sampler">
+                    <sampler2D>
+                        <source>file28-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file28-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="lambert11-fx">
+            <profile_COMMON>
+                <newparam sid="file29-surface">
+                    <surface type="2D">
+                        <init_from>file29</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file29-sampler">
+                    <sampler2D>
+                        <source>file29-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file29-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="lambert2-fx">
+            <profile_COMMON>
+                <newparam sid="file22-surface">
+                    <surface type="2D">
+                        <init_from>file22</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file22-sampler">
+                    <sampler2D>
+                        <source>file22-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file22-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+    </library_effects>
+    <library_images>
+        <image id="file29" name="file29" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/blue.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file29</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file25" name="file25" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/carbonfiber.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file25</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file28" name="file28" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/green.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file28</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file22" name="file22" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/grey.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file22</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file24" name="file24" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/orange.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file24</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file23" name="file23" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/red.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file23</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+    </library_images>
+    <library_visual_scenes>
+        <visual_scene id="VisualSceneNode" name="orientation_test">
+            <node id="camera1" name="camera1">
+                <translate sid="translate">24.5791 14.1321 31.4654</translate>
+                <rotate sid="rotateZ">0 0 1 0</rotate>
+                <rotate sid="rotateY">0 1 0 42</rotate>
+                <rotate sid="rotateX">1 0 0 -16.2</rotate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_camera url="#cameraShape1" />
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>camera1</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="CameraAim" name="CameraAim">
+                <translate sid="translate">0.0209301 3.68542 2.06912</translate>
+                <rotate sid="rotateY">0 1 0 43.2561</rotate>
+                <rotate sid="rotateX">1 0 0 -20</rotate>
+                <scale sid="scale">1 1 1</scale>
+                <node id="CameraDist" name="CameraDist">
+                    <translate sid="translate">0 0 45</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_camera url="#CameraDistShape" />
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>CameraDist</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>CameraAim</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere4" name="pSphere4">
+                <translate sid="translate">-9.69237 0 7.70498</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape4">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere4</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere1" name="pSphere1">
+                <translate sid="translate">13.0966 0 5.76254</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape1">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere1</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere2" name="pSphere2">
+                <translate sid="translate">21.7661 0 -13.6375</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape2">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere2</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere3" name="pSphere3">
+                <translate sid="translate">-13.862 0 -13.6154</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape3">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere3</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere5" name="pSphere5">
+                <translate sid="translate">31.0862 0 18.5992</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape5">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere5</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube1" name="pCube1">
+                <translate sid="translate">0 0 0</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape1">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert4SG" target="#lambert2">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube1</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="group1" name="group1">
+                <translate sid="translate">0 0 0</translate>
+                <rotate sid="rotateZ">0 0 1 -162.693</rotate>
+                <rotate sid="rotateY">0 1 0 21.3345</rotate>
+                <rotate sid="rotateX">1 0 0 -100.567</rotate>
+                <scale sid="scale">1 1 1</scale>
+                <node id="pSphere6" name="pSphere6">
+                    <translate sid="translate">-13.862 0 -13.6154</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape6">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert6SG" target="#Plastic">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere6</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere7" name="pSphere7">
+                    <translate sid="translate">-9.69237 0 7.70498</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape7">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert6SG" target="#Plastic">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere7</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere8" name="pSphere8">
+                    <translate sid="translate">21.7661 0 -13.6375</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape8">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert6SG" target="#Plastic">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere8</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere9" name="pSphere9">
+                    <translate sid="translate">13.0966 0 5.76254</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape9">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert6SG" target="#Plastic">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere9</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>group1</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="group2" name="group2">
+                <translate sid="translate">0 0 0</translate>
+                <rotate sid="rotateZ">0 0 1 45.4017</rotate>
+                <rotate sid="rotateY">0 1 0 79.393</rotate>
+                <rotate sid="rotateX">1 0 0 5.10889</rotate>
+                <scale sid="scale">1 1 1</scale>
+                <node id="pSphere10" name="pSphere10">
+                    <translate sid="translate">31.0862 0 18.5992</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape10">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere10</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere11" name="pSphere11">
+                    <translate sid="translate">13.0966 0 5.76254</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape11">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere11</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere12" name="pSphere12">
+                    <translate sid="translate">7.4784 16.3496 7.36882</translate>
+                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
+                    <rotate sid="rotateY">0 1 0 158.666</rotate>
+                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape12">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere12</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere13" name="pSphere13">
+                    <translate sid="translate">-9.69237 0 7.70498</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape13">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere13</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere14" name="pSphere14">
+                    <translate sid="translate">11.3635 -4.3926 2.21012</translate>
+                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
+                    <rotate sid="rotateY">0 1 0 158.666</rotate>
+                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape14">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere14</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere15" name="pSphere15">
+                    <translate sid="translate">21.7661 0 -13.6375</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape15">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere15</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere16" name="pSphere16">
+                    <translate sid="translate">-9.5945 -8.92317 -5.74901</translate>
+                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
+                    <rotate sid="rotateY">0 1 0 158.666</rotate>
+                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape16">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere16</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere17" name="pSphere17">
+                    <translate sid="translate">-13.862 0 -13.6154</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape17">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere17</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere18" name="pSphere18">
+                    <translate sid="translate">-24.2135 6.497 -5.58935</translate>
+                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
+                    <rotate sid="rotateY">0 1 0 158.666</rotate>
+                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape18">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere18</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>group2</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube2" name="pCube2">
+                <translate sid="translate">0 0 0</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape2">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert8SG" target="#PlasticCenter">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube2</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube3" name="pCube3">
+                <translate sid="translate">15 0 0</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape3">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert9SG" target="#PlasticRed">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube3</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube4" name="pCube4">
+                <translate sid="translate">0 15 0</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape4">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert10SG" target="#lambert10">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube4</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube5" name="pCube5">
+                <translate sid="translate">0 0 15</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape5">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert11SG" target="#lambert11">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube5</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+        </visual_scene>
+    </library_visual_scenes>
+    <scene>
+        <instance_visual_scene url="#VisualSceneNode" />
+    </scene>
+</COLLADA>
diff --git a/tests/RenderScriptTests/SceneGraph/assets/paint.jpg b/tests/RenderScriptTests/SceneGraph/assets/paint.jpg
new file mode 100644
index 0000000..0791045
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/paint.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/red.jpg b/tests/RenderScriptTests/SceneGraph/assets/red.jpg
new file mode 100644
index 0000000..320a2a6
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/red.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png
new file mode 100644
index 0000000..f7353fd
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml b/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml
new file mode 100644
index 0000000..9ea30107
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2011 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.
+*/
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/load_model"
+          android:title="@string/load_model" />
+    <item android:id="@+id/use_blur"
+          android:title="@string/use_blur" />
+</menu>
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
new file mode 100644
index 0000000..fa468cc
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
@@ -0,0 +1,15 @@
+varying vec2 varTex0;
+
+void main() {
+   vec2 blurCoord = varTex0;
+   blurCoord.x = varTex0.x + UNI_blurOffset0;
+   vec3 col = texture2D(UNI_Tex0, blurCoord).rgb;
+   blurCoord.x = varTex0.x + UNI_blurOffset1;
+   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   blurCoord.x = varTex0.x + UNI_blurOffset2;
+   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   blurCoord.x = varTex0.x + UNI_blurOffset3;
+   col += texture2D(UNI_Tex0, blurCoord).rgb;
+
+   gl_FragColor = vec4(col * 0.25, 0.0); //texture2D(UNI_Tex0, varTex0);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl
new file mode 100644
index 0000000..a644a3e
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl
@@ -0,0 +1,17 @@
+varying vec2 varTex0;
+
+void main() {
+   vec2 blurCoord = varTex0;
+   blurCoord.y = varTex0.y + UNI_blurOffset0;
+   vec3 col = texture2D(UNI_Tex0, blurCoord).rgb;
+   blurCoord.y = varTex0.y + UNI_blurOffset1;
+   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   blurCoord.y = varTex0.y + UNI_blurOffset2;
+   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   blurCoord.y = varTex0.y + UNI_blurOffset3;
+   col += texture2D(UNI_Tex0, blurCoord).rgb;
+
+   col = col * 0.25;
+
+   gl_FragColor = vec4(col, 0.0); //texture2D(UNI_Tex0, varTex0);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl
new file mode 100644
index 0000000..bc824b6
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl
@@ -0,0 +1,7 @@
+varying vec2 varTex0;
+
+void main() {
+   gl_Position = ATTRIB_position;
+   varTex0 = ATTRIB_texture0;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
new file mode 100644
index 0000000..5d8938b
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
@@ -0,0 +1,19 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = (varWorldNormal);
+
+   vec3 light0Vec = V;
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = dot(worldNorm, light0Vec);
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   col.xyz = col.xyz * light0_Diffuse * 1.2;
+   gl_FragColor = col; //vec4(0.0, 1.0, 0.0, 0.0);
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
new file mode 100644
index 0000000..51f0612
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
@@ -0,0 +1,23 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = V;
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
+   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, 15.0) * 0.5;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   col.xyz = col.xyz * (textureCube(UNI_Tex1, worldNorm).rgb * 0.5 + vec3(light0_Diffuse));
+   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
+
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl
new file mode 100644
index 0000000..893d553
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl
@@ -0,0 +1,26 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = V;
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.01, 0.99);
+   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, 150.0) * 0.5;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   col.xyz = col.xyz * light0_Diffuse * 1.1;
+   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
+
+   float fresnel = mix(pow(1.0 - light0_Diffuse, 15.0), 1.0, 0.1);
+   col.xyz = mix(col.xyz, textureCube(UNI_Tex1, -light0R).rgb * 2.4, fresnel);
+   col.w = 0.8;
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl
new file mode 100644
index 0000000..ceb53bd
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl
@@ -0,0 +1,22 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = V;
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
+   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, 10.0) * 0.5;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   col.xyz = col.xyz * light0_Diffuse * 1.2;
+   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl
new file mode 100644
index 0000000..b253622
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl
@@ -0,0 +1,29 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = normalize(UNI_lightPos_0.xyz - varWorldPos.xyz);
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
+   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, 10.0) * 0.7;
+
+   vec3 light1Vec = normalize(UNI_lightPos_1.xyz - varWorldPos.xyz);
+   vec3 light1R = reflect(light1Vec, worldNorm);
+   float light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0);
+   float light1Spec = clamp(dot(-light1R, V), 0.001, 1.0);
+   float light1_Specular = pow(light1Spec, 10.0) * 0.7;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = UNI_diffuse;
+   col.xyz = col.xyz * (light0_Diffuse * UNI_lightColor_0.xyz +
+                        light1_Diffuse * UNI_lightColor_1.xyz);
+   col.xyz += (light0_Specular + light1_Specular);
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d b/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d
new file mode 100644
index 0000000..f48895c
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
new file mode 100644
index 0000000..42b231a
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
@@ -0,0 +1,13 @@
+varying vec2 varTex0;
+
+void main() {
+   vec3 col = texture2D(UNI_Tex0, varTex0).rgb;
+
+   vec3 desat = vec3(0.299, 0.587, 0.114);
+   float lum = dot(desat, col);
+   float stepVal = step(lum, 0.8);
+   col = mix(col, vec3(0.0), stepVal)*0.5;
+
+   gl_FragColor = vec4(col, 0.0);
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl
new file mode 100644
index 0000000..1ea234f
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl
@@ -0,0 +1,17 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+// This is where actual shader code begins
+void main() {
+   vec4 objPos = ATTRIB_position;
+   vec4 worldPos = UNI_model * objPos;
+   gl_Position = UNI_viewProj * worldPos;
+
+   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
+   vec3 worldNorm = model3 * ATTRIB_normal;
+
+   varWorldPos = worldPos.xyz;
+   varWorldNormal = worldNorm;
+   varTex0 = ATTRIB_texture0;
+}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
new file mode 100644
index 0000000..dd709cf
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
@@ -0,0 +1,7 @@
+varying vec2 varTex0;
+
+void main() {
+   lowp vec4 col = texture2D(UNI_Tex0, varTex0).rgba;
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/values/strings.xml b/tests/RenderScriptTests/SceneGraph/res/values/strings.xml
new file mode 100644
index 0000000..c916d79
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2011 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <skip />
+    <string name="load_model">Load Model</string>
+    <string name="use_blur">Use Blur</string>
+</resources>
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java
new file mode 100644
index 0000000..42f2be5
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.SceneManager;
+
+import android.renderscript.*;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Camera extends SceneGraphBase {
+
+    Transform mTransform;
+
+    ScriptField_Camera_s.Item mData;
+    ScriptField_Camera_s mField;
+
+    public Camera() {
+        mData = new ScriptField_Camera_s.Item();
+        mData.near = 0.1f;
+        mData.far = 1000.0f;
+        mData.horizontalFOV = 60.0f;
+        mData.aspect = 0;
+    }
+
+    public void setTransform(Transform t) {
+        mTransform = t;
+        if (mField != null) {
+            mField.set_transformMatrix(0, mTransform.getRSData().getAllocation(), true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+    public void setFOV(float fov) {
+        mData.horizontalFOV = fov;
+        if (mField != null) {
+            mField.set_horizontalFOV(0, fov, true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+
+    public void setNear(float n) {
+        mData.near = n;
+        if (mField != null) {
+            mField.set_near(0, n, true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+
+    public void setFar(float f) {
+        mData.far = f;
+        if (mField != null) {
+            mField.set_far(0, f, true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+
+    public void setName(String n) {
+        super.setName(n);
+        if (mField != null) {
+            RenderScriptGL rs = SceneManager.getRS();
+            mData.name = getNameAlloc(rs);
+            mField.set_name(0, mData.name, true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+
+    ScriptField_Camera_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+
+        if (mTransform == null) {
+            throw new RuntimeException("Cameras without transforms are invalid");
+        }
+
+        mField = new ScriptField_Camera_s(rs, 1);
+
+        mData.transformMatrix = mTransform.getRSData().getAllocation();
+        mData.transformTimestamp = 1;
+        mData.timestamp = 1;
+        mData.isDirty = 1;
+        mData.name = getNameAlloc(rs);
+        mField.set(mData, 0, true);
+
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
new file mode 100644
index 0000000..d954313
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
@@ -0,0 +1,563 @@
+/*

+ * Copyright (C) 2011 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.scenegraph;

+import com.android.scenegraph.CompoundTransform.TranslateComponent;

+import com.android.scenegraph.CompoundTransform.RotateComponent;

+import com.android.scenegraph.CompoundTransform.ScaleComponent;

+import java.io.IOException;

+import java.io.InputStream;

+import java.util.ArrayList;

+import java.util.Iterator;

+import java.util.List;

+import java.util.StringTokenizer;

+import java.util.HashMap;

+

+import javax.xml.parsers.DocumentBuilder;

+import javax.xml.parsers.DocumentBuilderFactory;

+import javax.xml.parsers.ParserConfigurationException;

+

+import org.w3c.dom.Document;

+import org.w3c.dom.Element;

+import org.w3c.dom.Node;

+import org.w3c.dom.NodeList;

+import org.xml.sax.SAXException;

+

+import android.renderscript.*;

+import android.util.Log;

+

+public class ColladaParser {

+    static final String TAG = "ColladaParser";

+    Document mDom;

+

+    HashMap<String, LightBase> mLights;

+    HashMap<String, Camera> mCameras;

+    HashMap<String, ArrayList<ShaderParam> > mEffectsParams;

+    HashMap<String, Texture2D> mImages;

+    HashMap<String, Texture2D> mSamplerImageMap;

+    HashMap<String, String> mMeshIdNameMap;

+    Scene mScene;

+

+    String mRootDir;

+

+    String toString(Float3 v) {

+        String valueStr = v.x + " " + v.y + " " + v.z;

+        return valueStr;

+    }

+

+    String toString(Float4 v) {

+        String valueStr = v.x + " " + v.y + " " + v.z + " " + v.w;

+        return valueStr;

+    }

+

+    public ColladaParser(){

+        mLights = new HashMap<String, LightBase>();

+        mCameras = new HashMap<String, Camera>();

+        mEffectsParams = new HashMap<String, ArrayList<ShaderParam> >();

+        mImages = new HashMap<String, Texture2D>();

+        mMeshIdNameMap = new HashMap<String, String>();

+    }

+

+    public void init(InputStream is, String rootDir) {

+        mLights.clear();

+        mCameras.clear();

+        mEffectsParams.clear();

+

+        mRootDir = rootDir;

+

+        long start = System.currentTimeMillis();

+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

+        try {

+            DocumentBuilder db = dbf.newDocumentBuilder();

+            mDom = db.parse(is);

+        } catch(ParserConfigurationException e) {

+            e.printStackTrace();

+        } catch(SAXException e) {

+            e.printStackTrace();

+        } catch(IOException e) {

+            e.printStackTrace();

+        }

+        long end = System.currentTimeMillis();

+        Log.v("TIMER", "    Parse time: " + (end - start));

+        exportSceneData();

+    }

+

+    Scene getScene() {

+        return mScene;

+    }

+

+    private void exportSceneData(){

+        mScene = new Scene();

+

+        Element docEle = mDom.getDocumentElement();

+        NodeList nl = docEle.getElementsByTagName("light");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element l = (Element)nl.item(i);

+                convertLight(l);

+            }

+        }

+

+        nl = docEle.getElementsByTagName("camera");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element c = (Element)nl.item(i);

+                convertCamera(c);

+            }

+        }

+

+        nl = docEle.getElementsByTagName("image");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element img = (Element)nl.item(i);

+                convertImage(img);

+            }

+        }

+

+        nl = docEle.getElementsByTagName("effect");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element e = (Element)nl.item(i);

+                convertEffects(e);

+            }

+        }

+

+        // Material is just a link to the effect

+        nl = docEle.getElementsByTagName("material");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element m = (Element)nl.item(i);

+                convertMaterials(m);

+            }

+        }

+

+        // Look through the geometry list and build up a correlation between id's and names

+        nl = docEle.getElementsByTagName("geometry");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element m = (Element)nl.item(i);

+                convertGeometries(m);

+            }

+        }

+

+

+        nl = docEle.getElementsByTagName("visual_scene");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element s = (Element)nl.item(i);

+                getScene(s);

+            }

+        }

+    }

+

+    private void getRenderable(Element shape, Transform t) {

+        String geoURL = shape.getAttribute("url").substring(1);

+        String geoName = mMeshIdNameMap.get(geoURL);

+        if (geoName != null) {

+            geoURL = geoName;

+        }

+        //RenderableGroup group = new RenderableGroup();

+        //group.setName(geoURL.substring(1));

+        //mScene.appendRenderable(group);

+        NodeList nl = shape.getElementsByTagName("instance_material");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element materialRef = (Element)nl.item(i);

+                String meshIndexName = materialRef.getAttribute("symbol");

+                String materialName = materialRef.getAttribute("target");

+

+                Renderable d = new Renderable();

+                d.setMesh(geoURL, meshIndexName);

+                d.setMaterialName(materialName.substring(1));

+                d.setName(geoURL);

+

+                //Log.v(TAG, "Created drawable geo " + geoURL + " index " + meshIndexName + " material " + materialName);

+

+                d.setTransform(t);

+                //Log.v(TAG, "Set source param " + t.getName());

+

+                // Now find all the parameters that exist on the material

+                ArrayList<ShaderParam> materialParams;

+                materialParams = mEffectsParams.get(materialName.substring(1));

+                for (int pI = 0; pI < materialParams.size(); pI ++) {

+                    d.appendSourceParams(materialParams.get(pI));

+                    //Log.v(TAG, "Set source param i: " + pI + " name " + materialParams.get(pI).getParamName());

+                }

+                mScene.appendRenderable(d);

+                //group.appendChildren(d);

+            }

+        }

+    }

+

+    private void updateLight(Element shape, Transform t) {

+        String lightURL = shape.getAttribute("url");

+        // collada uses a uri structure to link things,

+        // but we ignore it for now and do a simple search

+        LightBase light = mLights.get(lightURL.substring(1));

+        if (light != null) {

+            light.setTransform(t);

+            //Log.v(TAG, "Set Light " + light.getName() + " " + t.getName());

+        }

+    }

+

+    private void updateCamera(Element shape, Transform t) {

+        String camURL = shape.getAttribute("url");

+        // collada uses a uri structure to link things,

+        // but we ignore it for now and do a simple search

+        Camera cam = mCameras.get(camURL.substring(1));

+        if (cam != null) {

+            cam.setTransform(t);

+            //Log.v(TAG, "Set Camera " + cam.getName() + " " + t.getName());

+        }

+    }

+

+    private void getNode(Element node, Transform parent, String indent) {

+        String name = node.getAttribute("name");

+        String id = node.getAttribute("id");

+        CompoundTransform current = new CompoundTransform();

+        current.setName(name);

+        if (parent != null) {

+            parent.appendChild(current);

+        } else {

+            mScene.appendTransform(current);

+        }

+

+        mScene.addToTransformMap(current);

+

+        //Log.v(TAG, indent + "|");

+        //Log.v(TAG, indent + "[" + name + "]");

+

+        Node childNode = node.getFirstChild();

+        while (childNode != null) {

+            if (childNode.getNodeType() == Node.ELEMENT_NODE) {

+                Element field = (Element)childNode;

+                String fieldName = field.getTagName();

+                String description = field.getAttribute("sid");

+                if (fieldName.equals("translate")) {

+                    Float3 value = getFloat3(field);

+                    current.addComponent(new TranslateComponent(description, value));

+                    //Log.v(TAG, indent + " translate " + description + toString(value));

+                } else if (fieldName.equals("rotate")) {

+                    Float4 value = getFloat4(field);

+                    //Log.v(TAG, indent + " rotate " + description + toString(value));

+                    Float3 axis = new Float3(value.x, value.y, value.z);

+                    current.addComponent(new RotateComponent(description, axis, value.w));

+                } else if (fieldName.equals("scale")) {

+                    Float3 value = getFloat3(field);

+                    //Log.v(TAG, indent + " scale " + description + toString(value));

+                    current.addComponent(new ScaleComponent(description, value));

+                } else if (fieldName.equals("instance_geometry")) {

+                    getRenderable(field, current);

+                } else if (fieldName.equals("instance_light")) {

+                    updateLight(field, current);

+                } else if (fieldName.equals("instance_camera")) {

+                    updateCamera(field, current);

+                } else if (fieldName.equals("node")) {

+                    getNode(field, current, indent + "   ");

+                }

+            }

+            childNode = childNode.getNextSibling();

+        }

+    }

+

+    // This will find the actual texture node, which is sometimes hidden behind a sampler

+    // and sometimes referenced directly

+    Texture2D getTexture(String samplerName) {

+        String texName = samplerName;

+

+        // Check to see if the image file is hidden by a sampler surface link combo

+        Element sampler = mDom.getElementById(samplerName);

+        if (sampler != null) {

+            NodeList nl = sampler.getElementsByTagName("source");

+            if (nl != null && nl.getLength() == 1) {

+                Element ref = (Element)nl.item(0);

+                String surfaceName = getString(ref);

+                if (surfaceName == null) {

+                    return null;

+                }

+

+                Element surface = mDom.getElementById(surfaceName);

+                if (surface == null) {

+                    return null;

+                }

+                nl = surface.getElementsByTagName("init_from");

+                if (nl != null && nl.getLength() == 1) {

+                    ref = (Element)nl.item(0);

+                    texName = getString(ref);

+                }

+            }

+        }

+

+        //Log.v(TAG, "Extracted texture name " + texName);

+        return mImages.get(texName);

+    }

+

+    void extractParams(Element fx, ArrayList<ShaderParam> params) {

+        Node paramNode = fx.getFirstChild();

+        while (paramNode != null) {

+            if (paramNode.getNodeType() == Node.ELEMENT_NODE) {

+                String name = paramNode.getNodeName();

+                // Now find what type it is

+                Node typeNode = paramNode.getFirstChild();

+                while (typeNode != null && typeNode.getNodeType() != Node.ELEMENT_NODE) {

+                    typeNode = typeNode.getNextSibling();

+                }

+                String paramType = typeNode.getNodeName();

+                Element typeElem = (Element)typeNode;

+                ShaderParam sceneParam = null;

+                if (paramType.equals("color")) {

+                    Float4Param f4p = new Float4Param(name);

+                    Float4 value = getFloat4(typeElem);

+                    f4p.setValue(value);

+                    sceneParam = f4p;

+                    //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + toString(value));

+                } else if (paramType.equals("float")) {

+                    Float4Param f4p = new Float4Param(name);

+                    float value = getFloat(typeElem);

+                    f4p.setValue(new Float4(value, value, value, value));

+                    sceneParam = f4p;

+                    //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + value);

+                }  else if (paramType.equals("texture")) {

+                    String samplerName = typeElem.getAttribute("texture");

+                    Texture2D tex = getTexture(samplerName);

+                    TextureParam texP = new TextureParam(name);

+                    texP.setTexture(tex);

+                    sceneParam = texP;

+                    //Log.v(TAG, "Extracted texture " + tex);

+                }

+                if (sceneParam != null) {

+                    params.add(sceneParam);

+                }

+            }

+            paramNode = paramNode.getNextSibling();

+        }

+    }

+

+    private void convertMaterials(Element mat) {

+        String id = mat.getAttribute("id");

+        NodeList nl = mat.getElementsByTagName("instance_effect");

+        if (nl != null && nl.getLength() == 1) {

+            Element ref = (Element)nl.item(0);

+            String url = ref.getAttribute("url");

+            ArrayList<ShaderParam> params = mEffectsParams.get(url.substring(1));

+            mEffectsParams.put(id, params);

+        }

+    }

+

+    private void convertGeometries(Element geo) {

+        String id = geo.getAttribute("id");

+        String name = geo.getAttribute("name");

+        if (!id.equals(name)) {

+            mMeshIdNameMap.put(id, name);

+        }

+    }

+

+    private void convertEffects(Element fx) {

+        String id = fx.getAttribute("id");

+        ArrayList<ShaderParam> params = new ArrayList<ShaderParam>();

+

+        NodeList nl = fx.getElementsByTagName("newparam");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element field = (Element)nl.item(i);

+                field.setIdAttribute("sid", true);

+            }

+        }

+

+        nl = fx.getElementsByTagName("blinn");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element field = (Element)nl.item(i);

+                //Log.v(TAG, "blinn");

+                extractParams(field, params);

+            }

+        }

+        nl = fx.getElementsByTagName("lambert");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element field = (Element)nl.item(i);

+                //Log.v(TAG, "lambert");

+                extractParams(field, params);

+            }

+        }

+        nl = fx.getElementsByTagName("phong");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element field = (Element)nl.item(i);

+                //Log.v(TAG, "phong");

+                extractParams(field, params);

+            }

+        }

+        mEffectsParams.put(id, params);

+    }

+

+    private void convertLight(Element light) {

+        String name = light.getAttribute("name");

+        String id = light.getAttribute("id");

+

+        // Determine type

+        String[] knownTypes = { "point", "spot", "directional" };

+        final int POINT_LIGHT = 0;

+        final int SPOT_LIGHT = 1;

+        final int DIR_LIGHT = 2;

+        int type = -1;

+        for (int i = 0; i < knownTypes.length; i ++) {

+            NodeList nl = light.getElementsByTagName(knownTypes[i]);

+            if (nl != null && nl.getLength() != 0) {

+                type = i;

+                break;

+            }

+        }

+

+        //Log.v(TAG, "Found Light Type " + type);

+

+        LightBase sceneLight = null;

+        switch (type) {

+        case POINT_LIGHT:

+            sceneLight = new PointLight();

+            break;

+        case SPOT_LIGHT: // TODO: finish light types

+            break;

+        case DIR_LIGHT: // TODO: finish light types

+            break;

+        }

+

+        if (sceneLight == null) {

+            return;

+        }

+

+        Float3 color = getFloat3(light, "color");

+        sceneLight.setColor(color.x, color.y, color.z);

+        sceneLight.setName(name);

+        mScene.appendLight(sceneLight);

+        mLights.put(id, sceneLight);

+

+        //Log.v(TAG, "Light " + name + " color " + toString(color));

+    }

+

+    private void convertCamera(Element camera) {

+        String name = camera.getAttribute("name");

+        String id = camera.getAttribute("id");

+        float fov = 30.0f;

+        if (getString(camera, "yfov") != null) {

+            fov = getFloat(camera, "yfov");

+        } else if(getString(camera, "xfov") != null) {

+            float aspect = getFloat(camera, "aspect_ratio");

+            fov = getFloat(camera, "xfov") / aspect;

+        }

+

+        float near = getFloat(camera, "znear");

+        float far = getFloat(camera, "zfar");

+

+        Camera sceneCamera = new Camera();

+        sceneCamera.setFOV(fov);

+        sceneCamera.setNear(near);

+        sceneCamera.setFar(far);

+        sceneCamera.setName(name);

+        mScene.appendCamera(sceneCamera);

+        mCameras.put(id, sceneCamera);

+    }

+

+    private void convertImage(Element img) {

+        String name = img.getAttribute("name");

+        String id = img.getAttribute("id");

+        String file = getString(img, "init_from");

+

+        Texture2D tex = new Texture2D();

+        tex.setFileName(file);

+        tex.setFileDir(mRootDir);

+        mScene.appendTextures(tex);

+        mImages.put(id, tex);

+    }

+

+    private void getScene(Element scene) {

+        String name = scene.getAttribute("name");

+        String id = scene.getAttribute("id");

+

+        Node childNode = scene.getFirstChild();

+        while (childNode != null) {

+            if (childNode.getNodeType() == Node.ELEMENT_NODE) {

+                String indent = "";

+                getNode((Element)childNode, null, indent);

+            }

+            childNode = childNode.getNextSibling();

+        }

+    }

+

+    private String getString(Element elem, String name) {

+        String text = null;

+        NodeList nl = elem.getElementsByTagName(name);

+        if (nl != null && nl.getLength() != 0) {

+            text = ((Element)nl.item(0)).getFirstChild().getNodeValue();

+        }

+        return text;

+    }

+

+    private String getString(Element elem) {

+        String text = null;

+        text = elem.getFirstChild().getNodeValue();

+        return text;

+    }

+

+    private int getInt(Element elem, String name) {

+        return Integer.parseInt(getString(elem, name));

+    }

+

+    private float getFloat(Element elem, String name) {

+        return Float.parseFloat(getString(elem, name));

+    }

+

+    private float getFloat(Element elem) {

+        return Float.parseFloat(getString(elem));

+    }

+

+    private Float3 parseFloat3(String valueString) {

+        StringTokenizer st = new StringTokenizer(valueString);

+        float x = Float.parseFloat(st.nextToken());

+        float y = Float.parseFloat(st.nextToken());

+        float z = Float.parseFloat(st.nextToken());

+        return new Float3(x, y, z);

+    }

+

+    private Float4 parseFloat4(String valueString) {

+        StringTokenizer st = new StringTokenizer(valueString);

+        float x = Float.parseFloat(st.nextToken());

+        float y = Float.parseFloat(st.nextToken());

+        float z = Float.parseFloat(st.nextToken());

+        float w = Float.parseFloat(st.nextToken());

+        return new Float4(x, y, z, w);

+    }

+

+    private Float3 getFloat3(Element elem, String name) {

+        String valueString = getString(elem, name);

+        return parseFloat3(valueString);

+    }

+

+    private Float4 getFloat4(Element elem, String name) {

+        String valueString = getString(elem, name);

+        return parseFloat4(valueString);

+    }

+

+    private Float3 getFloat3(Element elem) {

+        String valueString = getString(elem);

+        return parseFloat3(valueString);

+    }

+

+    private Float4 getFloat4(Element elem) {

+        String valueString = getString(elem);

+        return parseFloat4(valueString);

+    }

+}

diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java
new file mode 100644
index 0000000..301075e
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Allocation.MipmapControl;
+import android.renderscript.Element.Builder;
+import android.renderscript.Font.Style;
+import android.renderscript.Program.TextureType;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+import com.android.scenegraph.SceneManager.SceneLoadedCallback;
+
+
+public class ColladaScene {
+
+    private String modelName;
+    private static String TAG = "ColladaScene";
+    private final int STATE_LAST_FOCUS = 1;
+    boolean mLoadFromSD = false;
+
+    SceneLoadedCallback mCallback;
+
+    public ColladaScene(String name, SceneLoadedCallback cb) {
+        modelName = name;
+        mCallback = cb;
+    }
+
+    public void init(RenderScriptGL rs, Resources res) {
+        mRS = rs;
+        mRes = res;
+
+        mLoadFromSD = SceneManager.isSDCardPath(modelName);
+
+        new ColladaLoaderTask().execute(modelName);
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    Scene mActiveScene;
+
+    private class ColladaLoaderTask extends AsyncTask<String, Void, Boolean> {
+        ColladaParser sceneSource;
+        protected Boolean doInBackground(String... names) {
+            String rootDir = names[0].substring(0, names[0].lastIndexOf('/') + 1);
+            long start = System.currentTimeMillis();
+            sceneSource = new ColladaParser();
+            InputStream is = null;
+            try {
+                if (!mLoadFromSD) {
+                    is = mRes.getAssets().open(names[0]);
+                } else {
+                    File f = new File(names[0]);
+                    is = new BufferedInputStream(new FileInputStream(f));
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Could not open collada file");
+                return new Boolean(false);
+            }
+            long end = System.currentTimeMillis();
+            Log.v("TIMER", "Stream load time: " + (end - start));
+
+            start = System.currentTimeMillis();
+            sceneSource.init(is, rootDir);
+            end = System.currentTimeMillis();
+            Log.v("TIMER", "Collada parse time: " + (end - start));
+            return new Boolean(true);
+        }
+
+        protected void onPostExecute(Boolean result) {
+            mActiveScene = sceneSource.getScene();
+            if (mCallback != null) {
+                mCallback.mLoadedScene = mActiveScene;
+                mCallback.run();
+            }
+
+            String shortName = modelName.substring(0, modelName.lastIndexOf('.'));
+            new A3DLoaderTask().execute(shortName + ".a3d");
+        }
+    }
+
+    private class A3DLoaderTask extends AsyncTask<String, Void, Boolean> {
+        protected Boolean doInBackground(String... names) {
+            long start = System.currentTimeMillis();
+            FileA3D model;
+            if (!mLoadFromSD) {
+                model = FileA3D.createFromAsset(mRS, mRes.getAssets(), names[0]);
+            } else {
+                model = FileA3D.createFromFile(mRS, names[0]);
+            }
+            int numModels = model.getIndexEntryCount();
+            for (int i = 0; i < numModels; i ++) {
+                FileA3D.IndexEntry entry = model.getIndexEntry(i);
+                if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
+                    mActiveScene.meshLoaded(entry.getMesh());
+                }
+            }
+            long end = System.currentTimeMillis();
+            Log.v("TIMER", "A3D load time: " + (end - start));
+            return new Boolean(true);
+        }
+
+        protected void onPostExecute(Boolean result) {
+        }
+    }
+
+}
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
new file mode 100644
index 0000000..d995dd0
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.SceneManager;
+
+import android.renderscript.*;
+import android.renderscript.Float3;
+import android.renderscript.Matrix4f;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class CompoundTransform extends Transform {
+
+    public static abstract class Component {
+        String mName;
+        CompoundTransform mParent;
+        int mParentIndex;
+        protected ScriptField_TransformComponent_s.Item mData;
+
+        Component(int type, String name) {
+            mData = new ScriptField_TransformComponent_s.Item();
+            mData.type = type;
+            mName = name;
+        }
+
+        void setNameAlloc() {
+            RenderScriptGL rs = SceneManager.getRS();
+            if (mData.name != null)  {
+                return;
+            }
+            mData.name = SceneManager.getCachedAlloc(getName());
+            if (mData.name == null) {
+                mData.name = SceneManager.getStringAsAllocation(rs, getName());
+                SceneManager.cacheAlloc(getName(), mData.name);
+            }
+        }
+
+        ScriptField_TransformComponent_s.Item getRSData() {
+            setNameAlloc();
+            return mData;
+        }
+
+        protected void update() {
+            if (mParent != null) {
+                mParent.updateRSComponent(this);
+            }
+        }
+
+        public String getName() {
+            return mName;
+        }
+    }
+
+    public static class TranslateComponent extends Component {
+        public TranslateComponent(String name, Float3 translate) {
+            super(ScriptC_export.const_Transform_TRANSLATE, name);
+            setValue(translate);
+        }
+        public Float3 getValue() {
+            return new Float3(mData.value.x, mData.value.y, mData.value.z);
+        }
+        public void setValue(Float3 val) {
+            mData.value.x = val.x;
+            mData.value.y = val.y;
+            mData.value.z = val.z;
+            update();
+        }
+    }
+
+    public static class RotateComponent extends Component {
+        public RotateComponent(String name, Float3 axis, float angle) {
+            super(ScriptC_export.const_Transform_ROTATE, name);
+            setAxis(axis);
+            setAngle(angle);
+        }
+        public Float3 getAxis() {
+            return new Float3(mData.value.x, mData.value.y, mData.value.z);
+        }
+        public float getAngle() {
+            return mData.value.w;
+        }
+        public void setAxis(Float3 val) {
+            mData.value.x = val.x;
+            mData.value.y = val.y;
+            mData.value.z = val.z;
+            update();
+        }
+        public void setAngle(float val) {
+            mData.value.w = val;
+            update();
+        }
+    }
+
+    public static class ScaleComponent extends Component {
+        public ScaleComponent(String name, Float3 scale) {
+            super(ScriptC_export.const_Transform_SCALE, name);
+            setValue(scale);
+        }
+        public Float3 getValue() {
+            return new Float3(mData.value.x, mData.value.y, mData.value.z);
+        }
+        public void setValue(Float3 val) {
+            mData.value.x = val.x;
+            mData.value.y = val.y;
+            mData.value.z = val.z;
+            update();
+        }
+    }
+
+    ScriptField_TransformComponent_s mComponentField;
+    public ArrayList<Component> mTransformComponents;
+
+    public CompoundTransform() {
+        mTransformComponents = new ArrayList<Component>();
+    }
+
+    public void addComponent(Component c) {
+        if (c.mParent != null) {
+            throw new IllegalArgumentException("Transform components may not be shared");
+        }
+        c.mParent = this;
+        c.mParentIndex = mTransformComponents.size();
+        mTransformComponents.add(c);
+        updateRSComponentAllocation();
+    }
+
+    public void setComponent(int index, Component c) {
+        if (c.mParent != null) {
+            throw new IllegalArgumentException("Transform components may not be shared");
+        }
+        if (index >= mTransformComponents.size()) {
+            throw new IllegalArgumentException("Invalid component index");
+        }
+        c.mParent = this;
+        c.mParentIndex = index;
+        mTransformComponents.set(index, c);
+        updateRSComponent(c);
+    }
+
+    void updateRSComponent(Component c) {
+        if (mField == null || mComponentField == null) {
+            return;
+        }
+        mComponentField.set(c.getRSData(), c.mParentIndex, true);
+        mField.set_isDirty(0, 1, true);
+    }
+
+    void updateRSComponentAllocation() {
+        if (mField == null) {
+            return;
+        }
+        initLocalData();
+
+        mField.set_components(0, mTransformData.components, false);
+        mField.set_isDirty(0, 1, true);
+    }
+
+    void initLocalData() {
+        RenderScriptGL rs = SceneManager.getRS();
+        int numComponenets = mTransformComponents.size();
+        if (numComponenets > 0) {
+            mComponentField = new ScriptField_TransformComponent_s(rs, numComponenets);
+            for (int i = 0; i < numComponenets; i ++) {
+                Component ith = mTransformComponents.get(i);
+                mComponentField.set(ith.getRSData(), i, false);
+            }
+            mComponentField.copyAll();
+
+            mTransformData.components = mComponentField.getAllocation();
+        }
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java
new file mode 100644
index 0000000..1502458
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.Scene;
+import com.android.scenegraph.SceneManager;
+
+import android.renderscript.Element;
+import android.renderscript.Float4;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Float4Param extends ShaderParam {
+    private static String TAG = "Float4Param";
+
+    LightBase mLight;
+
+    public Float4Param(String name) {
+        super(name);
+    }
+
+    public Float4Param(String name, float x) {
+        super(name);
+        set(x, 0, 0, 0);
+    }
+
+    public Float4Param(String name, float x, float y) {
+        super(name);
+        set(x, y, 0, 0);
+    }
+
+    public Float4Param(String name, float x, float y, float z) {
+        super(name);
+        set(x, y, z, 0);
+    }
+
+    public Float4Param(String name, float x, float y, float z, float w) {
+        super(name);
+        set(x, y, z, w);
+    }
+
+    void set(float x, float y, float z, float w) {
+        mData.float_value.x = x;
+        mData.float_value.y = y;
+        mData.float_value.z = z;
+        mData.float_value.w = w;
+        if (mField != null) {
+            mField.set_float_value(0, mData.float_value, true);
+        }
+        incTimestamp();
+    }
+
+    public void setValue(Float4 v) {
+        set(v.x, v.y, v.z, v.w);
+    }
+
+    public Float4 getValue() {
+        return mData.float_value;
+    }
+
+    public void setLight(LightBase l) {
+        mLight = l;
+        if (mField != null) {
+            mData.light = mLight.getRSData().getAllocation();
+            mField.set_light(0, mData.light, true);
+        }
+        incTimestamp();
+    }
+
+    boolean findLight(String property) {
+        String indexStr = mParamName.substring(property.length() + 1);
+        if (indexStr == null) {
+            Log.e(TAG, "Invalid light index.");
+            return false;
+        }
+        int index = Integer.parseInt(indexStr);
+        if (index == -1) {
+            return false;
+        }
+        Scene parentScene = SceneManager.getInstance().getActiveScene();
+        ArrayList<LightBase> allLights = parentScene.getLights();
+        if (index >= allLights.size()) {
+            return false;
+        }
+        mLight = allLights.get(index);
+        if (mLight == null) {
+            return false;
+        }
+        return true;
+    }
+
+    int getTypeFromName() {
+        int paramType = ScriptC_export.const_ShaderParam_FLOAT4_DATA;
+        if (mParamName.equalsIgnoreCase(cameraPos)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_CAMERA_POS;
+        } else if(mParamName.equalsIgnoreCase(cameraDir)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_CAMERA_DIR;
+        } else if(mParamName.startsWith(lightColor) && findLight(lightColor)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_COLOR;
+        } else if(mParamName.startsWith(lightPos) && findLight(lightPos)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_POS;
+        } else if(mParamName.startsWith(lightDir) && findLight(lightDir)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_DIR;
+        }
+        return paramType;
+    }
+
+    void initLocalData() {
+        mData.type = getTypeFromName();
+        if (mCamera != null) {
+            mData.camera = mCamera.getRSData().getAllocation();
+        }
+        if (mLight != null) {
+            mData.light = mLight.getRSData().getAllocation();
+        }
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
new file mode 100644
index 0000000..c8cc3ac
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.TextureBase;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramFragment.Builder;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class FragmentShader extends Shader {
+    ProgramFragment mProgram;
+    ScriptField_FragmentShader_s mField;
+
+    public static class Builder {
+
+        FragmentShader mShader;
+        ProgramFragment.Builder mBuilder;
+
+        public Builder(RenderScriptGL rs) {
+            mShader = new FragmentShader();
+            mBuilder = new ProgramFragment.Builder(rs);
+        }
+
+        public Builder setShader(Resources resources, int resourceID) {
+            mBuilder.setShader(resources, resourceID);
+            return this;
+        }
+
+        public Builder setObjectConst(Type type) {
+            mShader.mPerObjConstants = type;
+            return this;
+        }
+
+        public Builder setShaderConst(Type type) {
+            mShader.mPerShaderConstants = type;
+            return this;
+        }
+
+        public Builder addShaderTexture(Program.TextureType texType, String name) {
+            mShader.mShaderTextureNames.add(name);
+            mShader.mShaderTextureTypes.add(texType);
+            return this;
+        }
+
+        public Builder addTexture(Program.TextureType texType, String name) {
+            mShader.mTextureNames.add(name);
+            mShader.mTextureTypes.add(texType);
+            return this;
+        }
+
+        public FragmentShader create() {
+            if (mShader.mPerShaderConstants != null) {
+                mBuilder.addConstant(mShader.mPerShaderConstants);
+            }
+            if (mShader.mPerObjConstants != null) {
+                mBuilder.addConstant(mShader.mPerObjConstants);
+            }
+            for (int i = 0; i < mShader.mTextureTypes.size(); i ++) {
+                mBuilder.addTexture(mShader.mTextureTypes.get(i));
+            }
+            for (int i = 0; i < mShader.mShaderTextureTypes.size(); i ++) {
+                mBuilder.addTexture(mShader.mShaderTextureTypes.get(i));
+            }
+
+            mShader.mProgram = mBuilder.create();
+            return mShader;
+        }
+    }
+
+    public ProgramFragment getProgram() {
+        return mProgram;
+    }
+
+    ScriptField_ShaderParam_s getTextureParams() {
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        ArrayList<ScriptField_ShaderParam_s.Item> paramList;
+        paramList = new ArrayList<ScriptField_ShaderParam_s.Item>();
+
+        int shaderTextureStart = mTextureTypes.size();
+        for (int i = 0; i < mShaderTextureNames.size(); i ++) {
+            ShaderParam sp = mSourceParams.get(mShaderTextureNames.get(i));
+            if (sp != null && sp instanceof TextureParam) {
+                TextureParam p = (TextureParam)sp;
+                ScriptField_ShaderParam_s.Item paramRS = new ScriptField_ShaderParam_s.Item();
+                paramRS.bufferOffset = shaderTextureStart + i;
+                paramRS.transformTimestamp = 0;
+                paramRS.dataTimestamp = 0;
+                paramRS.data = p.getRSData().getAllocation();
+                paramList.add(paramRS);
+            }
+        }
+
+        ScriptField_ShaderParam_s rsParams = null;
+        int paramCount = paramList.size();
+        if (paramCount != 0) {
+            rsParams = new ScriptField_ShaderParam_s(rs, paramCount);
+            for (int i = 0; i < paramCount; i++) {
+                rsParams.set(paramList.get(i), i, false);
+            }
+            rsParams.copyAll();
+        }
+        return rsParams;
+    }
+
+    ScriptField_FragmentShader_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        ScriptField_FragmentShader_s.Item item = new ScriptField_FragmentShader_s.Item();
+        item.program = mProgram;
+
+        ScriptField_ShaderParam_s texParams = getTextureParams();
+        if (texParams != null) {
+            item.shaderTextureParams = texParams.getAllocation();
+        }
+
+        linkConstants(rs);
+        if (mPerShaderConstants != null) {
+            item.shaderConst = mConstantBuffer;
+            item.shaderConstParams = mConstantBufferParams.getAllocation();
+            mProgram.bindConstants(item.shaderConst, 0);
+        }
+
+        item.objectConstIndex = -1;
+        if (mPerObjConstants != null) {
+            item.objectConstIndex = mPerShaderConstants != null ? 1 : 0;
+        }
+
+        mField = new ScriptField_FragmentShader_s(rs, 1);
+        mField.set(item, 0, true);
+        return mField;
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java
new file mode 100644
index 0000000..8f5e2e7
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class LightBase extends SceneGraphBase {
+    static final int RS_LIGHT_POINT = 0;
+    static final int RS_LIGHT_DIRECTIONAL = 1;
+
+    ScriptField_Light_s mField;
+    ScriptField_Light_s.Item mFieldData;
+    Transform mTransform;
+    Float4 mColor;
+    float mIntensity;
+    public LightBase() {
+        mColor = new Float4(0.0f, 0.0f, 0.0f, 0.0f);
+        mIntensity = 1.0f;
+    }
+
+    public void setTransform(Transform t) {
+        mTransform = t;
+        updateRSData();
+    }
+
+    public void setColor(float r, float g, float b) {
+        mColor.x = r;
+        mColor.y = g;
+        mColor.z = b;
+        updateRSData();
+    }
+
+    public void setColor(Float3 c) {
+        setColor(c.x, c.y, c.z);
+    }
+
+    public void setIntensity(float i) {
+        mIntensity = i;
+        updateRSData();
+    }
+
+    public void setName(String n) {
+        super.setName(n);
+        updateRSData();
+    }
+
+    protected void updateRSData() {
+        if (mField == null) {
+            return;
+        }
+        RenderScriptGL rs = SceneManager.getRS();
+        mFieldData.transformMatrix = mTransform.getRSData().getAllocation();
+        mFieldData.name = getNameAlloc(rs);
+        mFieldData.color = mColor;
+        mFieldData.intensity = mIntensity;
+
+        initLocalData();
+
+        mField.set(mFieldData, 0, true);
+    }
+
+    abstract void initLocalData();
+
+    ScriptField_Light_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+        if (mField == null) {
+            mField = new ScriptField_Light_s(rs, 1);
+            mFieldData = new ScriptField_Light_s.Item();
+        }
+
+        updateRSData();
+
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java
new file mode 100644
index 0000000..6d70bc9
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Matrix4f;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class MatrixTransform extends Transform {
+
+    Matrix4f mLocalMatrix;
+    public MatrixTransform() {
+        mLocalMatrix = new Matrix4f();
+    }
+
+    public void setMatrix(Matrix4f matrix) {
+        mLocalMatrix = matrix;
+        updateRSData();
+    }
+
+    public Matrix4f getMatrix() {
+        return new Matrix4f(mLocalMatrix.getArray());
+    }
+
+    void initLocalData() {
+        mTransformData.localMat = mLocalMatrix;
+    }
+
+    void updateRSData() {
+        if (mField == null) {
+            return;
+        }
+        mField.set_localMat(0, mLocalMatrix, false);
+        mField.set_isDirty(0, 1, true);
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java
new file mode 100644
index 0000000..574bafc
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class PointLight extends LightBase {
+    public PointLight() {
+    }
+
+     void initLocalData() {
+        mFieldData.type = RS_LIGHT_POINT;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java
new file mode 100644
index 0000000..02fd69d
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.util.Log;
+
+import android.renderscript.*;
+import android.content.res.Resources;
+
+/**
+ * @hide
+ */
+public class RenderPass extends SceneGraphBase {
+
+    TextureRenderTarget mColorTarget;
+    Float4 mClearColor;
+    boolean mShouldClearColor;
+
+    TextureRenderTarget mDepthTarget;
+    float mClearDepth;
+    boolean mShouldClearDepth;
+
+    ArrayList<RenderableBase> mObjectsToDraw;
+
+    Camera mCamera;
+
+    ScriptField_RenderPass_s.Item mRsField;
+
+    public RenderPass() {
+        mObjectsToDraw = new ArrayList<RenderableBase>();
+        mClearColor = new Float4(0.0f, 0.0f, 0.0f, 0.0f);
+        mShouldClearColor = true;
+        mClearDepth = 1.0f;
+        mShouldClearDepth = true;
+    }
+
+    public void appendRenderable(Renderable d) {
+        mObjectsToDraw.add(d);
+    }
+
+    public void setCamera(Camera c) {
+        mCamera = c;
+    }
+
+    public void setColorTarget(TextureRenderTarget colorTarget) {
+        mColorTarget = colorTarget;
+    }
+    public void setClearColor(Float4 clearColor) {
+        mClearColor = clearColor;
+    }
+    public void setShouldClearColor(boolean shouldClearColor) {
+        mShouldClearColor = shouldClearColor;
+    }
+
+    public void setDepthTarget(TextureRenderTarget depthTarget) {
+        mDepthTarget = depthTarget;
+    }
+    public void setClearDepth(float clearDepth) {
+        mClearDepth = clearDepth;
+    }
+    public void setShouldClearDepth(boolean shouldClearDepth) {
+        mShouldClearDepth = shouldClearDepth;
+    }
+
+    public ArrayList<RenderableBase> getRenderables() {
+        return mObjectsToDraw;
+    }
+
+    ScriptField_RenderPass_s.Item getRsField(RenderScriptGL rs, Resources res) {
+        if (mRsField != null) {
+            return mRsField;
+        }
+
+        mRsField = new ScriptField_RenderPass_s.Item();
+        if (mColorTarget != null) {
+            mRsField.color_target = mColorTarget.getRsData(true).get_texture(0);
+        }
+        if (mColorTarget != null) {
+            mRsField.depth_target = mDepthTarget.getRsData(true).get_texture(0);
+        }
+        mRsField.camera = mCamera != null ? mCamera.getRSData().getAllocation() : null;
+
+        if (mObjectsToDraw.size() != 0) {
+            Allocation drawableData = Allocation.createSized(rs,
+                                                              Element.ALLOCATION(rs),
+                                                              mObjectsToDraw.size());
+            Allocation[] drawableAllocs = new Allocation[mObjectsToDraw.size()];
+            for (int i = 0; i < mObjectsToDraw.size(); i ++) {
+                Renderable dI = (Renderable)mObjectsToDraw.get(i);
+                drawableAllocs[i] = dI.getRsField(rs, res).getAllocation();
+            }
+            drawableData.copyFrom(drawableAllocs);
+            mRsField.objects = drawableData;
+        }
+
+        mRsField.clear_color = mClearColor;
+        mRsField.clear_depth = mClearDepth;
+        mRsField.should_clear_color = mShouldClearColor;
+        mRsField.should_clear_depth = mShouldClearDepth;
+        return mRsField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java
new file mode 100644
index 0000000..c08a722
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import android.content.res.Resources;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramRaster;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RenderState extends SceneGraphBase {
+    VertexShader mVertex;
+    FragmentShader mFragment;
+    ProgramStore mStore;
+    ProgramRaster mRaster;
+
+    ScriptField_RenderState_s mField;
+
+    public RenderState(VertexShader pv,
+                       FragmentShader pf,
+                       ProgramStore ps,
+                       ProgramRaster pr) {
+        mVertex = pv;
+        mFragment = pf;
+        mStore = ps;
+        mRaster = pr;
+    }
+
+    public RenderState(RenderState r) {
+        mVertex = r.mVertex;
+        mFragment = r.mFragment;
+        mStore = r.mStore;
+        mRaster = r.mRaster;
+    }
+
+    public void setProgramVertex(VertexShader pv) {
+        mVertex = pv;
+        updateRSData();
+    }
+
+    public void setProgramFragment(FragmentShader pf) {
+        mFragment = pf;
+        updateRSData();
+    }
+
+    public void setProgramStore(ProgramStore ps) {
+        mStore = ps;
+        updateRSData();
+    }
+
+    public void setProgramRaster(ProgramRaster pr) {
+        mRaster = pr;
+        updateRSData();
+    }
+
+    void updateRSData() {
+        if (mField == null) {
+            return;
+        }
+        ScriptField_RenderState_s.Item item = new ScriptField_RenderState_s.Item();
+        item.pv = mVertex.getRSData().getAllocation();
+        item.pf = mFragment.getRSData().getAllocation();
+        item.ps = mStore;
+        item.pr = mRaster;
+
+        mField.set(item, 0, true);
+    }
+
+    public ScriptField_RenderState_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+
+        mField = new ScriptField_RenderState_s(rs, 1);
+        updateRSData();
+
+        return mField;
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
new file mode 100644
index 0000000..9f7ab41
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import com.android.scenegraph.Float4Param;
+import com.android.scenegraph.SceneManager;
+import com.android.scenegraph.ShaderParam;
+import com.android.scenegraph.TransformParam;
+
+import android.content.res.Resources;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Element.DataType;
+import android.renderscript.Matrix4f;
+import android.renderscript.Mesh;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Renderable extends RenderableBase {
+    HashMap<String, ShaderParam> mSourceParams;
+
+    RenderState mRenderState;
+    Transform mTransform;
+
+    String mMeshName;
+    String mMeshIndexName;
+
+    public String mMaterialName;
+
+    ScriptField_Renderable_s mField;
+    ScriptField_Renderable_s.Item mData;
+
+    public Renderable() {
+        mSourceParams = new HashMap<String, ShaderParam>();
+        mData = new ScriptField_Renderable_s.Item();
+    }
+
+    public void setCullType(int cull) {
+        mData.cullType = cull;
+    }
+
+    public void setRenderState(RenderState renderState) {
+        mRenderState = renderState;
+        if (mField != null) {
+            RenderScriptGL rs = SceneManager.getRS();
+            updateFieldItem(rs);
+            mField.set(mData, 0, true);
+        }
+    }
+
+    public void setMesh(Mesh mesh) {
+        mData.mesh = mesh;
+        if (mField != null) {
+            mField.set_mesh(0, mData.mesh, true);
+        }
+    }
+
+    public void setMesh(String mesh, String indexName) {
+        mMeshName = mesh;
+        mMeshIndexName = indexName;
+    }
+
+    public void setMaterialName(String name) {
+        mMaterialName = name;
+    }
+
+    public void setTransform(Transform t) {
+        mTransform = t;
+        if (mField != null) {
+            RenderScriptGL rs = SceneManager.getRS();
+            updateFieldItem(rs);
+            mField.set(mData, 0, true);
+        }
+    }
+
+    public void appendSourceParams(ShaderParam p) {
+        mSourceParams.put(p.getParamName(), p);
+        // Possibly lift this restriction later
+        if (mField != null) {
+            throw new RuntimeException("Can't add source params to objects that are rendering");
+        }
+    }
+
+    public void resolveMeshData(Mesh mesh) {
+        mData.mesh = mesh;
+        if (mData.mesh == null) {
+            Log.v("DRAWABLE: ", "*** NO MESH *** " + mMeshName);
+            return;
+        }
+        int subIndexCount = mData.mesh.getPrimitiveCount();
+        if (subIndexCount == 1 || mMeshIndexName == null) {
+            mData.meshIndex = 0;
+        } else {
+            for (int i = 0; i < subIndexCount; i ++) {
+                if (mData.mesh.getIndexSetAllocation(i).getName().equals(mMeshIndexName)) {
+                    mData.meshIndex = i;
+                    break;
+                }
+            }
+        }
+        if (mField != null) {
+            mField.set(mData, 0, true);
+        }
+    }
+
+    void updateTextures(RenderScriptGL rs) {
+        Iterator<ShaderParam> allParamsIter = mSourceParams.values().iterator();
+        int paramIndex = 0;
+        while (allParamsIter.hasNext()) {
+            ShaderParam sp = allParamsIter.next();
+            if (sp instanceof TextureParam) {
+                TextureParam p = (TextureParam)sp;
+                TextureBase tex = p.getTexture();
+                if (tex != null) {
+                    mData.pf_textures[paramIndex++] = tex.getRsData(false).getAllocation();
+                }
+            }
+        }
+        ProgramFragment pf = mRenderState.mFragment.mProgram;
+        mData.pf_num_textures = pf != null ? Math.min(pf.getTextureCount(), paramIndex) : 0;
+        if (mField != null) {
+            mField.set_pf_textures(0, mData.pf_textures, true);
+            mField.set_pf_num_textures(0, mData.pf_num_textures, true);
+        }
+    }
+
+    public void setVisible(boolean vis) {
+        mData.cullType = vis ? 0 : 2;
+        if (mField != null) {
+            mField.set_cullType(0, mData.cullType, true);
+        }
+    }
+
+    ScriptField_Renderable_s getRsField(RenderScriptGL rs, Resources res) {
+        if (mField != null) {
+            return mField;
+        }
+        updateFieldItem(rs);
+        updateTextures(rs);
+
+        mField = new ScriptField_Renderable_s(rs, 1);
+        mField.set(mData, 0, true);
+
+        return mField;
+    }
+
+    void updateVertexConstants(RenderScriptGL rs) {
+        Allocation pvParams = null, vertexConstants = null;
+        VertexShader pv = mRenderState.mVertex;
+        if (pv != null && pv.getObjectConstants() != null) {
+            vertexConstants = Allocation.createTyped(rs, pv.getObjectConstants());
+            Element vertexConst = vertexConstants.getType().getElement();
+            pvParams = ShaderParam.fillInParams(vertexConst, mSourceParams,
+                                                mTransform).getAllocation();
+        }
+        mData.pv_const = vertexConstants;
+        mData.pv_constParams = pvParams;
+    }
+
+    void updateFragmentConstants(RenderScriptGL rs) {
+        Allocation pfParams = null, fragmentConstants = null;
+        FragmentShader pf = mRenderState.mFragment;
+        if (pf != null && pf.getObjectConstants() != null) {
+            fragmentConstants = Allocation.createTyped(rs, pf.getObjectConstants());
+            Element fragmentConst = fragmentConstants.getType().getElement();
+            pfParams = ShaderParam.fillInParams(fragmentConst, mSourceParams,
+                                                mTransform).getAllocation();
+        }
+        mData.pf_const = fragmentConstants;
+        mData.pf_constParams = pfParams;
+    }
+
+    void updateFieldItem(RenderScriptGL rs) {
+        updateVertexConstants(rs);
+        updateFragmentConstants(rs);
+
+        if (mTransform != null) {
+            mData.transformMatrix = mTransform.getRSData().getAllocation();
+        }
+        mData.name = getNameAlloc(rs);
+        mData.render_state = mRenderState.getRSData().getAllocation();
+        mData.bVolInitialized = 0;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java
new file mode 100644
index 0000000..74535dd
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RenderableBase extends SceneGraphBase {
+    public RenderableBase() {
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java
new file mode 100644
index 0000000..590bbab
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RenderableGroup extends RenderableBase {
+
+    ArrayList<RenderableBase> mChildren;
+
+    public RenderableGroup() {
+        mChildren = new ArrayList<RenderableBase>();
+    }
+
+    public void appendChildren(RenderableBase d) {
+        mChildren.add(d);
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
new file mode 100644
index 0000000..8c09860
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.android.scenegraph.SceneManager;
+import com.android.scenegraph.TextureBase;
+
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Mesh;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Scene extends SceneGraphBase {
+    private static String TIMER_TAG = "TIMER";
+
+    CompoundTransform mRootTransforms;
+    HashMap<String, Transform> mTransformMap;
+    ArrayList<RenderPass> mRenderPasses;
+    ArrayList<LightBase> mLights;
+    ArrayList<Camera> mCameras;
+    ArrayList<FragmentShader> mFragmentShaders;
+    ArrayList<VertexShader> mVertexShaders;
+    ArrayList<RenderableBase> mRenderables;
+    HashMap<String, RenderableBase> mRenderableMap;
+    ArrayList<Texture2D> mTextures;
+
+    HashMap<String, ArrayList<Renderable> > mRenderableMeshMap;
+
+    // RS Specific stuff
+    ScriptField_SgTransform mTransformRSData;
+
+    RenderScriptGL mRS;
+    Resources mRes;
+
+    ScriptField_RenderPass_s mRenderPassAlloc;
+
+    public Scene() {
+        mRenderPasses = new ArrayList<RenderPass>();
+        mLights = new ArrayList<LightBase>();
+        mCameras = new ArrayList<Camera>();
+        mFragmentShaders = new ArrayList<FragmentShader>();
+        mVertexShaders = new ArrayList<VertexShader>();
+        mRenderables = new ArrayList<RenderableBase>();
+        mRenderableMap = new HashMap<String, RenderableBase>();
+        mRenderableMeshMap = new HashMap<String, ArrayList<Renderable> >();
+        mTextures = new ArrayList<Texture2D>();
+        mRootTransforms = new CompoundTransform();
+        mRootTransforms.setName("_scene_root_");
+        mTransformMap = new HashMap<String, Transform>();
+    }
+
+    public void appendTransform(Transform t) {
+        mRootTransforms.appendChild(t);
+    }
+
+    // temporary
+    public void addToTransformMap(Transform t) {
+        mTransformMap.put(t.getName(), t);
+    }
+
+    public Transform getTransformByName(String name) {
+        return mTransformMap.get(name);
+    }
+
+    public void appendRenderPass(RenderPass p) {
+        mRenderPasses.add(p);
+    }
+
+    public void clearRenderPasses() {
+        mRenderPasses.clear();
+    }
+
+    public void appendLight(LightBase l) {
+        mLights.add(l);
+    }
+
+    public void appendCamera(Camera c) {
+        mCameras.add(c);
+    }
+
+    public void appendShader(FragmentShader f) {
+        mFragmentShaders.add(f);
+    }
+
+    public void appendShader(VertexShader v) {
+        mVertexShaders.add(v);
+    }
+
+    public ArrayList<Camera> getCameras() {
+        return mCameras;
+    }
+
+    public ArrayList<LightBase> getLights() {
+        return mLights;
+    }
+
+    public void appendRenderable(RenderableBase d) {
+        mRenderables.add(d);
+        mRenderableMap.put(d.getName(), d);
+    }
+
+    public ArrayList<RenderableBase> getRenderables() {
+        return mRenderables;
+    }
+
+    public RenderableBase getRenderableByName(String name) {
+        return mRenderableMap.get(name);
+    }
+
+    public void appendTextures(Texture2D tex) {
+        mTextures.add(tex);
+    }
+
+    public void assignRenderStateToMaterial(RenderState renderState, String regex) {
+        Pattern pattern = Pattern.compile(regex);
+        int numRenderables = mRenderables.size();
+        for (int i = 0; i < numRenderables; i ++) {
+            Renderable shape = (Renderable)mRenderables.get(i);
+            Matcher m = pattern.matcher(shape.mMaterialName);
+            if (m.find()) {
+                shape.setRenderState(renderState);
+            }
+        }
+    }
+
+    public void assignRenderState(RenderState renderState) {
+        int numRenderables = mRenderables.size();
+        for (int i = 0; i < numRenderables; i ++) {
+            Renderable shape = (Renderable)mRenderables.get(i);
+            shape.setRenderState(renderState);
+        }
+    }
+
+    public void meshLoaded(Mesh m) {
+        ArrayList<Renderable> entries = mRenderableMeshMap.get(m.getName());
+        int numEntries = entries.size();
+        for (int i = 0; i < numEntries; i++) {
+            Renderable d = entries.get(i);
+            d.resolveMeshData(m);
+        }
+    }
+
+    void addToMeshMap(Renderable d) {
+        ArrayList<Renderable> entries = mRenderableMeshMap.get(d.mMeshName);
+        if (entries == null) {
+            entries = new ArrayList<Renderable>();
+            mRenderableMeshMap.put(d.mMeshName, entries);
+        }
+        entries.add(d);
+    }
+
+    public void destroyRS() {
+        SceneManager sceneManager = SceneManager.getInstance();
+        mTransformRSData = null;
+        sceneManager.mRenderLoop.bind_gRootNode(mTransformRSData);
+        sceneManager.mRenderLoop.set_gRenderableObjects(null);
+        mRenderPassAlloc = null;
+        sceneManager.mRenderLoop.set_gRenderPasses(null);
+        sceneManager.mRenderLoop.bind_gFrontToBack(null);
+        sceneManager.mRenderLoop.bind_gBackToFront(null);
+        sceneManager.mRenderLoop.set_gCameras(null);
+
+        mTransformMap = null;
+        mRenderPasses = null;
+        mLights = null;
+        mCameras = null;
+        mRenderables = null;
+        mRenderableMap = null;
+        mTextures = null;
+        mRenderableMeshMap = null;
+        mRootTransforms = null;
+    }
+
+    public void initRenderPassRS(RenderScriptGL rs, SceneManager sceneManager) {
+        if (mRenderPasses.size() != 0) {
+            mRenderPassAlloc = new ScriptField_RenderPass_s(mRS, mRenderPasses.size());
+            for (int i = 0; i < mRenderPasses.size(); i ++) {
+                mRenderPassAlloc.set(mRenderPasses.get(i).getRsField(mRS, mRes), i, false);
+            }
+            mRenderPassAlloc.copyAll();
+            sceneManager.mRenderLoop.set_gRenderPasses(mRenderPassAlloc.getAllocation());
+        }
+    }
+
+    private void addDrawables(RenderScriptGL rs, Resources res, SceneManager sceneManager) {
+        Allocation drawableData = Allocation.createSized(rs,
+                                                         Element.ALLOCATION(rs),
+                                                         mRenderables.size());
+        Allocation[] drawableAllocs = new Allocation[mRenderables.size()];
+        for (int i = 0; i < mRenderables.size(); i ++) {
+            Renderable dI = (Renderable)mRenderables.get(i);
+            addToMeshMap(dI);
+            drawableAllocs[i] = dI.getRsField(rs, res).getAllocation();
+        }
+        drawableData.copyFrom(drawableAllocs);
+        sceneManager.mRenderLoop.set_gRenderableObjects(drawableData);
+
+        initRenderPassRS(rs, sceneManager);
+    }
+
+    private void addShaders(RenderScriptGL rs, Resources res, SceneManager sceneManager) {
+        Allocation shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs),
+                                                       mVertexShaders.size());
+        Allocation[] shaderAllocs = new Allocation[mVertexShaders.size()];
+        for (int i = 0; i < mVertexShaders.size(); i ++) {
+            VertexShader sI = mVertexShaders.get(i);
+            shaderAllocs[i] = sI.getRSData().getAllocation();
+        }
+        shaderData.copyFrom(shaderAllocs);
+        sceneManager.mRenderLoop.set_gVertexShaders(shaderData);
+
+        shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs), mFragmentShaders.size());
+        shaderAllocs = new Allocation[mFragmentShaders.size()];
+        for (int i = 0; i < mFragmentShaders.size(); i ++) {
+            FragmentShader sI = mFragmentShaders.get(i);
+            shaderAllocs[i] = sI.getRSData().getAllocation();
+        }
+        shaderData.copyFrom(shaderAllocs);
+        sceneManager.mRenderLoop.set_gFragmentShaders(shaderData);
+    }
+
+    public void initRS() {
+        SceneManager sceneManager = SceneManager.getInstance();
+        mRS = SceneManager.getRS();
+        mRes = SceneManager.getRes();
+        long start = System.currentTimeMillis();
+        mTransformRSData = mRootTransforms.getRSData();
+        long end = System.currentTimeMillis();
+        Log.v(TIMER_TAG, "Transform init time: " + (end - start));
+
+        start = System.currentTimeMillis();
+
+        sceneManager.mRenderLoop.bind_gRootNode(mTransformRSData);
+        end = System.currentTimeMillis();
+        Log.v(TIMER_TAG, "Script init time: " + (end - start));
+
+        start = System.currentTimeMillis();
+        addDrawables(mRS, mRes, sceneManager);
+        end = System.currentTimeMillis();
+        Log.v(TIMER_TAG, "Renderable init time: " + (end - start));
+
+        addShaders(mRS, mRes, sceneManager);
+
+        Allocation opaqueBuffer = null;
+        if (mRenderables.size() > 0) {
+            opaqueBuffer = Allocation.createSized(mRS, Element.U32(mRS), mRenderables.size());
+        }
+        Allocation transparentBuffer = null;
+        if (mRenderables.size() > 0) {
+            transparentBuffer = Allocation.createSized(mRS, Element.U32(mRS), mRenderables.size());
+        }
+
+        sceneManager.mRenderLoop.bind_gFrontToBack(opaqueBuffer);
+        sceneManager.mRenderLoop.bind_gBackToFront(transparentBuffer);
+
+        if (mCameras.size() > 0) {
+            Allocation cameraData;
+            cameraData = Allocation.createSized(mRS, Element.ALLOCATION(mRS), mCameras.size());
+            Allocation[] cameraAllocs = new Allocation[mCameras.size()];
+            for (int i = 0; i < mCameras.size(); i ++) {
+                cameraAllocs[i] = mCameras.get(i).getRSData().getAllocation();
+            }
+            cameraData.copyFrom(cameraAllocs);
+            sceneManager.mRenderLoop.set_gCameras(cameraData);
+        }
+
+        if (mLights.size() > 0) {
+            Allocation lightData = Allocation.createSized(mRS,
+                                                          Element.ALLOCATION(mRS),
+                                                          mLights.size());
+            Allocation[] lightAllocs = new Allocation[mLights.size()];
+            for (int i = 0; i < mLights.size(); i ++) {
+                lightAllocs[i] = mLights.get(i).getRSData().getAllocation();
+            }
+            lightData.copyFrom(lightAllocs);
+            sceneManager.mRenderLoop.set_gLights(lightData);
+        }
+    }
+}
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java
new file mode 100644
index 0000000..412ffbf
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.SceneManager;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class SceneGraphBase {
+    String mName;
+    Allocation mNameAlloc;
+    public void setName(String n) {
+        mName = n;
+        mNameAlloc = null;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    Allocation getNameAlloc(RenderScriptGL rs) {
+        if (mNameAlloc == null)  {
+            mNameAlloc = SceneManager.getStringAsAllocation(rs, getName());
+        }
+        return mNameAlloc;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
new file mode 100644
index 0000000..f77f483
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.android.scenegraph.Camera;
+import com.android.scenegraph.MatrixTransform;
+import com.android.scenegraph.Scene;
+import com.android.testapp.R;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Allocation.MipmapControl;
+import android.renderscript.Mesh;
+import android.renderscript.RenderScriptGL;
+import android.renderscript.Type.Builder;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+/**
+ * @hide
+ */
+public class SceneManager extends SceneGraphBase {
+
+    HashMap<String, Allocation> mAllocationMap;
+
+    ScriptC_render mRenderLoop;
+    ScriptC mCameraScript;
+    ScriptC mLightScript;
+    ScriptC mObjectParamsScript;
+    ScriptC mFragmentParamsScript;
+    ScriptC mVertexParamsScript;
+    ScriptC mCullScript;
+    ScriptC_transform mTransformScript;
+    ScriptC_export mExportScript;
+
+    RenderScriptGL mRS;
+    Resources mRes;
+    Mesh mQuad;
+    int mWidth;
+    int mHeight;
+
+    Scene mActiveScene;
+    private static SceneManager sSceneManager;
+
+    private Allocation sDefault2D;
+    private Allocation sDefaultCube;
+
+    private static Allocation getDefault(boolean isCube) {
+        final int dimension = 4;
+        final int bytesPerPixel = 4;
+        int arraySize = dimension * dimension * bytesPerPixel;
+
+        RenderScriptGL rs = sSceneManager.mRS;
+        Type.Builder b = new Type.Builder(rs, Element.RGBA_8888(rs));
+        b.setX(dimension).setY(dimension);
+        if (isCube) {
+            b.setFaces(true);
+            arraySize *= 6;
+        }
+        Type bitmapType = b.create();
+
+        Allocation.MipmapControl mip = Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
+        int usage =  Allocation.USAGE_GRAPHICS_TEXTURE;
+        Allocation defaultImage = Allocation.createTyped(rs, bitmapType, mip, usage);
+
+        byte imageData[] = new byte[arraySize];
+        defaultImage.copyFrom(imageData);
+        return defaultImage;
+    }
+
+    static Allocation getDefaultTex2D() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        if (sSceneManager.sDefault2D == null) {
+            sSceneManager.sDefault2D = getDefault(false);
+        }
+        return sSceneManager.sDefault2D;
+    }
+
+    static Allocation getDefaultTexCube() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        if (sSceneManager.sDefaultCube == null) {
+            sSceneManager.sDefaultCube = getDefault(true);
+        }
+        return sSceneManager.sDefaultCube;
+    }
+
+    public static boolean isSDCardPath(String path) {
+        int sdCardIndex = path.indexOf("sdcard/");
+        // We are looking for /sdcard/ or sdcard/
+        if (sdCardIndex == 0 || sdCardIndex == 1) {
+            return true;
+        }
+        sdCardIndex = path.indexOf("mnt/sdcard/");
+        if (sdCardIndex == 0 || sdCardIndex == 1) {
+            return true;
+        }
+        return false;
+    }
+
+    static Bitmap loadBitmap(String name, Resources res) {
+        InputStream is = null;
+        boolean loadFromSD = isSDCardPath(name);
+        try {
+            if (!loadFromSD) {
+                is = res.getAssets().open(name);
+            } else {
+                File f = new File(name);
+                is = new BufferedInputStream(new FileInputStream(f));
+            }
+        } catch (IOException e) {
+            Log.e("ImageLoaderTask", " Message: " + e.getMessage());
+            return null;
+        }
+
+        Bitmap b = BitmapFactory.decodeStream(is);
+        try {
+            is.close();
+        } catch (IOException e) {
+            Log.e("ImageLoaderTask", " Message: " + e.getMessage());
+        }
+        return b;
+    }
+
+    public static Allocation loadCubemap(String name, RenderScriptGL rs, Resources res) {
+        Bitmap b = loadBitmap(name, res);
+        if (b == null) {
+            return null;
+        }
+        return Allocation.createCubemapFromBitmap(rs, b,
+                                                  MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                                  Allocation.USAGE_GRAPHICS_TEXTURE);
+    }
+
+    public static Allocation loadTexture2D(String name, RenderScriptGL rs, Resources res) {
+        Bitmap b = loadBitmap(name, res);
+        if (b == null) {
+            return null;
+        }
+        return Allocation.createFromBitmap(rs, b,
+                                           Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                           Allocation.USAGE_GRAPHICS_TEXTURE);
+    }
+
+    public static ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) {
+        ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        builder.setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ONE);
+        builder.setDitherEnabled(false);
+        builder.setDepthMaskEnabled(false);
+        return builder.create();
+    }
+
+    static Allocation getStringAsAllocation(RenderScript rs, String str) {
+        if (str == null) {
+            return null;
+        }
+        if (str.length() == 0) {
+            return null;
+        }
+        byte[] allocArray = null;
+        byte[] nullChar = new byte[1];
+        nullChar[0] = 0;
+        try {
+            allocArray = str.getBytes("UTF-8");
+            Allocation alloc = Allocation.createSized(rs, Element.U8(rs),
+                                                      allocArray.length + 1,
+                                                      Allocation.USAGE_SCRIPT);
+            alloc.copy1DRangeFrom(0, allocArray.length, allocArray);
+            alloc.copy1DRangeFrom(allocArray.length, 1, nullChar);
+            return alloc;
+        }
+        catch (Exception e) {
+            throw new RSRuntimeException("Could not convert string to utf-8.");
+        }
+    }
+
+    static Allocation getCachedAlloc(String str) {
+        if (sSceneManager == null) {
+            throw new RuntimeException("Scene manager not initialized");
+        }
+        return sSceneManager.mAllocationMap.get(str);
+    }
+
+    static void cacheAlloc(String str, Allocation alloc) {
+        if (sSceneManager == null) {
+            throw new RuntimeException("Scene manager not initialized");
+        }
+        sSceneManager.mAllocationMap.put(str, alloc);
+    }
+
+    public static class SceneLoadedCallback implements Runnable {
+        public Scene mLoadedScene;
+        public String mName;
+        public void run() {
+        }
+    }
+
+    public Scene getActiveScene() {
+        return mActiveScene;
+    }
+
+    public void setActiveScene(Scene s) {
+        mActiveScene = s;
+
+        // Do some sanity checking
+        if (mActiveScene.getCameras().size() == 0) {
+            Matrix4f camPos = new Matrix4f();
+            camPos.translate(0, 0, 10);
+            MatrixTransform cameraTransform = new MatrixTransform();
+            cameraTransform.setName("_DefaultCameraTransform");
+            cameraTransform.setMatrix(camPos);
+            mActiveScene.appendTransform(cameraTransform);
+            Camera cam = new Camera();
+            cam.setName("_DefaultCamera");
+            cam.setTransform(cameraTransform);
+            mActiveScene.appendCamera(cam);
+        }
+    }
+
+    static RenderScriptGL getRS() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        return sSceneManager.mRS;
+    }
+
+    static Resources getRes() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        return sSceneManager.mRes;
+    }
+
+    public static SceneManager getInstance() {
+        if (sSceneManager == null) {
+            sSceneManager = new SceneManager();
+        }
+        return sSceneManager;
+    }
+
+    protected SceneManager() {
+    }
+
+    public void loadModel(String name, SceneLoadedCallback cb) {
+        ColladaScene scene = new ColladaScene(name, cb);
+        scene.init(mRS, mRes);
+    }
+
+    public Mesh getScreenAlignedQuad() {
+        if (mQuad != null) {
+            return mQuad;
+        }
+
+        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
+                                           3, Mesh.TriangleMeshBuilder.TEXTURE_0);
+
+        tmb.setTexture(0.0f, 1.0f);
+        tmb.addVertex(-1.0f, 1.0f, 1.0f);
+
+        tmb.setTexture(0.0f, 0.0f);
+        tmb.addVertex(-1.0f, -1.0f, 1.0f);
+
+        tmb.setTexture(1.0f, 0.0f);
+        tmb.addVertex(1.0f, -1.0f, 1.0f);
+
+        tmb.setTexture(1.0f, 1.0f);
+        tmb.addVertex(1.0f, 1.0f, 1.0f);
+
+        tmb.addTriangle(0, 1, 2);
+        tmb.addTriangle(2, 3, 0);
+
+        mQuad = tmb.create(true);
+        return mQuad;
+    }
+
+    public Renderable getRenderableQuad(String name, RenderState state) {
+        Renderable quad = new Renderable();
+        quad.setTransform(new MatrixTransform());
+        quad.setMesh(getScreenAlignedQuad());
+        quad.setName(name);
+        quad.setRenderState(state);
+        quad.setCullType(1);
+        return quad;
+    }
+
+    public void initRS(RenderScriptGL rs, Resources res, int w, int h) {
+        mRS = rs;
+        mRes = res;
+        mAllocationMap = new HashMap<String, Allocation>();
+
+        mExportScript = new ScriptC_export(rs, res, R.raw.export);
+
+        mTransformScript = new ScriptC_transform(rs, res, R.raw.transform);
+        mTransformScript.set_gTransformScript(mTransformScript);
+
+        mCameraScript = new ScriptC_camera(rs, res, R.raw.camera);
+        mLightScript = new ScriptC_light(rs, res, R.raw.light);
+        mObjectParamsScript = new ScriptC_object_params(rs, res, R.raw.object_params);
+        mFragmentParamsScript = new ScriptC_object_params(rs, res, R.raw.fragment_params);
+        mVertexParamsScript = new ScriptC_object_params(rs, res, R.raw.vertex_params);
+        mCullScript = new ScriptC_cull(rs, res, R.raw.cull);
+
+        mRenderLoop = new ScriptC_render(rs, res, R.raw.render);
+        mRenderLoop.set_gTransformScript(mTransformScript);
+        mRenderLoop.set_gCameraScript(mCameraScript);
+        mRenderLoop.set_gLightScript(mLightScript);
+        mRenderLoop.set_gObjectParamsScript(mObjectParamsScript);
+        mRenderLoop.set_gFragmentParamsScript(mFragmentParamsScript);
+        mRenderLoop.set_gVertexParamsScript(mVertexParamsScript);
+        mRenderLoop.set_gCullScript(mCullScript);
+
+        mRenderLoop.set_gPFSBackground(ProgramStore.BLEND_NONE_DEPTH_TEST(mRS));
+    }
+
+    public ScriptC getRenderLoop() {
+        return mRenderLoop;
+    }
+}
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java
new file mode 100644
index 0000000..4975114
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import com.android.scenegraph.SceneGraphBase;
+import com.android.scenegraph.ShaderParam;
+
+import android.renderscript.*;
+import android.renderscript.ProgramFragment.Builder;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class Shader extends SceneGraphBase {
+    protected Type mPerObjConstants;
+    protected Type mPerShaderConstants;
+
+    protected HashMap<String, ShaderParam> mSourceParams;
+    protected ArrayList<String> mShaderTextureNames;
+    protected ArrayList<Program.TextureType > mShaderTextureTypes;
+    protected ArrayList<String> mTextureNames;
+    protected ArrayList<Program.TextureType > mTextureTypes;
+
+    protected Allocation mConstantBuffer;
+    protected ScriptField_ShaderParam_s mConstantBufferParams;
+
+    public Shader() {
+        mSourceParams = new HashMap<String, ShaderParam>();
+        mShaderTextureNames = new ArrayList<String>();
+        mShaderTextureTypes = new ArrayList<Program.TextureType>();
+        mTextureNames = new ArrayList<String>();
+        mTextureTypes = new ArrayList<Program.TextureType>();
+    }
+
+    public void appendSourceParams(ShaderParam p) {
+        mSourceParams.put(p.getParamName(), p);
+    }
+
+    public Type getObjectConstants() {
+        return mPerObjConstants;
+    }
+
+    public Type getShaderConstants() {
+        return mPerObjConstants;
+    }
+
+    void linkConstants(RenderScriptGL rs) {
+        if (mPerShaderConstants == null) {
+            return;
+        }
+
+        Element constElem = mPerShaderConstants.getElement();
+        mConstantBufferParams  = ShaderParam.fillInParams(constElem, mSourceParams, null);
+
+        mConstantBuffer = Allocation.createTyped(rs, mPerShaderConstants);
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
new file mode 100644
index 0000000..8dea535
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import com.android.scenegraph.SceneManager;
+import com.android.scenegraph.Transform;
+
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class ShaderParam extends SceneGraphBase {
+
+    static final String cameraPos        = "cameraPos";
+    static final String cameraDir        = "cameraDir";
+
+    static final String lightColor       = "lightColor";
+    static final String lightPos         = "lightPos";
+    static final String lightDir         = "lightDir";
+
+    static final String view             = "view";
+    static final String proj             = "proj";
+    static final String viewProj         = "viewProj";
+    static final String model            = "model";
+    static final String modelView        = "modelView";
+    static final String modelViewProj    = "modelViewProj";
+
+    static final long sMaxTimeStamp = 0xffffffffL;
+
+    ScriptField_ShaderParamData_s.Item mData;
+    ScriptField_ShaderParamData_s mField;
+
+    String mParamName;
+    Camera mCamera;
+
+    static ScriptField_ShaderParam_s fillInParams(Element constantElem,
+                                                  HashMap<String, ShaderParam> sourceParams,
+                                                  Transform transform) {
+        RenderScriptGL rs = SceneManager.getRS();
+        ArrayList<ScriptField_ShaderParam_s.Item> paramList;
+        paramList = new ArrayList<ScriptField_ShaderParam_s.Item>();
+
+        int subElemCount = constantElem.getSubElementCount();
+        for (int i = 0; i < subElemCount; i ++) {
+            String inputName = constantElem.getSubElementName(i);
+            int offset = constantElem.getSubElementOffsetBytes(i);
+
+            ShaderParam matchingParam = sourceParams.get(inputName);
+            Element subElem = constantElem.getSubElement(i);
+            // Make one if it's not there
+            if (matchingParam == null) {
+                if (subElem.getDataType() == Element.DataType.FLOAT_32) {
+                    matchingParam = new Float4Param(inputName);
+                } else if (subElem.getDataType() == Element.DataType.MATRIX_4X4) {
+                    TransformParam trParam = new TransformParam(inputName);
+                    trParam.setTransform(transform);
+                    matchingParam = trParam;
+                }
+            }
+            ScriptField_ShaderParam_s.Item paramRS = new ScriptField_ShaderParam_s.Item();
+            paramRS.bufferOffset = offset;
+            paramRS.transformTimestamp = 0;
+            paramRS.dataTimestamp = 0;
+            paramRS.data = matchingParam.getRSData().getAllocation();
+            if (subElem.getDataType() == Element.DataType.FLOAT_32) {
+                paramRS.float_vecSize = subElem.getVectorSize();
+            }
+
+            paramList.add(paramRS);
+        }
+
+        ScriptField_ShaderParam_s rsParams = null;
+        int paramCount = paramList.size();
+        if (paramCount != 0) {
+            rsParams = new ScriptField_ShaderParam_s(rs, paramCount);
+            for (int i = 0; i < paramCount; i++) {
+                rsParams.set(paramList.get(i), i, false);
+            }
+            rsParams.copyAll();
+        }
+        return rsParams;
+    }
+
+    public ShaderParam(String name) {
+        mParamName = name;
+        mData = new ScriptField_ShaderParamData_s.Item();
+    }
+
+    public String getParamName() {
+        return mParamName;
+    }
+
+    public void setCamera(Camera c) {
+        mCamera = c;
+        if (mField != null) {
+            mData.camera = mCamera.getRSData().getAllocation();
+            mField.set_camera(0, mData.camera, true);
+        }
+    }
+
+    protected void incTimestamp() {
+        if (mField != null) {
+            mData.timestamp ++;
+            mData.timestamp %= sMaxTimeStamp;
+            mField.set_timestamp(0, mData.timestamp, true);
+        }
+    }
+
+    abstract void initLocalData();
+
+    public ScriptField_ShaderParamData_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        mField = new ScriptField_ShaderParamData_s(rs, 1);
+
+        if (mParamName != null) {
+            mData.paramName = SceneManager.getCachedAlloc(mParamName);
+            if (mData.paramName == null) {
+                mData.paramName = SceneManager.getStringAsAllocation(rs, mParamName);
+                SceneManager.cacheAlloc(mParamName, mData.paramName);
+            }
+        }
+        initLocalData();
+        mData.timestamp = 1;
+
+        mField.set(mData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
new file mode 100644
index 0000000..8fae9d9
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+
+import com.android.scenegraph.SceneManager;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Texture2D extends TextureBase {
+    String mFileName;
+    String mFileDir;
+
+    public Texture2D() {
+        super(ScriptC_export.const_TextureType_TEXTURE_2D);
+    }
+
+    public Texture2D(Allocation tex) {
+        super(ScriptC_export.const_TextureType_TEXTURE_2D);
+        setTexture(tex);
+    }
+
+    public void setFileDir(String dir) {
+        mFileDir = dir;
+    }
+
+    public void setFileName(String file) {
+        mFileName = file;
+    }
+
+    public String getFileName() {
+        return mFileName;
+    }
+
+    public void setTexture(Allocation tex) {
+        mData.texture = tex != null ? tex : SceneManager.getDefaultTex2D();
+        if (mField != null) {
+            mField.set_texture(0, mData.texture, true);
+        }
+    }
+
+    void load() {
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1);
+        setTexture(SceneManager.loadTexture2D(mFileDir + shortName, rs, res));
+    }
+
+    ScriptField_Texture_s getRsData(boolean loadNow) {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        mField = new ScriptField_Texture_s(rs, 1);
+
+        if (loadNow) {
+            load();
+        } else {
+            mData.texture = SceneManager.getDefaultTex2D();
+            new SingleImageLoaderTask().execute(this);
+        }
+
+        mField.set(mData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java
new file mode 100644
index 0000000..ba49d4e
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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.scenegraph;
+
+import java.lang.Math;
+
+import com.android.scenegraph.SceneManager;
+import android.os.AsyncTask;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class TextureBase extends SceneGraphBase {
+
+    class SingleImageLoaderTask extends AsyncTask<TextureBase, Void, Boolean> {
+        protected Boolean doInBackground(TextureBase... objects) {
+            TextureBase tex = objects[0];
+            tex.load();
+            return new Boolean(true);
+        }
+        protected void onPostExecute(Boolean result) {
+        }
+    }
+
+    ScriptField_Texture_s.Item mData;
+    ScriptField_Texture_s mField;
+    TextureBase(int type) {
+        mData = new ScriptField_Texture_s.Item();
+        mData.type = type;
+    }
+
+    protected Allocation mRsTexture;
+    abstract ScriptField_Texture_s getRsData(boolean loadNow);
+    abstract void load();
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
new file mode 100644
index 0000000..12c81c2
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+
+import com.android.scenegraph.SceneManager;
+import com.android.scenegraph.TextureBase;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TextureCube extends TextureBase {
+    String mFileName;
+    String mFileDir;
+
+    public TextureCube() {
+        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
+    }
+
+    public TextureCube(Allocation tex) {
+        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
+        setTexture(tex);
+    }
+
+    public TextureCube(String dir, String file) {
+        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
+        setFileDir(dir);
+        setFileName(file);
+    }
+
+    public void setFileDir(String dir) {
+        mFileDir = dir;
+    }
+
+    public void setFileName(String file) {
+        mFileName = file;
+    }
+
+    public String getFileName() {
+        return mFileName;
+    }
+
+    public void setTexture(Allocation tex) {
+        mData.texture = tex != null ? tex : SceneManager.getDefaultTexCube();
+        if (mField != null) {
+            mField.set_texture(0, mData.texture, true);
+        }
+    }
+
+    void load() {
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1);
+        setTexture(SceneManager.loadCubemap(mFileDir + shortName, rs, res));
+    }
+
+    ScriptField_Texture_s getRsData(boolean loadNow) {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        mField = new ScriptField_Texture_s(rs, 1);
+
+        if (loadNow) {
+            load();
+        } else {
+            mData.texture = SceneManager.getDefaultTexCube();
+            new SingleImageLoaderTask().execute(this);
+        }
+
+        mField.set(mData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java
new file mode 100644
index 0000000..e656ed2
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.graphics.Camera;
+import android.renderscript.RenderScriptGL;
+import android.renderscript.Float4;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.Element;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TextureParam extends ShaderParam {
+
+    TextureBase mTexture;
+
+    public TextureParam(String name) {
+        super(name);
+    }
+
+    public TextureParam(String name, TextureBase t) {
+        super(name);
+        setTexture(t);
+    }
+
+    public void setTexture(TextureBase t) {
+        mTexture = t;
+    }
+
+    public TextureBase getTexture() {
+        return mTexture;
+    }
+
+    void initLocalData() {
+        mData.type = ScriptC_export.const_ShaderParam_TEXTURE;
+        if (mTexture != null) {
+            mData.texture = mTexture.getRsData(false).getAllocation();
+        }
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java
new file mode 100644
index 0000000..6aa29a5
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 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.scenegraph;
+
+import java.lang.Math;
+
+import com.android.scenegraph.SceneManager;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TextureRenderTarget extends TextureBase {
+    public TextureRenderTarget() {
+        super(ScriptC_export.const_TextureType_TEXTURE_RENDER_TARGET);
+    }
+
+    public TextureRenderTarget(Allocation tex) {
+        super(ScriptC_export.const_TextureType_TEXTURE_RENDER_TARGET);
+        setTexture(tex);
+    }
+
+    public void setTexture(Allocation tex) {
+        mData.texture = tex;
+        if (mField != null) {
+            mField.set_texture(0, mData.texture, true);
+        }
+    }
+
+    void load() {
+    }
+
+    ScriptField_Texture_s getRsData(boolean loadNow) {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+
+        mField = new ScriptField_Texture_s(rs, 1);
+        mField.set(mData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java
new file mode 100644
index 0000000..8180bd0
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.*;
+import android.renderscript.Matrix4f;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class Transform extends SceneGraphBase {
+    Transform mParent;
+    ArrayList<Transform> mChildren;
+
+    ScriptField_SgTransform mField;
+    ScriptField_SgTransform.Item mTransformData;
+
+    public Transform() {
+        mChildren = new ArrayList<Transform>();
+        mParent = null;
+    }
+
+    public void appendChild(Transform t) {
+        mChildren.add(t);
+        t.mParent = this;
+        updateRSChildData(true);
+    }
+
+    abstract void initLocalData();
+
+    void updateRSChildData(boolean copyData) {
+        if (mField == null) {
+            return;
+        }
+        RenderScriptGL rs = SceneManager.getRS();
+        if (mChildren.size() != 0) {
+            Allocation childRSData = Allocation.createSized(rs, Element.ALLOCATION(rs),
+                                                            mChildren.size());
+            mTransformData.children = childRSData;
+
+            Allocation[] childrenAllocs = new Allocation[mChildren.size()];
+            for (int i = 0; i < mChildren.size(); i ++) {
+                Transform child = mChildren.get(i);
+                childrenAllocs[i] = child.getRSData().getAllocation();
+            }
+            childRSData.copyFrom(childrenAllocs);
+        }
+        if (copyData) {
+            mField.set(mTransformData, 0, true);
+        }
+    }
+
+    ScriptField_SgTransform getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+        mField = new ScriptField_SgTransform(rs, 1);
+
+        mTransformData = new ScriptField_SgTransform.Item();
+        mTransformData.name = getNameAlloc(rs);
+        mTransformData.isDirty = 1;
+        mTransformData.timestamp = 1;
+
+        initLocalData();
+        updateRSChildData(false);
+
+        mField.set(mTransformData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java
new file mode 100644
index 0000000..d120d5d
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.RenderScriptGL;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.Element;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TransformParam extends ShaderParam {
+
+    Transform mTransform;
+    LightBase mLight;
+
+    public TransformParam(String name) {
+        super(name);
+    }
+
+    public void setTransform(Transform t) {
+        mTransform = t;
+        if (mField != null && mTransform != null) {
+            mData.transform = mTransform.getRSData().getAllocation();
+        }
+        incTimestamp();
+    }
+
+    int getTypeFromName() {
+        int paramType = ScriptC_export.const_ShaderParam_TRANSFORM_DATA;
+        if (mParamName.equalsIgnoreCase(view)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_VIEW;
+        } else if(mParamName.equalsIgnoreCase(proj)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_PROJ;
+        } else if(mParamName.equalsIgnoreCase(viewProj)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_VIEW_PROJ;
+        } else if(mParamName.equalsIgnoreCase(model)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL;
+        } else if(mParamName.equalsIgnoreCase(modelView)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL_VIEW;
+        } else if(mParamName.equalsIgnoreCase(modelViewProj)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL_VIEW_PROJ;
+        }
+        return paramType;
+    }
+
+    void initLocalData() {
+        mData.type = getTypeFromName();
+        if (mTransform != null) {
+            mData.transform = mTransform.getRSData().getAllocation();
+        }
+        if (mCamera != null) {
+            mData.camera = mCamera.getRSData().getAllocation();
+        }
+        if (mLight != null) {
+            mData.light = mLight.getRSData().getAllocation();
+        }
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
new file mode 100644
index 0000000..f7d0e6d
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 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.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class VertexShader extends Shader {
+    ProgramVertex mProgram;
+    ScriptField_VertexShader_s mField;
+
+    public static class Builder {
+        VertexShader mShader;
+        ProgramVertex.Builder mBuilder;
+
+        public Builder(RenderScriptGL rs) {
+            mShader = new VertexShader();
+            mBuilder = new ProgramVertex.Builder(rs);
+        }
+
+        public Builder setShader(Resources resources, int resourceID) {
+            mBuilder.setShader(resources, resourceID);
+            return this;
+        }
+
+        public Builder setObjectConst(Type type) {
+            mShader.mPerObjConstants = type;
+            return this;
+        }
+
+        public Builder setShaderConst(Type type) {
+            mShader.mPerShaderConstants = type;
+            return this;
+        }
+
+        public Builder addInput(Element e) {
+            mBuilder.addInput(e);
+            return this;
+        }
+
+        public VertexShader create() {
+            if (mShader.mPerShaderConstants != null) {
+                mBuilder.addConstant(mShader.mPerShaderConstants);
+            }
+            if (mShader.mPerObjConstants != null) {
+                mBuilder.addConstant(mShader.mPerObjConstants);
+            }
+            mShader.mProgram = mBuilder.create();
+            return mShader;
+        }
+    }
+
+    public ProgramVertex getProgram() {
+        return mProgram;
+    }
+
+    ScriptField_VertexShader_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        ScriptField_VertexShader_s.Item item = new ScriptField_VertexShader_s.Item();
+        item.program = mProgram;
+
+        linkConstants(rs);
+        if (mPerShaderConstants != null) {
+            item.shaderConst = mConstantBuffer;
+            item.shaderConstParams = mConstantBufferParams.getAllocation();
+            mProgram.bindConstants(item.shaderConst, 0);
+        }
+
+        item.objectConstIndex = -1;
+        if (mPerObjConstants != null) {
+            item.objectConstIndex = mPerShaderConstants != null ? 1 : 0;
+        }
+
+        mField = new ScriptField_VertexShader_s(rs, 1);
+        mField.set(item, 0, true);
+        return mField;
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs
new file mode 100644
index 0000000..dc0a885
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs
@@ -0,0 +1,66 @@
+// Copyright (C) 2011 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+//#define DEBUG_CAMERA
+#include "scenegraph_objects.rsh"
+
+void root(const rs_allocation *v_in, rs_allocation *v_out, const float *usrData) {
+
+    SgCamera *cam = (SgCamera *)rsGetElementAt(*v_in, 0);
+    float aspect = *usrData;
+    if (cam->aspect != aspect) {
+        cam->isDirty = 1;
+        cam->aspect = aspect;
+    }
+    if (cam->isDirty) {
+        rsMatrixLoadPerspective(&cam->proj, cam->horizontalFOV, cam->aspect, cam->near, cam->far);
+    }
+
+    const SgTransform *camTransform = (const SgTransform *)rsGetElementAt(cam->transformMatrix, 0);
+    //rsDebug("Camera stamp", cam->transformTimestamp);
+    //rsDebug("Transform stamp", camTransform->timestamp);
+    if (camTransform->timestamp != cam->transformTimestamp || cam->isDirty) {
+        cam->isDirty = 1;
+        rs_matrix4x4 camPosMatrix;
+        rsMatrixLoad(&camPosMatrix, &camTransform->globalMat);
+        float4 zero = {0.0f, 0.0f, 0.0f, 1.0f};
+        cam->position = rsMatrixMultiply(&camPosMatrix, zero);
+
+        rsMatrixInverse(&camPosMatrix);
+        rsMatrixLoad(&cam->view, &camPosMatrix);
+
+        rsMatrixLoad(&cam->viewProj, &cam->proj);
+        rsMatrixMultiply(&cam->viewProj, &cam->view);
+
+        rsExtractFrustumPlanes(&cam->viewProj,
+                               &cam->frustumPlanes[0], &cam->frustumPlanes[1],
+                               &cam->frustumPlanes[2], &cam->frustumPlanes[3],
+                               &cam->frustumPlanes[3], &cam->frustumPlanes[4]);
+    }
+
+    if (cam->isDirty) {
+        cam->timestamp ++;
+    }
+
+    cam->isDirty = 0;
+    cam->transformTimestamp = camTransform->timestamp;
+
+#ifdef DEBUG_CAMERA
+    printCameraInfo(cam);
+#endif //DEBUG_CAMERA
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs
new file mode 100644
index 0000000..024e026
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs
@@ -0,0 +1,86 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+static void getTransformedSphere(SgRenderable *obj) {
+    obj->worldBoundingSphere = obj->boundingSphere;
+    obj->worldBoundingSphere.w = 1.0f;
+    const SgTransform *objTransform = (const SgTransform *)rsGetElementAt(obj->transformMatrix, 0);
+    obj->worldBoundingSphere = rsMatrixMultiply(&objTransform->globalMat, obj->worldBoundingSphere);
+
+    const float4 unitVec = {0.57735f, 0.57735f, 0.57735f, 0.0f};
+    float4 scaledVec = rsMatrixMultiply(&objTransform->globalMat, unitVec);
+    scaledVec.w = 0.0f;
+    obj->worldBoundingSphere.w = obj->boundingSphere.w * length(scaledVec);
+}
+
+static bool frustumCulled(SgRenderable *obj, SgCamera *cam) {
+    if (!obj->bVolInitialized) {
+        float minX, minY, minZ, maxX, maxY, maxZ;
+        rsgMeshComputeBoundingBox(obj->mesh,
+                                  &minX, &minY, &minZ,
+                                  &maxX, &maxY, &maxZ);
+        //rsDebug("min", minX, minY, minZ);
+        //rsDebug("max", maxX, maxY, maxZ);
+        float4 sphere;
+        sphere.x = (maxX + minX) * 0.5f;
+        sphere.y = (maxY + minY) * 0.5f;
+        sphere.z = (maxZ + minZ) * 0.5f;
+        float3 radius;
+        radius.x = (maxX - sphere.x);
+        radius.y = (maxY - sphere.y);
+        radius.z = (maxZ - sphere.z);
+
+        sphere.w = length(radius);
+        obj->boundingSphere = sphere;
+        obj->bVolInitialized = 1;
+        //rsDebug("Sphere", sphere);
+    }
+
+    getTransformedSphere(obj);
+
+    return !rsIsSphereInFrustum(&obj->worldBoundingSphere,
+                                &cam->frustumPlanes[0], &cam->frustumPlanes[1],
+                                &cam->frustumPlanes[2], &cam->frustumPlanes[3],
+                                &cam->frustumPlanes[4], &cam->frustumPlanes[5]);
+}
+
+
+void root(rs_allocation *v_out, const void *usrData) {
+
+    SgRenderable *drawable = (SgRenderable *)rsGetElementAt(*v_out, 0);
+    const SgCamera *camera = (const SgCamera*)usrData;
+
+    drawable->isVisible = 0;
+    // Not loaded yet
+    if (!rsIsObject(drawable->mesh) || drawable->cullType == CULL_ALWAYS) {
+        return;
+    }
+
+    // check to see if we are culling this object and if it's
+    // outside the frustum
+    if (drawable->cullType == CULL_FRUSTUM && frustumCulled(drawable, (SgCamera*)camera)) {
+#ifdef DEBUG_RENDERABLES
+        rsDebug("Culled", drawable);
+        printName(drawable->name);
+#endif // DEBUG_RENDERABLES
+        return;
+    }
+    drawable->isVisible = 1;
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs
new file mode 100644
index 0000000..b438a43
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs
@@ -0,0 +1,61 @@
+// Copyright (C) 2011 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+// The sole purpose of this script is to have various structs exposed
+// so that java reflected classes are generated
+#include "scenegraph_objects.rsh"
+
+// Export our native constants to java so that we don't have parallel definitions
+const int ShaderParam_FLOAT4_DATA = SHADER_PARAM_FLOAT4_DATA;
+const int ShaderParam_TRANSFORM_DATA = SHADER_PARAM_TRANSFORM_DATA;
+const int ShaderParam_TRANSFORM_MODEL = SHADER_PARAM_TRANSFORM_MODEL;
+
+const int ShaderParam_FLOAT4_CAMERA_POS = SHADER_PARAM_FLOAT4_CAMERA_POS;
+const int ShaderParam_FLOAT4_CAMERA_DIR = SHADER_PARAM_FLOAT4_CAMERA_DIR;
+const int ShaderParam_TRANSFORM_VIEW = SHADER_PARAM_TRANSFORM_VIEW;
+const int ShaderParam_TRANSFORM_PROJ = SHADER_PARAM_TRANSFORM_PROJ;
+const int ShaderParam_TRANSFORM_VIEW_PROJ = SHADER_PARAM_TRANSFORM_VIEW_PROJ;
+const int ShaderParam_TRANSFORM_MODEL_VIEW = SHADER_PARAM_TRANSFORM_MODEL_VIEW;
+const int ShaderParam_TRANSFORM_MODEL_VIEW_PROJ = SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ;
+
+const int ShaderParam_FLOAT4_LIGHT_COLOR = SHADER_PARAM_FLOAT4_LIGHT_COLOR;
+const int ShaderParam_FLOAT4_LIGHT_POS = SHADER_PARAM_FLOAT4_LIGHT_POS;
+const int ShaderParam_FLOAT4_LIGHT_DIR = SHADER_PARAM_FLOAT4_LIGHT_DIR;
+
+const int ShaderParam_TEXTURE = SHADER_PARAM_TEXTURE;
+
+const int Transform_TRANSLATE = TRANSFORM_TRANSLATE;
+const int Transform_ROTATE = TRANSFORM_ROTATE;
+const int Transform_SCALE = TRANSFORM_SCALE;
+
+const int TextureType_TEXTURE_2D = TEXTURE_2D;
+const int TextureType_TEXTURE_CUBE = TEXTURE_CUBE;
+const int TextureType_TEXTURE_RENDER_TARGET = TEXTURE_RENDER_TARGET;
+
+SgTransform *exportPtr;
+SgTransformComponent *componentPtr;
+SgRenderState *sExport;
+SgRenderable *drExport;
+SgRenderPass *pExport;
+SgCamera *exportPtrCam;
+SgLight *exportPtrLight;
+SgShaderParam *spExport;
+SgShaderParamData *spDataExport;
+SgVertexShader *pvExport;
+SgFragmentShader *pfExport;
+SgTexture *texExport;
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs
new file mode 100644
index 0000000..7202285
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs
@@ -0,0 +1,30 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+//#define DEBUG_PARAMS
+
+#include "params.rsh"
+
+void root(rs_allocation *v_out, const void *usrData) {
+    SgFragmentShader *shader = (SgFragmentShader *)rsGetElementAt(*v_out, 0);
+    const SgCamera *camera = (const SgCamera*)usrData;
+    processAllParams(shader->shaderConst, shader->shaderConstParams, camera);
+    processTextureParams(shader);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs
new file mode 100644
index 0000000..e11979f
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs
@@ -0,0 +1,33 @@
+// Copyright (C) 2011 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+//#define DEBUG_LIGHT
+#include "scenegraph_objects.rsh"
+
+void root(const rs_allocation *v_in, rs_allocation *v_out) {
+
+    SgLight *light = (SgLight *)rsGetElementAt(*v_in, 0);
+    const SgTransform *lTransform = (const SgTransform *)rsGetElementAt(light->transformMatrix, 0);
+
+    float4 zero = {0.0f, 0.0f, 0.0f, 1.0f};
+    light->position = rsMatrixMultiply(&lTransform->globalMat, zero);
+
+#ifdef DEBUG_LIGHT
+    printLightInfo(light);
+#endif //DEBUG_LIGHT
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs
new file mode 100644
index 0000000..0d524a6
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs
@@ -0,0 +1,36 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+//#define DEBUG_PARAMS
+
+#include "params.rsh"
+
+void root(rs_allocation *v_out, const void *usrData) {
+
+    SgRenderable *drawable = (SgRenderable *)rsGetElementAt(*v_out, 0);
+    // Visibility flag was set earlier in the cull stage
+    if (!drawable->isVisible) {
+        return;
+    }
+
+    const SgCamera *camera = (const SgCamera*)usrData;
+    processAllParams(drawable->pf_const, drawable->pf_constParams, camera);
+    processAllParams(drawable->pv_const, drawable->pv_constParams, camera);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
new file mode 100644
index 0000000..575794b
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
@@ -0,0 +1,193 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+//#define DEBUG_PARAMS
+static void debugParam(SgShaderParam *p, SgShaderParamData *pData) {
+    rsDebug("____________ Param ____________", p);
+    printName(pData->paramName);
+    rsDebug("bufferOffset", p->bufferOffset);
+    rsDebug("type ", pData->type);
+    rsDebug("data timestamp ", pData->timestamp);
+    rsDebug("param timestamp", p->dataTimestamp);
+
+    const SgTransform *pTransform = NULL;
+    if (rsIsObject(pData->transform)) {
+        pTransform = (const SgTransform *)rsGetElementAt(pData->transform, 0);
+
+        rsDebug("transform", pTransform);
+        printName(pTransform->name);
+        rsDebug("timestamp", pTransform->timestamp);
+        rsDebug("param timestamp", p->transformTimestamp);
+    }
+
+    const SgLight *pLight = NULL;
+    if (rsIsObject(pData->light)) {
+        pLight = (const SgLight *)rsGetElementAt(pData->light, 0);
+        printLightInfo(pLight);
+    }
+}
+
+
+static void writeFloatData(float *ptr, const float4 *input, uint32_t vecSize) {
+#ifdef DEBUG_PARAMS
+    rsDebug("Writing value ", *input);
+    rsDebug("Writing vec size ", vecSize);
+#endif // DEBUG_PARAMS
+
+    switch (vecSize) {
+    case 1:
+        *ptr = input->x;
+        break;
+    case 2:
+        *((float2*)ptr) = (*input).xy;
+        break;
+    case 3:
+        *((float3*)ptr) = (*input).xyz;
+        break;
+    case 4:
+        *((float4*)ptr) = *input;
+        break;
+    }
+}
+
+static bool processParam(SgShaderParam *p, SgShaderParamData *pData,
+                         uint8_t *constantBuffer,
+                         const SgCamera *currentCam,
+                         SgFragmentShader *shader) {
+    bool isDataOnly = (pData->type > SHADER_PARAM_DATA_ONLY);
+    const SgTransform *pTransform = NULL;
+    if (rsIsObject(pData->transform)) {
+        pTransform = (const SgTransform *)rsGetElementAt(pData->transform, 0);
+    }
+
+    if (isDataOnly) {
+        // If we are a transform param and our transform is unchanged, nothing to do
+        if (pTransform) {
+            if (p->transformTimestamp == pTransform->timestamp) {
+                return false;
+            }
+            p->transformTimestamp = pTransform->timestamp;
+        } else {
+            if (p->dataTimestamp == pData->timestamp) {
+                return false;
+            }
+            p->dataTimestamp = pData->timestamp;
+        }
+    }
+
+    const SgLight *pLight = NULL;
+    if (rsIsObject(pData->light)) {
+        pLight = (const SgLight *)rsGetElementAt(pData->light, 0);
+    }
+
+    uint8_t *dataPtr = NULL;
+    const SgTexture *tex = NULL;
+    if (pData->type == SHADER_PARAM_TEXTURE) {
+        tex = rsGetElementAt(pData->texture, 0);
+    } else {
+        dataPtr = constantBuffer + p->bufferOffset;
+    }
+
+    switch (pData->type) {
+    case SHADER_PARAM_TEXTURE:
+        rsgBindTexture(shader->program, p->bufferOffset, tex->texture);
+        break;
+    case SHADER_PARAM_FLOAT4_DATA:
+        writeFloatData((float*)dataPtr, &pData->float_value, p->float_vecSize);
+        break;
+    case SHADER_PARAM_FLOAT4_CAMERA_POS:
+        writeFloatData((float*)dataPtr, &currentCam->position, p->float_vecSize);
+        break;
+    case SHADER_PARAM_FLOAT4_CAMERA_DIR: break;
+    case SHADER_PARAM_FLOAT4_LIGHT_COLOR:
+        writeFloatData((float*)dataPtr, &pLight->color, p->float_vecSize);
+        break;
+    case SHADER_PARAM_FLOAT4_LIGHT_POS:
+        writeFloatData((float*)dataPtr, &pLight->position, p->float_vecSize);
+        break;
+    case SHADER_PARAM_FLOAT4_LIGHT_DIR: break;
+
+    case SHADER_PARAM_TRANSFORM_DATA:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &pTransform->globalMat);
+        break;
+    case SHADER_PARAM_TRANSFORM_VIEW:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->view);
+        break;
+    case SHADER_PARAM_TRANSFORM_PROJ:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->proj);
+        break;
+    case SHADER_PARAM_TRANSFORM_VIEW_PROJ:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->viewProj);
+        break;
+    case SHADER_PARAM_TRANSFORM_MODEL:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &pTransform->globalMat);
+        break;
+    case SHADER_PARAM_TRANSFORM_MODEL_VIEW:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->view);
+        rsMatrixLoadMultiply((rs_matrix4x4*)dataPtr,
+                             (rs_matrix4x4*)dataPtr,
+                             &pTransform->globalMat);
+        break;
+    case SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->viewProj);
+        rsMatrixLoadMultiply((rs_matrix4x4*)dataPtr,
+                             (rs_matrix4x4*)dataPtr,
+                             &pTransform->globalMat);
+        break;
+    }
+    return true;
+}
+
+static void processAllParams(rs_allocation shaderConst,
+                             rs_allocation allParams,
+                             const SgCamera *camera) {
+    if (rsIsObject(shaderConst)) {
+        uint8_t *constantBuffer = (uint8_t*)rsGetElementAt(shaderConst, 0);
+
+        int numParams = 0;
+        if (rsIsObject(allParams)) {
+            numParams = rsAllocationGetDimX(allParams);
+        }
+        bool updated = false;
+        for (int i = 0; i < numParams; i ++) {
+            SgShaderParam *current = (SgShaderParam*)rsGetElementAt(allParams, i);
+            SgShaderParamData *currentData = (SgShaderParamData*)rsGetElementAt(current->data, 0);
+#ifdef DEBUG_PARAMS
+            debugParam(current, currentData);
+#endif // DEBUG_PARAMS
+            updated = processParam(current, currentData, constantBuffer, camera, NULL) || updated;
+        }
+    }
+}
+
+static void processTextureParams(SgFragmentShader *shader) {
+    int numParams = 0;
+    if (rsIsObject(shader->shaderTextureParams)) {
+        numParams = rsAllocationGetDimX(shader->shaderTextureParams);
+    }
+    for (int i = 0; i < numParams; i ++) {
+        SgShaderParam *current = (SgShaderParam*)rsGetElementAt(shader->shaderTextureParams, i);
+        SgShaderParamData *currentData = (SgShaderParamData*)rsGetElementAt(current->data, 0);
+#ifdef DEBUG_PARAMS
+        debugParam(current, currentData);
+#endif // DEBUG_PARAMS
+        processParam(current, currentData, NULL, NULL, shader);
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
new file mode 100644
index 0000000..d8d48b3
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
@@ -0,0 +1,240 @@
+// Copyright (C) 2011-2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "rs_graphics.rsh"
+#include "scenegraph_objects.rsh"
+
+rs_script gTransformScript;
+rs_script gCameraScript;
+rs_script gLightScript;
+rs_script gObjectParamsScript;
+rs_script gFragmentParamsScript;
+rs_script gVertexParamsScript;
+rs_script gCullScript;
+
+SgTransform *gRootNode;
+rs_allocation gCameras;
+rs_allocation gLights;
+rs_allocation gFragmentShaders;
+rs_allocation gVertexShaders;
+rs_allocation gRenderableObjects;
+
+rs_allocation gRenderPasses;
+
+// Temporary shaders
+rs_program_store gPFSBackground;
+
+uint32_t *gFrontToBack;
+static uint32_t gFrontToBackCount = 0;
+uint32_t *gBackToFront;
+static uint32_t gBackToFrontCount = 0;
+
+static SgCamera *gActiveCamera = NULL;
+
+static rs_allocation nullAlloc;
+
+// #define DEBUG_RENDERABLES
+static void draw(SgRenderable *obj) {
+#ifdef DEBUG_RENDERABLES
+    const SgTransform *objTransform = (const SgTransform *)rsGetElementAt(obj->transformMatrix, 0);
+    rsDebug("**** Drawing object with transform", obj);
+    printName(objTransform->name);
+    rsDebug("Model matrix: ", &objTransform->globalMat);
+    printName(obj->name);
+#endif //DEBUG_RENDERABLES
+
+    const SgRenderState *renderState = (const SgRenderState *)rsGetElementAt(obj->render_state, 0);
+    const SgVertexShader *pv = (const SgVertexShader *)rsGetElementAt(renderState->pv, 0);
+    const SgFragmentShader *pf = (const SgFragmentShader *)rsGetElementAt(renderState->pf, 0);
+
+    if (pv->objectConstIndex != -1) {
+        rsgBindConstant(pv->program, pv->objectConstIndex, obj->pv_const);
+    }
+    if (pf->objectConstIndex != -1) {
+        rsgBindConstant(pf->program, pf->objectConstIndex, obj->pf_const);
+    }
+
+    if (rsIsObject(renderState->ps)) {
+        rsgBindProgramStore(renderState->ps);
+    } else {
+        rsgBindProgramStore(gPFSBackground);
+    }
+
+    if (rsIsObject(renderState->pr)) {
+        rsgBindProgramRaster(renderState->pr);
+    } else {
+        rs_program_raster pr;
+        rsgBindProgramRaster(pr);
+    }
+
+    rsgBindProgramVertex(pv->program);
+    rsgBindProgramFragment(pf->program);
+
+    for (uint32_t i = 0; i < obj->pf_num_textures; i ++) {
+        const SgTexture *tex = rsGetElementAt(obj->pf_textures[i], 0);
+        rsgBindTexture(pf->program, i, tex->texture);
+    }
+
+    rsgDrawMesh(obj->mesh, obj->meshIndex);
+}
+
+static void sortToBucket(SgRenderable *obj) {
+    const SgRenderState *renderState = (const SgRenderState *)rsGetElementAt(obj->render_state, 0);
+    if (rsIsObject(renderState->ps)) {
+        bool isOpaque = false;
+        if (isOpaque) {
+            gFrontToBack[gFrontToBackCount++] = (uint32_t)obj;
+        } else {
+            gBackToFront[gBackToFrontCount++] = (uint32_t)obj;
+        }
+    } else {
+        gFrontToBack[gFrontToBackCount++] = (uint32_t)obj;
+    }
+}
+
+static void updateActiveCamera(rs_allocation cam) {
+    gActiveCamera = (SgCamera *)rsGetElementAt(cam, 0);
+}
+
+static void prepareCameras() {
+    // now compute all the camera matrices
+    if (rsIsObject(gCameras)) {
+        float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+        rsForEach(gCameraScript, gCameras, nullAlloc, &aspect, sizeof(aspect));
+    }
+}
+
+static void prepareLights() {
+    if (rsIsObject(gLights)) {
+        rsForEach(gLightScript, gLights, nullAlloc);
+    }
+}
+
+static void drawSorted() {
+    for (int i = 0; i < gFrontToBackCount; i ++) {
+        SgRenderable *current = (SgRenderable*)gFrontToBack[i];
+        draw(current);
+    }
+
+    for (int i = 0; i < gBackToFrontCount; i ++) {
+        SgRenderable *current = (SgRenderable*)gBackToFront[i];
+        draw(current);
+    }
+}
+
+static void drawAllObjects(rs_allocation allObj) {
+    if (!rsIsObject(allObj)) {
+        return;
+    }
+
+    rsForEach(gVertexParamsScript, nullAlloc, gVertexShaders,
+              gActiveCamera, sizeof(gActiveCamera));
+    rsForEach(gFragmentParamsScript, nullAlloc, gFragmentShaders,
+              gActiveCamera, sizeof(gActiveCamera));
+
+    // Run the params and cull script
+    rsForEach(gCullScript, nullAlloc, allObj, gActiveCamera, sizeof(gActiveCamera));
+    rsForEach(gObjectParamsScript, nullAlloc, allObj, gActiveCamera, sizeof(gActiveCamera));
+
+    int numRenderables = rsAllocationGetDimX(allObj);
+    for (int i = 0; i < numRenderables; i ++) {
+        rs_allocation *drawAlloc = (rs_allocation*)rsGetElementAt(allObj, i);
+        SgRenderable *current = (SgRenderable*)rsGetElementAt(*drawAlloc, 0);
+        if (current->isVisible) {
+            sortToBucket(current);
+        }
+    }
+    drawSorted();
+}
+
+int root(void) {
+#ifdef DEBUG_RENDERABLES
+    rsDebug("=============================================================================", 0);
+#endif // DEBUG_RENDERABLES
+
+    // first step is to update the transform hierachy
+    if (gRootNode && rsIsObject(gRootNode->children)) {
+        rsForEach(gTransformScript, gRootNode->children, nullAlloc, 0, 0);
+    }
+
+    prepareCameras();
+    prepareLights();
+
+    if (rsIsObject(gRenderPasses)) {
+        rsgClearDepth(1.0f);
+        int numPasses = rsAllocationGetDimX(gRenderPasses);
+        for (uint i = 0; i < numPasses; i ++) {
+            gFrontToBackCount = 0;
+            gBackToFrontCount = 0;
+            SgRenderPass *pass = (SgRenderPass*)rsGetElementAt(gRenderPasses, i);
+            if (rsIsObject(pass->color_target)) {
+                rsgBindColorTarget(pass->color_target, 0);
+            }
+            if (rsIsObject(pass->depth_target)) {
+                rsgBindDepthTarget(pass->depth_target);
+            }
+            if (!rsIsObject(pass->color_target) &&
+                !rsIsObject(pass->depth_target)) {
+                rsgClearAllRenderTargets();
+            }
+            updateActiveCamera(pass->camera);
+            if (pass->should_clear_color) {
+                rsgClearColor(pass->clear_color.x, pass->clear_color.y,
+                              pass->clear_color.z, pass->clear_color.w);
+            }
+            if (pass->should_clear_depth) {
+                rsgClearDepth(pass->clear_depth);
+            }
+            drawAllObjects(pass->objects);
+        }
+    } else {
+        gFrontToBackCount = 0;
+        gBackToFrontCount = 0;
+        rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+        rsgClearDepth(1.0f);
+
+        if (rsIsObject(gCameras)) {
+            rs_allocation *camAlloc = (rs_allocation*)rsGetElementAt(gCameras, 0);
+            updateActiveCamera(*camAlloc);
+        }
+        drawAllObjects(gRenderableObjects);
+    }
+    return 10;
+}
+
+// Search through sorted and culled objects
+void pick(int screenX, int screenY) {
+    float3 pnt, vec;
+    getCameraRay(gActiveCamera, screenX, screenY, &pnt, &vec);
+
+    for (int i = 0; i < gFrontToBackCount; i ++) {
+        SgRenderable *current = (SgRenderable*)gFrontToBack[i];
+        bool isPicked = intersect(current, pnt, vec);
+        if (isPicked) {
+            current->cullType = CULL_ALWAYS;
+        }
+    }
+
+    for (int i = 0; i < gBackToFrontCount; i ++) {
+        SgRenderable *current = (SgRenderable*)gBackToFront[i];
+        bool isPicked = intersect(current, pnt, vec);
+        if (isPicked) {
+            current->cullType = CULL_ALWAYS;
+        }
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
new file mode 100644
index 0000000..bdca3ab
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
@@ -0,0 +1,323 @@
+// Copyright (C) 2011-2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#ifndef _TRANSFORM_DEF_
+#define _TRANSFORM_DEF_
+
+#include "rs_graphics.rsh"
+
+#define TRANSFORM_NONE      0
+#define TRANSFORM_TRANSLATE 1
+#define TRANSFORM_ROTATE    2
+#define TRANSFORM_SCALE     3
+
+#define CULL_FRUSTUM 0
+#define CULL_ALWAYS  2
+
+#define LIGHT_POINT       0
+#define LIGHT_DIRECTIONAL 1
+
+// Shader params that involve only data
+#define SHADER_PARAM_DATA_ONLY                 10000
+#define SHADER_PARAM_FLOAT4_DATA               10001
+#define SHADER_PARAM_TRANSFORM_DATA            10002
+#define SHADER_PARAM_TRANSFORM_MODEL           10003
+
+// Shader params that involve camera
+#define SHADER_PARAM_CAMERA                    1000
+#define SHADER_PARAM_FLOAT4_CAMERA_POS         1001
+#define SHADER_PARAM_FLOAT4_CAMERA_DIR         1002
+#define SHADER_PARAM_TRANSFORM_VIEW            1003
+#define SHADER_PARAM_TRANSFORM_PROJ            1004
+#define SHADER_PARAM_TRANSFORM_VIEW_PROJ       1005
+#define SHADER_PARAM_TRANSFORM_MODEL_VIEW      1006
+#define SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ 1007
+
+// Shader Params that only involve lights
+#define SHADER_PARAM_LIGHT                     100
+#define SHADER_PARAM_FLOAT4_LIGHT_COLOR        103
+#define SHADER_PARAM_FLOAT4_LIGHT_POS          104
+#define SHADER_PARAM_FLOAT4_LIGHT_DIR          105
+
+#define SHADER_PARAM_TEXTURE                   10
+
+#define TEXTURE_NONE          0
+#define TEXTURE_2D            1
+#define TEXTURE_CUBE          2
+#define TEXTURE_RENDER_TARGET 3
+
+typedef struct TransformComponent_s {
+    float4 value;
+    int type;
+    rs_allocation name;
+} SgTransformComponent;
+
+typedef struct __attribute__((packed, aligned(4))) SgTransform {
+    rs_matrix4x4 globalMat;
+    rs_matrix4x4 localMat;
+
+    rs_allocation components;
+    int isDirty;
+
+    rs_allocation children;
+    rs_allocation name;
+
+    // Used to check whether transform params need to be updated
+    uint32_t timestamp;
+} SgTransform;
+
+typedef struct VertexShader_s {
+    rs_program_vertex program;
+    // Buffer with vertex constant data
+    rs_allocation shaderConst;
+    // ShaderParam's that populate data
+    rs_allocation shaderConstParams;
+    // location of the per object constants on the buffer
+    int objectConstIndex;
+} SgVertexShader;
+
+typedef struct FragmentShader_s {
+    rs_program_fragment program;
+    // Buffer with vertex constant data
+    rs_allocation shaderConst;
+    // ShaderParam's that populate data
+    rs_allocation shaderConstParams;
+    // ShaderParam's that set textures
+    rs_allocation shaderTextureParams;
+    // location of the per object constants on the buffer
+    int objectConstIndex;
+} SgFragmentShader;
+
+typedef struct RenderState_s {
+    rs_allocation pv; // VertexShader struct
+    rs_allocation pf; // FragmentShader struct
+    rs_program_store ps;
+    rs_program_raster pr;
+} SgRenderState;
+
+typedef struct Renderable_s {
+    rs_allocation render_state;
+    // Buffer with vertex constant data
+    rs_allocation pv_const;
+    // ShaderParam's that populate data
+    rs_allocation pv_constParams;
+    // Buffer with fragment constant data
+    rs_allocation pf_const;
+    // ShaderParam's that populate data
+    rs_allocation pf_constParams;
+    rs_allocation pf_textures[8];
+    int pf_num_textures;
+    rs_mesh mesh;
+    int meshIndex;
+    rs_allocation transformMatrix;
+    rs_allocation name;
+    float4 boundingSphere;
+    float4 worldBoundingSphere;
+    int bVolInitialized;
+    int cullType; // specifies whether to frustum cull
+    int isVisible;
+} SgRenderable;
+
+typedef struct RenderPass_s {
+    rs_allocation color_target;
+    rs_allocation depth_target;
+    rs_allocation camera;
+    rs_allocation objects;
+
+    float4 clear_color;
+    float clear_depth;
+    bool should_clear_color;
+    bool should_clear_depth;
+} SgRenderPass;
+
+typedef struct Camera_s {
+    rs_matrix4x4 proj;
+    rs_matrix4x4 view;
+    rs_matrix4x4 viewProj;
+    float4 position;
+    float near;
+    float far;
+    float horizontalFOV;
+    float aspect;
+    rs_allocation name;
+    rs_allocation transformMatrix;
+    float4 frustumPlanes[6];
+
+    int isDirty;
+    // Timestamp of the camera itself to signal params if anything changes
+    uint32_t timestamp;
+    // Timestamp of our transform
+    uint32_t transformTimestamp;
+} SgCamera;
+
+typedef struct Light_s {
+    float4 position;
+    float4 color;
+    float intensity;
+    int type;
+    rs_allocation name;
+    rs_allocation transformMatrix;
+} SgLight;
+
+// This represents the shader parameter data needed to set a float or transform data
+typedef struct ShaderParamData_s {
+    int type;
+    float4 float_value;
+    uint32_t timestamp;
+    rs_allocation paramName;
+    rs_allocation camera;
+    rs_allocation light;
+    rs_allocation transform;
+    rs_allocation texture;
+} SgShaderParamData;
+
+// This represents a shader parameter that knows how to update itself for a given
+// renderable or shader and contains a timestamp for the last time this buffer was updated
+typedef struct ShaderParam_s {
+    // Used to check whether transform params need to be updated
+    uint32_t transformTimestamp;
+    // Used to check whether data params need to be updated
+    // These are used when somebody set the matrix of float value directly in java
+    uint32_t dataTimestamp;
+    // Specifies where in the constant buffer data gets written to
+    int bufferOffset;
+    // An instance of SgShaderParamData that could be shared by multiple objects
+    rs_allocation data;
+    // How many components of the vector we need to write
+    int float_vecSize;
+} SgShaderParam;
+
+// This represents a texture object
+typedef struct Texture_s {
+    uint32_t type;
+    rs_allocation texture;
+} SgTexture;
+
+static void printName(rs_allocation name) {
+    if (!rsIsObject(name)) {
+        rsDebug("no name", 0);
+        return;
+    }
+
+    rsDebug((const char*)rsGetElementAt(name, 0), 0);
+}
+
+static void printCameraInfo(const SgCamera *cam) {
+    rsDebug("***** Camera information. ptr:", cam);
+    printName(cam->name);
+    const SgTransform *camTransform = (const SgTransform *)rsGetElementAt(cam->transformMatrix, 0);
+    rsDebug("Transform name:", camTransform);
+    printName(camTransform->name);
+
+    rsDebug("Aspect: ", cam->aspect);
+    rsDebug("Near: ", cam->near);
+    rsDebug("Far: ", cam->far);
+    rsDebug("Fov: ", cam->horizontalFOV);
+    rsDebug("Position: ", cam->position);
+    rsDebug("Proj: ", &cam->proj);
+    rsDebug("View: ", &cam->view);
+}
+
+static void printLightInfo(const SgLight *light) {
+    rsDebug("***** Light information. ptr:", light);
+    printName(light->name);
+    const SgTransform *lTransform = (const SgTransform *)rsGetElementAt(light->transformMatrix, 0);
+    rsDebug("Transform name:", lTransform);
+    printName(lTransform->name);
+
+    rsDebug("Position: ", light->position);
+    rsDebug("Color : ", light->color);
+    rsDebug("Intensity: ", light->intensity);
+    rsDebug("Type: ", light->type);
+}
+
+static void getCameraRay(const SgCamera *cam, int screenX, int screenY, float3 *pnt, float3 *vec) {
+    rsDebug("=================================", screenX);
+    rsDebug("Point X", screenX);
+    rsDebug("Point Y", screenY);
+
+    rs_matrix4x4 mvpInv;
+    rsMatrixLoad(&mvpInv, &cam->viewProj);
+    rsMatrixInverse(&mvpInv);
+
+    float width = (float)rsgGetWidth();
+    float height = (float)rsgGetHeight();
+
+    float4 pos = {(float)screenX, height - (float)screenY, 0.0f, 1.0f};
+
+    pos.x /= width;
+    pos.y /= height;
+
+    rsDebug("Pre Norm X", pos.x);
+    rsDebug("Pre Norm Y", pos.y);
+
+    pos.xy = pos.xy * 2.0f - 1.0f;
+
+    rsDebug("Norm X", pos.x);
+    rsDebug("Norm Y", pos.y);
+
+    pos = rsMatrixMultiply(&mvpInv, pos);
+    float oneOverW = 1.0f / pos.w;
+    pos.xyz *= oneOverW;
+
+    rsDebug("World X", pos.x);
+    rsDebug("World Y", pos.y);
+    rsDebug("World Z", pos.z);
+
+    rsDebug("Cam X", cam->position.x);
+    rsDebug("Cam Y", cam->position.y);
+    rsDebug("Cam Z", cam->position.z);
+
+    *vec = normalize(pos.xyz - cam->position.xyz);
+    rsDebug("Vec X", vec->x);
+    rsDebug("Vec Y", vec->y);
+    rsDebug("Vec Z", vec->z);
+    *pnt = cam->position.xyz;
+}
+
+static bool intersect(const SgRenderable *obj, float3 pnt, float3 vec) {
+    // Solving for t^2 + Bt + C = 0
+    float3 originMinusCenter = pnt - obj->worldBoundingSphere.xyz;
+    float B = dot(originMinusCenter, vec) * 2.0f;
+    float C = dot(originMinusCenter, originMinusCenter) -
+              obj->worldBoundingSphere.w * obj->worldBoundingSphere.w;
+
+    float discriminant = B * B - 4.0f * C;
+    if (discriminant < 0.0f) {
+        return false;
+    }
+    discriminant = sqrt(discriminant);
+
+    float t0 = (-B - discriminant) * 0.5f;
+    float t1 = (-B + discriminant) * 0.5f;
+
+    if (t0 > t1) {
+        float temp = t0;
+        t0 = t1;
+        t1 = temp;
+    }
+
+    // The sphere is behind us
+    if (t1 < 0.0f) {
+        return false;
+    }
+    return true;
+}
+
+
+#endif // _TRANSFORM_DEF_
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
new file mode 100644
index 0000000..941b5a8
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
@@ -0,0 +1,127 @@
+// Copyright (C) 2011 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.modelviewer)
+
+#include "scenegraph_objects.rsh"
+
+rs_script gTransformScript;
+
+typedef struct {
+    int changed;
+    rs_matrix4x4 *mat;
+} ParentData;
+
+//#define DEBUG_TRANSFORMS
+static void debugTransform(SgTransform *data, const ParentData *parent) {
+    rsDebug("****** <Transform> ******", (int)data);
+    printName(data->name);
+    rsDebug("isDirty", data->isDirty);
+    rsDebug("parent", (int)parent);
+    rsDebug("child ", rsIsObject(data->children));
+
+    // Refresh matrices if dirty
+    if (data->isDirty && rsIsObject(data->components)) {
+        uint32_t numComponenets = rsAllocationGetDimX(data->components);
+        for (int i = 0; i < numComponenets; i ++) {
+            const SgTransformComponent *comp = NULL;
+            comp = (const SgTransformComponent *)rsGetElementAt(data->components, i);
+
+            if (rsIsObject(comp->name)) {
+                rsDebug((const char*)rsGetElementAt(comp->name, 0), comp->value);
+                rsDebug("Type", comp->type);
+            } else {
+                rsDebug("no name", comp->value);
+                rsDebug("Type", comp->type);
+            }
+        }
+    }
+
+    rsDebug("timestamp", data->timestamp);
+    rsDebug("****** </Transform> ******", (int)data);
+}
+
+static void appendTransformation(int type, float4 data, rs_matrix4x4 *mat) {
+    rs_matrix4x4 temp;
+
+    switch (type) {
+    case TRANSFORM_TRANSLATE:
+        rsMatrixLoadTranslate(&temp, data.x, data.y, data.z);
+        break;
+    case TRANSFORM_ROTATE:
+        rsMatrixLoadRotate(&temp, data.w, data.x, data.y, data.z);
+        break;
+    case TRANSFORM_SCALE:
+        rsMatrixLoadScale(&temp, data.x, data.y, data.z);
+        break;
+    }
+    rsMatrixMultiply(mat, &temp);
+}
+
+void root(const rs_allocation *v_in, rs_allocation *v_out, const void *usrData) {
+
+    SgTransform *data = (SgTransform *)rsGetElementAt(*v_in, 0);
+    const ParentData *parent = (const ParentData *)usrData;
+
+#ifdef DEBUG_TRANSFORMS
+    debugTransform(data, parent);
+#endif //DEBUG_TRANSFORMS
+
+    rs_matrix4x4 *localMat = &data->localMat;
+    rs_matrix4x4 *globalMat = &data->globalMat;
+
+    // Refresh matrices if dirty
+    if (data->isDirty && rsIsObject(data->components)) {
+        bool resetLocal = false;
+        uint32_t numComponenets = rsAllocationGetDimX(data->components);
+        for (int i = 0; i < numComponenets; i ++) {
+            if (!resetLocal) {
+                // Reset our local matrix only for component transforms
+                rsMatrixLoadIdentity(localMat);
+                resetLocal = true;
+            }
+            const SgTransformComponent *comp = NULL;
+            comp = (const SgTransformComponent *)rsGetElementAt(data->components, i);
+            appendTransformation(comp->type, comp->value, localMat);
+        }
+    }
+
+    if (parent) {
+        data->isDirty = (parent->changed || data->isDirty) ? 1 : 0;
+        if (data->isDirty) {
+            rsMatrixLoad(globalMat, parent->mat);
+            rsMatrixMultiply(globalMat, localMat);
+        }
+    } else if (data->isDirty) {
+        rsMatrixLoad(globalMat, localMat);
+    }
+
+    ParentData toChild;
+    toChild.changed = 0;
+    toChild.mat = globalMat;
+
+    if (data->isDirty) {
+        toChild.changed = 1;
+        data->timestamp ++;
+    }
+
+    if (rsIsObject(data->children)) {
+        rs_allocation nullAlloc;
+        rsForEach(gTransformScript, data->children, nullAlloc, &toChild, sizeof(toChild));
+    }
+
+    data->isDirty = 0;
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs
new file mode 100644
index 0000000..88955a8
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs
@@ -0,0 +1,29 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+//#define DEBUG_PARAMS
+
+#include "params.rsh"
+
+void root(rs_allocation *v_out, const void *usrData) {
+    SgVertexShader *shader = (SgVertexShader *)rsGetElementAt(*v_out, 0);
+    const SgCamera *camera = (const SgCamera*)usrData;
+    processAllParams(shader->shaderConst, shader->shaderConstParams, camera);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java
new file mode 100644
index 0000000..420e133
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 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.testapp;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+ * A list view where the last item the user clicked is placed in
+ * the "activated" state, causing its background to highlight.
+ */
+public class FileSelector extends ListActivity {
+
+    File[] mCurrentSubList;
+    File mCurrentFile;
+
+    class DAEFilter implements FileFilter {
+        public boolean accept(File file) {
+            if (file.isDirectory()) {
+                return true;
+            }
+            return file.getName().endsWith(".dae");
+        }
+    }
+
+    private void populateList(File file) {
+
+        mCurrentFile = file;
+        setTitle(mCurrentFile.getAbsolutePath() + "/*.dae");
+        List<String> names = new ArrayList<String>();
+        names.add("..");
+
+        mCurrentSubList = mCurrentFile.listFiles(new DAEFilter());
+
+        if (mCurrentSubList != null) {
+            for (int i = 0; i < mCurrentSubList.length; i ++) {
+                String fileName = mCurrentSubList[i].getName();
+                if (mCurrentSubList[i].isDirectory()) {
+                    fileName = "/" + fileName;
+                }
+                names.add(fileName);
+            }
+        }
+
+        // Use the built-in layout for showing a list item with a single
+        // line of text whose background is changes when activated.
+        setListAdapter(new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_activated_1, names));
+        getListView().setTextFilterEnabled(true);
+
+        // Tell the list view to show one checked/activated item at a time.
+        getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        populateList(new File("/sdcard/"));
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        if (position == 0) {
+            File parent = mCurrentFile.getParentFile();
+            if (parent == null) {
+                return;
+            }
+            populateList(parent);
+            return;
+        }
+
+        // the first thing in list is parent directory
+        File selectedFile = mCurrentSubList[position - 1];
+        if (selectedFile.isDirectory()) {
+            populateList(selectedFile);
+            return;
+        }
+
+        Intent resultIntent = new Intent();
+        resultIntent.setData(Uri.fromFile(selectedFile));
+        setResult(RESULT_OK, resultIntent);
+        finish();
+    }
+
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java
new file mode 100644
index 0000000..28f916c
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java
@@ -0,0 +1,192 @@
+/*

+ * Copyright (C) 2011 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.testapp;

+

+import java.util.ArrayList;

+

+import com.android.scenegraph.*;

+

+import android.content.res.Resources;

+import android.graphics.Bitmap;

+import android.graphics.BitmapFactory;

+import android.os.AsyncTask;

+import android.renderscript.*;

+import android.renderscript.Allocation.MipmapControl;

+import android.renderscript.Element.Builder;

+import android.renderscript.Font.Style;

+import android.renderscript.Program.TextureType;

+import android.renderscript.ProgramStore.DepthFunc;

+import android.util.Log;

+

+class FullscreenBlur {

+

+    static TextureRenderTarget sRenderTargetBlur0Color;

+    static TextureRenderTarget sRenderTargetBlur0Depth;

+    static TextureRenderTarget sRenderTargetBlur1Color;

+    static TextureRenderTarget sRenderTargetBlur1Depth;

+    static TextureRenderTarget sRenderTargetBlur2Color;

+    static TextureRenderTarget sRenderTargetBlur2Depth;

+

+    static FragmentShader mPF_BlurH;

+    static FragmentShader mPF_BlurV;

+    static FragmentShader mPF_SelectColor;

+    static FragmentShader mPF_Texture;

+    static VertexShader mPV_Paint;

+    static VertexShader mPV_Blur;

+

+    static int targetWidth;

+    static int targetHeight;

+

+    // This is only used when full screen blur is enabled

+    // Basically, it's the offscreen render targets

+    static void createRenderTargets(RenderScriptGL rs, int w, int h) {

+        targetWidth = w/8;

+        targetHeight = h/8;

+        Type.Builder b = new Type.Builder(rs, Element.RGBA_8888(rs));

+        Type renderType = b.setX(targetWidth).setY(targetHeight).create();

+        int usage = Allocation.USAGE_GRAPHICS_TEXTURE | Allocation.USAGE_GRAPHICS_RENDER_TARGET;

+        sRenderTargetBlur0Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+        sRenderTargetBlur1Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+        sRenderTargetBlur2Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+

+        b = new Type.Builder(rs, Element.createPixel(rs, Element.DataType.UNSIGNED_16,

+                                                     Element.DataKind.PIXEL_DEPTH));

+        renderType = b.setX(targetWidth).setY(targetHeight).create();

+        usage = Allocation.USAGE_GRAPHICS_RENDER_TARGET;

+        sRenderTargetBlur0Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+        sRenderTargetBlur1Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+        sRenderTargetBlur2Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+    }

+

+    static void addOffsets(Renderable quad, float advance) {

+        quad.appendSourceParams(new Float4Param("blurOffset0", - advance * 2.5f));

+        quad.appendSourceParams(new Float4Param("blurOffset1", - advance * 0.5f));

+        quad.appendSourceParams(new Float4Param("blurOffset2", advance * 1.5f));

+        quad.appendSourceParams(new Float4Param("blurOffset3", advance * 3.5f));

+    }

+

+    static RenderPass addPass(Scene scene, Camera cam, TextureRenderTarget color, TextureRenderTarget depth) {

+        RenderPass pass = new RenderPass();

+        pass.setColorTarget(color);

+        pass.setDepthTarget(depth);

+        pass.setShouldClearColor(false);

+        pass.setShouldClearDepth(false);

+        pass.setCamera(cam);

+        scene.appendRenderPass(pass);

+        return pass;

+    }

+

+    static void addBlurPasses(Scene scene, RenderScriptGL rs, Camera cam) {

+        SceneManager sceneManager = SceneManager.getInstance();

+        ArrayList<RenderableBase> allDraw = scene.getRenderables();

+        int numDraw = allDraw.size();

+

+        ProgramRaster cullNone = ProgramRaster.CULL_NONE(rs);

+        ProgramStore blendAdd = SceneManager.BLEND_ADD_DEPTH_NONE(rs);

+        ProgramStore blendNone = ProgramStore.BLEND_NONE_DEPTH_NONE(rs);

+

+        RenderState drawTex = new RenderState(mPV_Blur, mPF_Texture, blendAdd, cullNone);

+        RenderState selectCol = new RenderState(mPV_Blur, mPF_SelectColor, blendNone, cullNone);

+        RenderState hBlur = new RenderState(mPV_Blur, mPF_BlurH, blendNone, cullNone);

+        RenderState vBlur = new RenderState(mPV_Blur, mPF_BlurV, blendNone, cullNone);

+

+        // Renders the scene off screen

+        RenderPass blurSourcePass = addPass(scene, cam,

+                                            sRenderTargetBlur0Color,

+                                            sRenderTargetBlur0Depth);

+        blurSourcePass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));

+        blurSourcePass.setShouldClearColor(true);

+        blurSourcePass.setClearDepth(1.0f);

+        blurSourcePass.setShouldClearDepth(true);

+        for (int i = 0; i < numDraw; i ++) {

+            blurSourcePass.appendRenderable((Renderable)allDraw.get(i));

+        }

+

+        // Pass for selecting bright colors

+        RenderPass selectColorPass = addPass(scene, cam,

+                                             sRenderTargetBlur2Color,

+                                             sRenderTargetBlur2Depth);

+        Renderable quad = sceneManager.getRenderableQuad("ScreenAlignedQuadS", selectCol);

+        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur0Color));

+        selectColorPass.appendRenderable(quad);

+

+        // Horizontal blur

+        RenderPass horizontalBlurPass = addPass(scene, cam,

+                                                sRenderTargetBlur1Color,

+                                                sRenderTargetBlur1Depth);

+        quad = sceneManager.getRenderableQuad("ScreenAlignedQuadH", hBlur);

+        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur2Color));

+        addOffsets(quad, 1.0f / (float)targetWidth);

+        horizontalBlurPass.appendRenderable(quad);

+

+        // Vertical Blur

+        RenderPass verticalBlurPass = addPass(scene, cam,

+                                              sRenderTargetBlur2Color,

+                                              sRenderTargetBlur2Depth);

+        quad = sceneManager.getRenderableQuad("ScreenAlignedQuadV", vBlur);

+        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur1Color));

+        addOffsets(quad, 1.0f / (float)targetHeight);

+        verticalBlurPass.appendRenderable(quad);

+    }

+

+    // Additively renders the blurred colors on top of the scene

+    static void addCompositePass(Scene scene, RenderScriptGL rs, Camera cam) {

+        SceneManager sceneManager = SceneManager.getInstance();

+        RenderState drawTex = new RenderState(mPV_Blur, mPF_Texture,

+                                              SceneManager.BLEND_ADD_DEPTH_NONE(rs),

+                                              ProgramRaster.CULL_NONE(rs));

+

+        RenderPass compositePass = addPass(scene, cam, null, null);

+        Renderable quad = sceneManager.getRenderableQuad("ScreenAlignedQuadComposite", drawTex);

+        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur2Color));

+        compositePass.appendRenderable(quad);

+    }

+

+    static private FragmentShader getShader(Resources res, RenderScriptGL rs,

+                                            int resID, Type constants) {

+        FragmentShader.Builder fb = new FragmentShader.Builder(rs);

+        fb.setShader(res, resID);

+        fb.addTexture(TextureType.TEXTURE_2D, "color");

+        if (constants != null) {

+            fb.setObjectConst(constants);

+        }

+        FragmentShader prog = fb.create();

+        prog.getProgram().bindSampler(Sampler.CLAMP_LINEAR(rs), 0);

+        return prog;

+    }

+

+    static void initShaders(Resources res, RenderScriptGL rs) {

+        ScriptField_BlurOffsets blurConst = new ScriptField_BlurOffsets(rs, 1);

+        VertexShader.Builder vb = new VertexShader.Builder(rs);

+        vb.addInput(ScriptField_VertexShaderInputs.createElement(rs));

+        vb.setShader(res, R.raw.blur_vertex);

+        mPV_Blur = vb.create();

+

+        mPF_Texture = getShader(res, rs, R.raw.texture, null);

+        mPF_Texture.getProgram().bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(rs), 0);

+        mPF_BlurH = getShader(res, rs, R.raw.blur_h, blurConst.getAllocation().getType());

+        mPF_BlurV = getShader(res, rs, R.raw.blur_v, blurConst.getAllocation().getType());

+        mPF_SelectColor = getShader(res, rs, R.raw.select_color, null);

+    }

+

+}

+

+

+

+

+

diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java
new file mode 100644
index 0000000..385a7ab
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 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.testapp;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+import android.view.MenuInflater;
+import android.view.Window;
+import android.net.Uri;
+
+import java.lang.Runtime;
+
+public class TestApp extends Activity {
+
+    private TestAppView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new TestAppView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.resume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.pause();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.loader_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle item selection
+        switch (item.getItemId()) {
+        case R.id.load_model:
+            loadModel();
+            return true;
+        case R.id.use_blur:
+            mView.mRender.toggleBlur();
+            return true;
+        default:
+            return super.onOptionsItemSelected(item);
+        }
+    }
+
+    private static final int FIND_DAE_MODEL = 10;
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode == RESULT_OK) {
+            if (requestCode == FIND_DAE_MODEL) {
+                Uri selectedImageUri = data.getData();
+                Log.e("Selected Path: ", selectedImageUri.getPath());
+                mView.mRender.loadModel(selectedImageUri.getPath());
+            }
+        }
+    }
+
+    public void loadModel() {
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_PICK);
+        intent.setClassName("com.android.testapp",
+                            "com.android.testapp.FileSelector");
+        startActivityForResult(intent, FIND_DAE_MODEL);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java
new file mode 100644
index 0000000..5bd8f0b
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 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.testapp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import com.android.scenegraph.SceneManager;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Allocation.MipmapControl;
+import android.renderscript.Element.Builder;
+import android.renderscript.Font.Style;
+import android.renderscript.Program.TextureType;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+// This is where the scenegraph and the rendered objects are initialized and used
+public class TestAppLoadingScreen {
+
+    private static String TAG = "TestAppLoadingScreen";
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private ScriptC_test_app mScript;
+
+    public TestAppLoadingScreen(RenderScriptGL rs, Resources res) {
+        mRS = rs;
+        mRes = res;
+        // Shows the loading screen with some text
+        renderLoading();
+        // Adds a little 3D bugdroid model to the laoding screen asynchronously.
+        new LoadingScreenLoaderTask().execute();
+    }
+
+    public void showLoadingScreen(boolean show) {
+        if (show) {
+            mRS.bindRootScript(mScript);
+        } else {
+            mRS.bindRootScript(SceneManager.getInstance().getRenderLoop());
+        }
+    }
+
+    // The loading screen has some elements that shouldn't be loaded on the UI thread
+    private class LoadingScreenLoaderTask extends AsyncTask<String, Void, Boolean> {
+        Allocation robotTex;
+        Mesh robotMesh;
+        protected Boolean doInBackground(String... names) {
+            long start = System.currentTimeMillis();
+            robotTex = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
+                                                           MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                                           Allocation.USAGE_GRAPHICS_TEXTURE);
+
+            FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
+            FileA3D.IndexEntry entry = model.getIndexEntry(0);
+            if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
+                robotMesh = entry.getMesh();
+            }
+
+            mScript.set_gPFSBackground(ProgramStore.BLEND_NONE_DEPTH_TEST(mRS));
+
+            ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
+            b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
+                         ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
+            ProgramFragment pfDefault = b.create();
+            pfDefault.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
+            mScript.set_gPFBackground(pfDefault);
+
+            ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
+            ProgramVertexFixedFunction pvDefault = pvb.create();
+            ProgramVertexFixedFunction.Constants va = new ProgramVertexFixedFunction.Constants(mRS);
+            ((ProgramVertexFixedFunction)pvDefault).bindConstants(va);
+            mScript.set_gPVBackground(pvDefault);
+
+            long end = System.currentTimeMillis();
+            Log.v("TIMER", "Loading load time: " + (end - start));
+            return new Boolean(true);
+        }
+
+        protected void onPostExecute(Boolean result) {
+            mScript.set_gRobotTex(robotTex);
+            mScript.set_gRobotMesh(robotMesh);
+        }
+    }
+
+    // Creates a simple script to show a loding screen until everything is initialized
+    // Could also be used to do some custom renderscript work before handing things over
+    // to the scenegraph
+    void renderLoading() {
+        mScript = new ScriptC_test_app(mRS, mRes, R.raw.test_app);
+        mRS.bindRootScript(mScript);
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
new file mode 100644
index 0000000..7bf7812
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011-2012 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.testapp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import com.android.scenegraph.*;
+import com.android.scenegraph.SceneManager.SceneLoadedCallback;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Program.TextureType;
+import android.util.Log;
+
+// This is where the scenegraph and the rendered objects are initialized and used
+public class TestAppRS {
+
+    private static String modelName = "orientation_test.dae";
+    private static String TAG = "TestAppRS";
+    private static String mFilePath = "";
+
+    int mWidth;
+    int mHeight;
+
+    boolean mUseBlur;
+
+    TestAppLoadingScreen mLoadingScreen;
+
+    // Used to asynchronously load scene elements like meshes and transform hierarchies
+    SceneLoadedCallback mLoadedCallback = new SceneLoadedCallback() {
+        public void run() {
+            prepareToRender(mLoadedScene);
+        }
+    };
+
+    // Top level class that initializes all the elements needed to use the scene graph
+    SceneManager mSceneManager;
+
+    // Used to move the camera around in the 3D world
+    TouchHandler mTouchHandler;
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+
+    // Shaders
+    private FragmentShader mPaintF;
+    private FragmentShader mLightsF;
+    private FragmentShader mAluminumF;
+    private FragmentShader mPlasticF;
+    private FragmentShader mDiffuseF;
+    private FragmentShader mTextureF;
+    private VertexShader mGenericV;
+
+    Scene mActiveScene;
+
+    // This is a part of the test app, it's used to tests multiple render passes and is toggled
+    // on and off in the menu, off by default
+    void toggleBlur() {
+        mUseBlur = !mUseBlur;
+
+        mActiveScene.clearRenderPasses();
+        initRenderPasses();
+        mActiveScene.initRenderPassRS(mRS, mSceneManager);
+
+        // This is just a hardcoded object in the scene that gets turned on and off for the demo
+        // to make things look a bit better. This could be deleted in the cleanup
+        Renderable plane = (Renderable)mActiveScene.getRenderableByName("pPlaneShape1");
+        if (plane != null) {
+            plane.setVisible(!mUseBlur);
+        }
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mUseBlur = false;
+        mRS = rs;
+        mRes = res;
+        mWidth = width;
+        mHeight = height;
+
+        mTouchHandler = new TouchHandler();
+
+        mSceneManager = SceneManager.getInstance();
+        // Initializes all the RS specific scenegraph elements
+        mSceneManager.initRS(mRS, mRes, mWidth, mHeight);
+
+        mLoadingScreen = new TestAppLoadingScreen(mRS, mRes);
+
+        // Initi renderscript stuff specific to the app. This will need to be abstracted out later.
+        FullscreenBlur.createRenderTargets(mRS, mWidth, mHeight);
+        initPaintShaders();
+
+        // Load a scene to render
+        mSceneManager.loadModel(mFilePath + modelName, mLoadedCallback);
+    }
+
+    // When a new model file is selected from the UI, this function gets called to init everything
+    void loadModel(String path) {
+        mLoadingScreen.showLoadingScreen(true);
+        mActiveScene.destroyRS();
+        mSceneManager.loadModel(path, mLoadedCallback);
+    }
+
+    public void onActionDown(float x, float y) {
+        mTouchHandler.onActionDown(x, y);
+    }
+
+    public void onActionScale(float scale) {
+        mTouchHandler.onActionScale(scale);
+    }
+
+    public void onActionMove(float x, float y) {
+        mTouchHandler.onActionMove(x, y);
+    }
+
+    FragmentShader createFromResource(int id, boolean addCubemap, Type constType) {
+        FragmentShader.Builder fb = new FragmentShader.Builder(mRS);
+        fb.setShaderConst(constType);
+        fb.setShader(mRes, id);
+        fb.addTexture(TextureType.TEXTURE_2D, "diffuse");
+        if (addCubemap) {
+            fb.addShaderTexture(TextureType.TEXTURE_CUBE, "reflection");
+        }
+        FragmentShader pf = fb.create();
+        pf.getProgram().bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS), 0);
+        if (addCubemap) {
+            pf.getProgram().bindSampler(Sampler.CLAMP_LINEAR_MIP_LINEAR(mRS), 1);
+        }
+        return pf;
+    }
+
+    private void initPaintShaders() {
+        ScriptField_ModelParams objConst = new ScriptField_ModelParams(mRS, 1);
+        ScriptField_ViewProjParams shaderConst = new ScriptField_ViewProjParams(mRS, 1);
+
+        VertexShader.Builder vb = new VertexShader.Builder(mRS);
+        vb.addInput(ScriptField_VertexShaderInputs.createElement(mRS));
+        vb.setShader(mRes, R.raw.shader2v);
+        vb.setObjectConst(objConst.getAllocation().getType());
+        vb.setShaderConst(shaderConst.getAllocation().getType());
+        mGenericV = vb.create();
+
+        ScriptField_CameraParams fsConst = new ScriptField_CameraParams(mRS, 1);
+        ScriptField_LightParams fsConst2 = new ScriptField_LightParams(mRS, 1);
+
+        mPaintF = createFromResource(R.raw.paintf, true, fsConst.getAllocation().getType());
+        // Assign a reflection map
+        TextureCube envCube = new TextureCube("sdcard/scenegraph/", "cube_env.png");
+        mPaintF.appendSourceParams(new TextureParam("reflection", envCube));
+
+        mAluminumF = createFromResource(R.raw.metal, true, fsConst.getAllocation().getType());
+        TextureCube diffCube = new TextureCube("sdcard/scenegraph/", "cube_spec.png");
+        mAluminumF.appendSourceParams(new TextureParam("reflection", diffCube));
+
+        mPlasticF = createFromResource(R.raw.plastic, false, fsConst.getAllocation().getType());
+        mDiffuseF = createFromResource(R.raw.diffuse, false, fsConst.getAllocation().getType());
+        mTextureF = createFromResource(R.raw.texture, false, fsConst.getAllocation().getType());
+
+        FragmentShader.Builder fb = new FragmentShader.Builder(mRS);
+        fb.setObjectConst(fsConst2.getAllocation().getType());
+        fb.setShader(mRes, R.raw.plastic_lights);
+        mLightsF = fb.create();
+
+        FullscreenBlur.initShaders(mRes, mRS);
+    }
+
+    void initRenderPasses() {
+        ArrayList<RenderableBase> allDraw = mActiveScene.getRenderables();
+        int numDraw = allDraw.size();
+
+        if (mUseBlur) {
+            FullscreenBlur.addBlurPasses(mActiveScene, mRS, mTouchHandler.getCamera());
+        }
+
+        RenderPass mainPass = new RenderPass();
+        mainPass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));
+        mainPass.setShouldClearColor(true);
+        mainPass.setClearDepth(1.0f);
+        mainPass.setShouldClearDepth(true);
+        mainPass.setCamera(mTouchHandler.getCamera());
+        for (int i = 0; i < numDraw; i ++) {
+            mainPass.appendRenderable((Renderable)allDraw.get(i));
+        }
+        mActiveScene.appendRenderPass(mainPass);
+
+        if (mUseBlur) {
+            FullscreenBlur.addCompositePass(mActiveScene, mRS, mTouchHandler.getCamera());
+        }
+    }
+
+    private void addShadersToScene() {
+        mActiveScene.appendShader(mPaintF);
+        mActiveScene.appendShader(mLightsF);
+        mActiveScene.appendShader(mAluminumF);
+        mActiveScene.appendShader(mPlasticF);
+        mActiveScene.appendShader(mDiffuseF);
+        mActiveScene.appendShader(mTextureF);
+        mActiveScene.appendShader(mGenericV);
+    }
+
+    public void prepareToRender(Scene s) {
+        mSceneManager.setActiveScene(s);
+        mActiveScene = s;
+        mTouchHandler.init(mActiveScene);
+        addShadersToScene();
+        RenderState plastic = new RenderState(mGenericV, mPlasticF, null, null);
+        RenderState diffuse = new RenderState(mGenericV, mDiffuseF, null, null);
+        RenderState paint = new RenderState(mGenericV, mPaintF, null, null);
+        RenderState aluminum = new RenderState(mGenericV, mAluminumF, null, null);
+        RenderState lights = new RenderState(mGenericV, mLightsF, null, null);
+        RenderState glassTransp = new RenderState(mGenericV, mPaintF,
+                                                  ProgramStore.BLEND_ALPHA_DEPTH_TEST(mRS), null);
+
+        initRenderPasses();
+
+        mActiveScene.assignRenderState(plastic);
+
+        mActiveScene.assignRenderStateToMaterial(diffuse, "lambert2$");
+
+        mActiveScene.assignRenderStateToMaterial(paint, "^Paint");
+        mActiveScene.assignRenderStateToMaterial(paint, "^Carbon");
+        mActiveScene.assignRenderStateToMaterial(paint, "^Glass");
+        mActiveScene.assignRenderStateToMaterial(paint, "^MainGlass");
+
+        mActiveScene.assignRenderStateToMaterial(aluminum, "^Metal");
+        mActiveScene.assignRenderStateToMaterial(aluminum, "^Brake");
+
+        mActiveScene.assignRenderStateToMaterial(glassTransp, "^GlassLight");
+
+        mActiveScene.assignRenderStateToMaterial(lights, "^LightBlinn");
+
+        Renderable plane = (Renderable)mActiveScene.getRenderableByName("pPlaneShape1");
+        if (plane != null) {
+            RenderState texState = new RenderState(mGenericV, mTextureF, null, null);
+            plane.setRenderState(texState);
+            plane.setVisible(!mUseBlur);
+        }
+
+        long start = System.currentTimeMillis();
+        mActiveScene.initRS();
+        long end = System.currentTimeMillis();
+        Log.v("TIMER", "Scene init time: " + (end - start));
+
+        mLoadingScreen.showLoadingScreen(false);
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java
new file mode 100644
index 0000000..687f35b
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2011 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.testapp;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+public class TestAppView extends RSSurfaceView {
+
+    public TestAppView(Context context) {
+        super(context);
+        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
+    }
+
+    private RenderScriptGL mRS;
+    TestAppRS mRender;
+
+    private ScaleGestureDetector mScaleDetector;
+    private static final int INVALID_POINTER_ID = -1;
+    private int mActivePointerId = INVALID_POINTER_ID;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            sc.setDepth(16, 24);
+            mRS = createRenderScriptGL(sc);
+            mRS.setSurface(holder, w, h);
+            mRender = new TestAppRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if (mRS != null) {
+            mRender = null;
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        // break point at here
+        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        mScaleDetector.onTouchEvent(ev);
+
+        boolean ret = false;
+        float x = ev.getX();
+        float y = ev.getY();
+
+        final int action = ev.getAction();
+
+        switch (action & MotionEvent.ACTION_MASK) {
+        case MotionEvent.ACTION_DOWN: {
+            mRender.onActionDown(x, y);
+            mActivePointerId = ev.getPointerId(0);
+            ret = true;
+            break;
+        }
+        case MotionEvent.ACTION_MOVE: {
+            if (!mScaleDetector.isInProgress()) {
+                mRender.onActionMove(x, y);
+            }
+            mRender.onActionDown(x, y);
+            ret = true;
+            break;
+        }
+
+        case MotionEvent.ACTION_UP: {
+            mActivePointerId = INVALID_POINTER_ID;
+            break;
+        }
+
+        case MotionEvent.ACTION_CANCEL: {
+            mActivePointerId = INVALID_POINTER_ID;
+            break;
+        }
+
+        case MotionEvent.ACTION_POINTER_UP: {
+            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
+                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+            final int pointerId = ev.getPointerId(pointerIndex);
+            if (pointerId == mActivePointerId) {
+                // This was our active pointer going up. Choose a new
+                // active pointer and adjust accordingly.
+                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+                x = ev.getX(newPointerIndex);
+                y = ev.getY(newPointerIndex);
+                mRender.onActionDown(x, y);
+                mActivePointerId = ev.getPointerId(newPointerIndex);
+            }
+            break;
+        }
+        }
+
+        return ret;
+    }
+
+    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+        @Override
+        public boolean onScale(ScaleGestureDetector detector) {
+            mRender.onActionScale(detector.getScaleFactor());
+            return true;
+        }
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
new file mode 100644
index 0000000..d8e48e8
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 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.testapp;
+
+import android.util.Log;
+import android.renderscript.Float3;
+import com.android.scenegraph.*;
+import com.android.scenegraph.CompoundTransform.RotateComponent;
+import com.android.scenegraph.CompoundTransform.TranslateComponent;
+
+public class TouchHandler {
+    private static String TAG = "TouchHandler";
+
+    float mLastX;
+    float mLastY;
+
+    float mRotateXValue;
+    float mRotateYValue;
+    Float3 mDistValue;
+    Float3 mPosValue;
+
+    CompoundTransform mCameraRig;
+    RotateComponent mRotateX;
+    RotateComponent mRotateY;
+    TranslateComponent mDist;
+    TranslateComponent mPosition;
+    Camera mCamera;
+
+    public void init(Scene scene) {
+        // Some initial values for camera position
+        mRotateXValue = -20;
+        mRotateYValue = 45;
+        mDistValue = new Float3(0, 0, 45);
+        mPosValue = new Float3(0, 4, 0);
+
+        mRotateX = new RotateComponent("RotateX", new Float3(1, 0, 0), mRotateXValue);
+        mRotateY = new RotateComponent("RotateY", new Float3(0, 1, 0), mRotateYValue);
+        mDist = new TranslateComponent("Distance", mDistValue);
+        mPosition = new TranslateComponent("Distance", mPosValue);
+
+        // Make a camera transform we can manipulate
+        mCameraRig = new CompoundTransform();
+        mCameraRig.setName("CameraRig");
+        mCameraRig.addComponent(mPosition);
+        mCameraRig.addComponent(mRotateY);
+        mCameraRig.addComponent(mRotateX);
+        mCameraRig.addComponent(mDist);
+        scene.appendTransform(mCameraRig);
+        mCamera = new Camera();
+        mCamera.setTransform(mCameraRig);
+        scene.appendCamera(mCamera);
+    }
+
+    public Camera getCamera() {
+        return mCamera;
+    }
+
+    public void onActionDown(float x, float y) {
+        mLastX = x;
+        mLastY = y;
+    }
+
+    public void onActionScale(float scale) {
+        if (mDist == null) {
+            return;
+        }
+        mDistValue.z *= 1.0f / scale;
+        mDistValue.z = Math.max(10.0f, Math.min(mDistValue.z, 150.0f));
+        mDist.setValue(mDistValue);
+    }
+
+    public void onActionMove(float x, float y) {
+        if (mRotateX == null) {
+            return;
+        }
+
+        float dx = mLastX - x;
+        float dy = mLastY - y;
+
+        if (Math.abs(dy) <= 2.0f) {
+            dy = 0.0f;
+        }
+        if (Math.abs(dx) <= 2.0f) {
+            dx = 0.0f;
+        }
+
+        mRotateYValue += dx * 0.25f;
+        mRotateYValue %= 360.0f;
+
+        mRotateXValue  += dy * 0.25f;
+        mRotateXValue  = Math.max(mRotateXValue , -80.0f);
+        mRotateXValue  = Math.min(mRotateXValue , 0.0f);
+
+        mRotateX.setAngle(mRotateXValue);
+        mRotateY.setAngle(mRotateYValue);
+
+        mLastX = x;
+        mLastY = y;
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
new file mode 100644
index 0000000..997a1a7
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
@@ -0,0 +1,86 @@
+// Copyright (C) 2011 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.testapp)
+
+#include "rs_graphics.rsh"
+#include "test_app.rsh"
+
+// Making sure these get reflected
+FBlurOffsets *blurExport;
+VShaderInputs *iExport;
+FShaderParams *fConst;
+FShaderLightParams *fConts2;
+VSParams *vConst2;
+VObjectParams *vConst3;
+
+rs_program_vertex gPVBackground;
+rs_program_fragment gPFBackground;
+
+rs_allocation gRobotTex;
+rs_mesh gRobotMesh;
+
+rs_program_store gPFSBackground;
+
+float gRotate;
+
+void init() {
+    gRotate = 0.0f;
+}
+
+static int pos = 50;
+static float gRotateY = 120.0f;
+static float3 gLookAt = 0;
+static float gZoom = 50.0f;
+static void displayLoading() {
+    if (rsIsObject(gRobotTex) && rsIsObject(gRobotMesh)) {
+        rsgBindProgramVertex(gPVBackground);
+        rs_matrix4x4 proj;
+        float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+        rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
+        rsgProgramVertexLoadProjectionMatrix(&proj);
+
+        rsgBindProgramFragment(gPFBackground);
+        rsgBindProgramStore(gPFSBackground);
+        rsgBindTexture(gPFBackground, 0, gRobotTex);
+
+        rs_matrix4x4 matrix;
+        rsMatrixLoadIdentity(&matrix);
+        // Position our models on the screen
+        gRotateY += rsGetDt()*100;
+        rsMatrixTranslate(&matrix, 0, 0, -gZoom);
+        rsMatrixRotate(&matrix, 20.0f, 1.0f, 0.0f, 0.0f);
+        rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
+        rsMatrixScale(&matrix, 0.2f, 0.2f, 0.2f);
+        rsgProgramVertexLoadModelMatrix(&matrix);
+        rsgDrawMesh(gRobotMesh);
+    }
+
+    uint width = rsgGetWidth();
+    uint height = rsgGetHeight();
+    int left = 0, right = 0, top = 0, bottom = 0;
+    const char* text = "Initializing...";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    int centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(text, centeredPos, height / 2 + height / 10);
+}
+
+int root(void) {
+    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgClearDepth(1.0f);
+    displayLoading();
+    return 30;
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh
new file mode 100644
index 0000000..5fbcbb2
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh
@@ -0,0 +1,52 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.testapp)
+
+// Helpers
+typedef struct ViewProjParams {
+    rs_matrix4x4 viewProj;
+} VSParams;
+
+typedef struct ModelParams {
+    rs_matrix4x4 model;
+} VObjectParams;
+
+typedef struct CameraParams {
+    float4 cameraPos;
+} FShaderParams;
+
+typedef struct LightParams {
+    float4 lightPos_0;
+    float4 lightColor_0;
+    float4 lightPos_1;
+    float4 lightColor_1;
+    float4 cameraPos;
+    float4 diffuse;
+} FShaderLightParams;
+
+typedef struct BlurOffsets {
+    float blurOffset0;
+    float blurOffset1;
+    float blurOffset2;
+    float blurOffset3;
+} FBlurOffsets;
+
+typedef struct VertexShaderInputs {
+    float4 position;
+    float3 normal;
+    float2 texture0;
+} VShaderInputs;
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
index eaada5f..c7bd809 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
@@ -64,6 +64,11 @@
 
         unitTests = new ArrayList<UnitTest>();
 
+        unitTests.add(new UT_mesh(this, mRes, mCtx));
+        unitTests.add(new UT_element(this, mRes, mCtx));
+        unitTests.add(new UT_sampler(this, mRes, mCtx));
+        unitTests.add(new UT_program_store(this, mRes, mCtx));
+        unitTests.add(new UT_program_raster(this, mRes, mCtx));
         unitTests.add(new UT_primitives(this, mRes, mCtx));
         unitTests.add(new UT_constant(this, mRes, mCtx));
         unitTests.add(new UT_vector(this, mRes, mCtx));
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_element.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_element.java
new file mode 100644
index 0000000..3e2a2ca
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_element.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 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.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Element.*;
+import android.renderscript.Element.DataKind.*;
+import android.renderscript.Element.DataType.*;
+
+public class UT_element extends UnitTest {
+    private Resources mRes;
+
+    Element simpleElem;
+    Element complexElem;
+
+    final String subElemNames[] = {
+        "subElem0",
+        "subElem1",
+        "subElem2",
+        "arrayElem0",
+        "arrayElem1",
+        "subElem3",
+        "subElem4",
+        "subElem5",
+        "subElem6",
+        "subElem_7",
+    };
+
+    final int subElemArraySizes[] = {
+        1,
+        1,
+        1,
+        2,
+        5,
+        1,
+        1,
+        1,
+        1,
+        1,
+    };
+
+    final int subElemOffsets[] = {
+        0,
+        4,
+        8,
+        12,
+        20,
+        40,
+        44,
+        48,
+        64,
+        80,
+    };
+
+    protected UT_element(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Element", ctx);
+        mRes = res;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_element s) {
+        simpleElem = Element.F32_3(RS);
+        complexElem = ScriptField_ComplexStruct.createElement(RS);
+        s.set_simpleElem(simpleElem);
+        s.set_complexElem(complexElem);
+
+        ScriptField_ComplexStruct data = new ScriptField_ComplexStruct(RS, 1);
+        s.bind_complexStruct(data);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_element s = new ScriptC_element(pRS, mRes, R.raw.element);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_element_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    private void testJavaSide(RenderScript RS) {
+
+        int subElemCount = simpleElem.getSubElementCount();
+        _RS_ASSERT("subElemCount == 0", subElemCount == 0);
+        _RS_ASSERT("simpleElem.getDataKind() == USER",
+                   simpleElem.getDataKind() == DataKind.USER);
+        _RS_ASSERT("simpleElem.getDataType() == FLOAT_32",
+                   simpleElem.getDataType() == DataType.FLOAT_32);
+
+        subElemCount = complexElem.getSubElementCount();
+        _RS_ASSERT("subElemCount == 10", subElemCount == 10);
+        _RS_ASSERT("complexElem.getDataKind() == USER",
+                   complexElem.getDataKind() == DataKind.USER);
+        _RS_ASSERT("complexElemsimpleElem.getDataType() == NONE",
+                   complexElem.getDataType() == DataType.NONE);
+        _RS_ASSERT("complexElem.getSizeBytes() == ScriptField_ComplexStruct.Item.sizeof",
+                   complexElem.getSizeBytes() == ScriptField_ComplexStruct.Item.sizeof);
+
+        for (int i = 0; i < subElemCount; i ++) {
+            _RS_ASSERT("complexElem.getSubElement(i) != null",
+                       complexElem.getSubElement(i) != null);
+            _RS_ASSERT("complexElem.getSubElementName(i).equals(subElemNames[i])",
+                       complexElem.getSubElementName(i).equals(subElemNames[i]));
+            _RS_ASSERT("complexElem.getSubElementArraySize(i) == subElemArraySizes[i]",
+                       complexElem.getSubElementArraySize(i) == subElemArraySizes[i]);
+            _RS_ASSERT("complexElem.getSubElementOffsetBytes(i) == subElemOffsets[i]",
+                       complexElem.getSubElementOffsetBytes(i) == subElemOffsets[i]);
+        }
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testScriptSide(pRS);
+        testJavaSide(pRS);
+        passTest();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_mesh.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_mesh.java
new file mode 100644
index 0000000..0c93702
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_mesh.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 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.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Mesh.*;
+
+public class UT_mesh extends UnitTest {
+    private Resources mRes;
+
+    Mesh mesh;
+
+    protected UT_mesh(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Mesh", ctx);
+        mRes = res;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_mesh s) {
+        Allocation vAlloc0 = Allocation.createSized(RS, Element.F32(RS), 10);
+        Allocation vAlloc1 = Allocation.createSized(RS, Element.F32_2(RS), 10);
+
+        Allocation iAlloc0 = Allocation.createSized(RS, Element.I16(RS), 10);
+        Allocation iAlloc2 = Allocation.createSized(RS, Element.I16(RS), 10);
+
+        Mesh.AllocationBuilder mBuilder = new Mesh.AllocationBuilder(RS);
+        mBuilder.addVertexAllocation(vAlloc0);
+        mBuilder.addVertexAllocation(vAlloc1);
+
+        mBuilder.addIndexSetAllocation(iAlloc0, Primitive.POINT);
+        mBuilder.addIndexSetType(Primitive.LINE);
+        mBuilder.addIndexSetAllocation(iAlloc2, Primitive.TRIANGLE);
+
+        s.set_mesh(mBuilder.create());
+        s.set_vertexAlloc0(vAlloc0);
+        s.set_vertexAlloc1(vAlloc1);
+        s.set_indexAlloc0(iAlloc0);
+        s.set_indexAlloc2(iAlloc2);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_mesh s = new ScriptC_mesh(pRS, mRes, R.raw.mesh);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_mesh_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    private void testJavaSide(RenderScript RS) {
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testScriptSide(pRS);
+        testJavaSide(pRS);
+        passTest();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_raster.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_raster.java
new file mode 100644
index 0000000..1de4d71
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_raster.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 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.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramRaster;
+import android.renderscript.ProgramRaster.CullMode;
+
+public class UT_program_raster extends UnitTest {
+    private Resources mRes;
+
+    ProgramRaster pointSpriteEnabled;
+    ProgramRaster cullMode;
+
+    protected UT_program_raster(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "ProgramRaster", ctx);
+        mRes = res;
+    }
+
+    private ProgramRaster.Builder getDefaultBuilder(RenderScript RS) {
+        ProgramRaster.Builder b = new ProgramRaster.Builder(RS);
+        b.setCullMode(CullMode.BACK);
+        b.setPointSpriteEnabled(false);
+        return b;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_program_raster s) {
+        ProgramRaster.Builder b = getDefaultBuilder(RS);
+        pointSpriteEnabled = b.setPointSpriteEnabled(true).create();
+        b = getDefaultBuilder(RS);
+        cullMode = b.setCullMode(CullMode.FRONT).create();
+
+        s.set_pointSpriteEnabled(pointSpriteEnabled);
+        s.set_cullMode(cullMode);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_program_raster s = new ScriptC_program_raster(pRS, mRes, R.raw.program_raster);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_program_raster_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    private void testJavaSide(RenderScript RS) {
+        _RS_ASSERT("pointSpriteEnabled.getPointSpriteEnabled() == true",
+                    pointSpriteEnabled.getPointSpriteEnabled() == true);
+        _RS_ASSERT("pointSpriteEnabled.getCullMode() == ProgramRaster.CullMode.BACK",
+                    pointSpriteEnabled.getCullMode() == ProgramRaster.CullMode.BACK);
+
+        _RS_ASSERT("cullMode.getPointSpriteEnabled() == false",
+                    cullMode.getPointSpriteEnabled() == false);
+        _RS_ASSERT("cullMode.getCullMode() == ProgramRaster.CullMode.FRONT",
+                    cullMode.getCullMode() == ProgramRaster.CullMode.FRONT);
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testScriptSide(pRS);
+        testJavaSide(pRS);
+        passTest();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_store.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_store.java
new file mode 100644
index 0000000..72a401d
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_store.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011 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.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramStore.BlendDstFunc;
+import android.renderscript.ProgramStore.BlendSrcFunc;
+import android.renderscript.ProgramStore.Builder;
+import android.renderscript.ProgramStore.DepthFunc;
+
+public class UT_program_store extends UnitTest {
+    private Resources mRes;
+
+    ProgramStore ditherEnable;
+    ProgramStore colorRWriteEnable;
+    ProgramStore colorGWriteEnable;
+    ProgramStore colorBWriteEnable;
+    ProgramStore colorAWriteEnable;
+    ProgramStore blendSrc;
+    ProgramStore blendDst;
+    ProgramStore depthWriteEnable;
+    ProgramStore depthFunc;
+
+    protected UT_program_store(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "ProgramStore", ctx);
+        mRes = res;
+    }
+
+    private ProgramStore.Builder getDefaultBuilder(RenderScript RS) {
+        ProgramStore.Builder b = new ProgramStore.Builder(RS);
+        b.setBlendFunc(ProgramStore.BlendSrcFunc.ZERO, ProgramStore.BlendDstFunc.ZERO);
+        b.setColorMaskEnabled(false, false, false, false);
+        b.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        b.setDepthMaskEnabled(false);
+        b.setDitherEnabled(false);
+        return b;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_program_store s) {
+        ProgramStore.Builder b = getDefaultBuilder(RS);
+        ditherEnable = b.setDitherEnabled(true).create();
+
+        b = getDefaultBuilder(RS);
+        colorRWriteEnable = b.setColorMaskEnabled(true,  false, false, false).create();
+
+        b = getDefaultBuilder(RS);
+        colorGWriteEnable = b.setColorMaskEnabled(false, true,  false, false).create();
+
+        b = getDefaultBuilder(RS);
+        colorBWriteEnable = b.setColorMaskEnabled(false, false, true,  false).create();
+
+        b = getDefaultBuilder(RS);
+        colorAWriteEnable = b.setColorMaskEnabled(false, false, false, true).create();
+
+        b = getDefaultBuilder(RS);
+        blendSrc = b.setBlendFunc(ProgramStore.BlendSrcFunc.DST_COLOR,
+                                  ProgramStore.BlendDstFunc.ZERO).create();
+
+        b = getDefaultBuilder(RS);
+        blendDst = b.setBlendFunc(ProgramStore.BlendSrcFunc.ZERO,
+                                  ProgramStore.BlendDstFunc.DST_ALPHA).create();
+
+        b = getDefaultBuilder(RS);
+        depthWriteEnable = b.setDepthMaskEnabled(true).create();
+
+        b = getDefaultBuilder(RS);
+        depthFunc = b.setDepthFunc(ProgramStore.DepthFunc.GREATER).create();
+
+        s.set_ditherEnable(ditherEnable);
+        s.set_colorRWriteEnable(colorRWriteEnable);
+        s.set_colorGWriteEnable(colorGWriteEnable);
+        s.set_colorBWriteEnable(colorBWriteEnable);
+        s.set_colorAWriteEnable(colorAWriteEnable);
+        s.set_blendSrc(blendSrc);
+        s.set_blendDst(blendDst);
+        s.set_depthWriteEnable(depthWriteEnable);
+        s.set_depthFunc(depthFunc);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_program_store s = new ScriptC_program_store(pRS, mRes, R.raw.program_store);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_program_store_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    void checkObject(ProgramStore ps,
+                     boolean depthMask,
+                     DepthFunc df,
+                     BlendSrcFunc bsf,
+                     BlendDstFunc bdf,
+                     boolean R,
+                     boolean G,
+                     boolean B,
+                     boolean A,
+                     boolean dither) {
+        _RS_ASSERT("ps.getDepthMaskEnabled() == depthMask", ps.getDepthMaskEnabled() == depthMask);
+        _RS_ASSERT("ps.getDepthFunc() == df", ps.getDepthFunc() == df);
+        _RS_ASSERT("ps.getBlendSrcFunc() == bsf", ps.getBlendSrcFunc() == bsf);
+        _RS_ASSERT("ps.getBlendDstFunc() == bdf", ps.getBlendDstFunc() == bdf);
+        _RS_ASSERT("ps.getColorMaskREnabled() == R", ps.getColorMaskREnabled() == R);
+        _RS_ASSERT("ps.getColorMaskGEnabled() == G", ps.getColorMaskGEnabled() == G);
+        _RS_ASSERT("ps.getColorMaskBEnabled() == B", ps.getColorMaskBEnabled() == B);
+        _RS_ASSERT("ps.getColorMaskAEnabled() == A", ps.getColorMaskAEnabled() == A);
+        _RS_ASSERT("ps.getDitherEnabled() == dither", ps.getDitherEnabled() == dither);
+    }
+
+    void varyBuilderColorAndDither(ProgramStore.Builder pb,
+                                   boolean depthMask,
+                                   DepthFunc df,
+                                   BlendSrcFunc bsf,
+                                   BlendDstFunc bdf) {
+        for (int r = 0; r <= 1; r++) {
+            boolean isR = (r == 1);
+            for (int g = 0; g <= 1; g++) {
+                boolean isG = (g == 1);
+                for (int b = 0; b <= 1; b++) {
+                    boolean isB = (b == 1);
+                    for (int a = 0; a <= 1; a++) {
+                        boolean isA = (a == 1);
+                        for (int dither = 0; dither <= 1; dither++) {
+                            boolean isDither = (dither == 1);
+                            pb.setDitherEnabled(isDither);
+                            pb.setColorMaskEnabled(isR, isG, isB, isA);
+                            ProgramStore ps = pb.create();
+                            checkObject(ps, depthMask, df, bsf, bdf, isR, isG, isB, isA, isDither);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void testJavaSide(RenderScript RS) {
+        for (int depth = 0; depth <= 1; depth++) {
+            boolean depthMask = (depth == 1);
+            for (DepthFunc df : DepthFunc.values()) {
+                for (BlendSrcFunc bsf : BlendSrcFunc.values()) {
+                    for (BlendDstFunc bdf : BlendDstFunc.values()) {
+                        ProgramStore.Builder b = new ProgramStore.Builder(RS);
+                        b.setDepthFunc(df);
+                        b.setDepthMaskEnabled(depthMask);
+                        b.setBlendFunc(bsf, bdf);
+                        varyBuilderColorAndDither(b, depthMask, df, bsf, bdf);
+                    }
+                }
+            }
+        }
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testJavaSide(pRS);
+        testScriptSide(pRS);
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_sampler.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_sampler.java
new file mode 100644
index 0000000..c328cf6
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_sampler.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2011 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.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Sampler;
+import android.renderscript.Sampler.Value;
+
+public class UT_sampler extends UnitTest {
+    private Resources mRes;
+
+    Sampler minification;
+    Sampler magnification;
+    Sampler wrapS;
+    Sampler wrapT;
+    Sampler anisotropy;
+
+    protected UT_sampler(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Sampler", ctx);
+        mRes = res;
+    }
+
+    private Sampler.Builder getDefaultBuilder(RenderScript RS) {
+        Sampler.Builder b = new Sampler.Builder(RS);
+        b.setMinification(Value.NEAREST);
+        b.setMagnification(Value.NEAREST);
+        b.setWrapS(Value.CLAMP);
+        b.setWrapT(Value.CLAMP);
+        b.setAnisotropy(1.0f);
+        return b;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_sampler s) {
+        Sampler.Builder b = getDefaultBuilder(RS);
+        b.setMinification(Value.LINEAR_MIP_LINEAR);
+        minification = b.create();
+
+        b = getDefaultBuilder(RS);
+        b.setMagnification(Value.LINEAR);
+        magnification = b.create();
+
+        b = getDefaultBuilder(RS);
+        b.setWrapS(Value.WRAP);
+        wrapS = b.create();
+
+        b = getDefaultBuilder(RS);
+        b.setWrapT(Value.WRAP);
+        wrapT = b.create();
+
+        b = getDefaultBuilder(RS);
+        b.setAnisotropy(8.0f);
+        anisotropy = b.create();
+
+        s.set_minification(minification);
+        s.set_magnification(magnification);
+        s.set_wrapS(wrapS);
+        s.set_wrapT(wrapT);
+        s.set_anisotropy(anisotropy);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_sampler s = new ScriptC_sampler(pRS, mRes, R.raw.sampler);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_sampler_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    private void testJavaSide(RenderScript RS) {
+        _RS_ASSERT("minification.getMagnification() == Sampler.Value.NEAREST",
+                    minification.getMagnification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("minification.getMinification() == Sampler.Value.LINEAR_MIP_LINEAR",
+                    minification.getMinification() == Sampler.Value.LINEAR_MIP_LINEAR);
+        _RS_ASSERT("minification.getWrapS() == Sampler.Value.CLAMP",
+                    minification.getWrapS() == Sampler.Value.CLAMP);
+        _RS_ASSERT("minification.getWrapT() == Sampler.Value.CLAMP",
+                    minification.getWrapT() == Sampler.Value.CLAMP);
+        _RS_ASSERT("minification.getAnisotropy() == 1.0f",
+                    minification.getAnisotropy() == 1.0f);
+
+        _RS_ASSERT("magnification.getMagnification() == Sampler.Value.LINEAR",
+                    magnification.getMagnification() == Sampler.Value.LINEAR);
+        _RS_ASSERT("magnification.getMinification() == Sampler.Value.NEAREST",
+                    magnification.getMinification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("magnification.getWrapS() == Sampler.Value.CLAMP",
+                    magnification.getWrapS() == Sampler.Value.CLAMP);
+        _RS_ASSERT("magnification.getWrapT() == Sampler.Value.CLAMP",
+                    magnification.getWrapT() == Sampler.Value.CLAMP);
+        _RS_ASSERT("magnification.getAnisotropy() == 1.0f",
+                    magnification.getAnisotropy() == 1.0f);
+
+        _RS_ASSERT("wrapS.getMagnification() == Sampler.Value.NEAREST",
+                    wrapS.getMagnification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("wrapS.getMinification() == Sampler.Value.NEAREST",
+                    wrapS.getMinification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("wrapS.getWrapS() == Sampler.Value.WRAP",
+                    wrapS.getWrapS() == Sampler.Value.WRAP);
+        _RS_ASSERT("wrapS.getWrapT() == Sampler.Value.CLAMP",
+                    wrapS.getWrapT() == Sampler.Value.CLAMP);
+        _RS_ASSERT("wrapS.getAnisotropy() == 1.0f",
+                    wrapS.getAnisotropy() == 1.0f);
+
+        _RS_ASSERT("wrapT.getMagnification() == Sampler.Value.NEAREST",
+                    wrapT.getMagnification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("wrapT.getMinification() == Sampler.Value.NEAREST",
+                    wrapT.getMinification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("wrapT.getWrapS() == Sampler.Value.CLAMP",
+                    wrapT.getWrapS() == Sampler.Value.CLAMP);
+        _RS_ASSERT("wrapT.getWrapT() == Sampler.Value.WRAP",
+                    wrapT.getWrapT() == Sampler.Value.WRAP);
+        _RS_ASSERT("wrapT.getAnisotropy() == 1.0f",
+                    wrapT.getAnisotropy() == 1.0f);
+
+        _RS_ASSERT("anisotropy.getMagnification() == Sampler.Value.NEAREST",
+                    anisotropy.getMagnification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("anisotropy.getMinification() == Sampler.Value.NEAREST",
+                    anisotropy.getMinification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("anisotropy.getWrapS() == Sampler.Value.CLAMP",
+                    anisotropy.getWrapS() == Sampler.Value.CLAMP);
+        _RS_ASSERT("anisotropy.getWrapT() == Sampler.Value.CLAMP",
+                    anisotropy.getWrapT() == Sampler.Value.CLAMP);
+        _RS_ASSERT("anisotropy.getAnisotropy() == 1.0f",
+                    anisotropy.getAnisotropy() == 8.0f);
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testScriptSide(pRS);
+        testJavaSide(pRS);
+        passTest();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UnitTest.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UnitTest.java
index edff83f..fbac124 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/UnitTest.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UnitTest.java
@@ -58,8 +58,8 @@
 
     protected void _RS_ASSERT(String message, boolean b) {
         if(b == false) {
-            result = -1;
             Log.e(name, message + " FAILED");
+            failTest();
         }
     }
 
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/element.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/element.rs
new file mode 100644
index 0000000..0c42d84
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/element.rs
@@ -0,0 +1,158 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+rs_element simpleElem;
+rs_element complexElem;
+typedef struct ComplexStruct {
+    float subElem0;
+    float subElem1;
+    int subElem2;
+    float arrayElem0[2];
+    int arrayElem1[5];
+    char subElem3;
+    float subElem4;
+    float2 subElem5;
+    float3 subElem6;
+    float4 subElem_7;
+} ComplexStruct_t;
+
+ComplexStruct_t *complexStruct;
+
+static const char *subElemNames[] = {
+    "subElem0",
+    "subElem1",
+    "subElem2",
+    "arrayElem0",
+    "arrayElem1",
+    "subElem3",
+    "subElem4",
+    "subElem5",
+    "subElem6",
+    "subElem_7",
+};
+
+static uint32_t subElemNamesSizes[] = {
+    8,
+    8,
+    8,
+    10,
+    10,
+    8,
+    8,
+    8,
+    8,
+    9,
+};
+
+static uint32_t subElemArraySizes[] = {
+    1,
+    1,
+    1,
+    2,
+    5,
+    1,
+    1,
+    1,
+    1,
+    1,
+};
+
+static void resetStruct() {
+    uint8_t *bytePtr = (uint8_t*)complexStruct;
+    uint32_t sizeOfStruct = sizeof(*complexStruct);
+    for(uint32_t i = 0; i < sizeOfStruct; i ++) {
+        bytePtr[i] = 0;
+    }
+}
+
+static bool equals(const char *name0, const char * name1, uint32_t len) {
+    for (uint32_t i = 0; i < len; i ++) {
+        if (name0[i] != name1[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool test_element_getters() {
+    bool failed = false;
+
+    uint32_t subElemOffsets[10];
+    uint32_t index = 0;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem0   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem1   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem2   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->arrayElem0 - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->arrayElem1 - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem3   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem4   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem5   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem6   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem_7  - (uint32_t)complexStruct;
+
+    uint32_t subElemCount = rsElementGetSubElementCount(simpleElem);
+    _RS_ASSERT(subElemCount == 0);
+    _RS_ASSERT(rsElementGetDataKind(simpleElem) == RS_KIND_USER);
+    _RS_ASSERT(rsElementGetDataType(simpleElem) == RS_TYPE_FLOAT_32);
+    _RS_ASSERT(rsElementGetVectorSize(simpleElem) == 3);
+
+    subElemCount = rsElementGetSubElementCount(complexElem);
+    _RS_ASSERT(subElemCount == 10);
+    _RS_ASSERT(rsElementGetDataKind(complexElem) == RS_KIND_USER);
+    _RS_ASSERT(rsElementGetDataType(complexElem) == RS_TYPE_NONE);
+    _RS_ASSERT(rsElementGetVectorSize(complexElem) == 1);
+    _RS_ASSERT(rsElementGetSizeBytes(complexElem) == sizeof(*complexStruct));
+
+    char buffer[64];
+    for (uint32_t i = 0; i < subElemCount; i ++) {
+        rs_element subElem = rsElementGetSubElement(complexElem, i);
+        _RS_ASSERT(rsIsObject(subElem));
+
+        _RS_ASSERT(rsElementGetSubElementNameLength(complexElem, i) == subElemNamesSizes[i] + 1);
+
+        uint32_t written = rsElementGetSubElementName(complexElem, i, buffer, 64);
+        _RS_ASSERT(written == subElemNamesSizes[i]);
+        _RS_ASSERT(equals(buffer, subElemNames[i], written));
+
+        _RS_ASSERT(rsElementGetSubElementArraySize(complexElem, i) == subElemArraySizes[i]);
+        _RS_ASSERT(rsElementGetSubElementOffsetBytes(complexElem, i) == subElemOffsets[i]);
+    }
+
+    // Tests error checking
+    rs_element subElem = rsElementGetSubElement(complexElem, subElemCount);
+    _RS_ASSERT(!rsIsObject(subElem));
+
+    _RS_ASSERT(rsElementGetSubElementNameLength(complexElem, subElemCount) == 0);
+
+    _RS_ASSERT(rsElementGetSubElementName(complexElem, subElemCount, buffer, 64) == 0);
+    _RS_ASSERT(rsElementGetSubElementName(complexElem, 0, NULL, 64) == 0);
+    _RS_ASSERT(rsElementGetSubElementName(complexElem, 0, buffer, 0) == 0);
+    uint32_t written = rsElementGetSubElementName(complexElem, 0, buffer, 5);
+    _RS_ASSERT(written == 4);
+    _RS_ASSERT(buffer[4] == '\0');
+
+    _RS_ASSERT(rsElementGetSubElementArraySize(complexElem, subElemCount) == 0);
+    _RS_ASSERT(rsElementGetSubElementOffsetBytes(complexElem, subElemCount) == 0);
+
+    if (failed) {
+        rsDebug("test_element_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_element_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void element_test() {
+    bool failed = false;
+    failed |= test_element_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/mesh.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/mesh.rs
new file mode 100644
index 0000000..627ab99
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/mesh.rs
@@ -0,0 +1,64 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+rs_mesh mesh;
+rs_allocation vertexAlloc0;
+rs_allocation vertexAlloc1;
+
+rs_allocation indexAlloc0;
+rs_allocation indexAlloc2;
+
+static bool test_mesh_getters() {
+    bool failed = false;
+
+    _RS_ASSERT(rsMeshGetVertexAllocationCount(mesh) == 2);
+    _RS_ASSERT(rsMeshGetPrimitiveCount(mesh) == 3);
+
+    rs_allocation meshV0 = rsMeshGetVertexAllocation(mesh, 0);
+    rs_allocation meshV1 = rsMeshGetVertexAllocation(mesh, 1);
+    rs_allocation meshV2 = rsMeshGetVertexAllocation(mesh, 2);
+    _RS_ASSERT(meshV0.p == vertexAlloc0.p);
+    _RS_ASSERT(meshV1.p == vertexAlloc1.p);
+    _RS_ASSERT(!rsIsObject(meshV2));
+
+    rs_allocation meshI0 = rsMeshGetIndexAllocation(mesh, 0);
+    rs_allocation meshI1 = rsMeshGetIndexAllocation(mesh, 1);
+    rs_allocation meshI2 = rsMeshGetIndexAllocation(mesh, 2);
+    rs_allocation meshI3 = rsMeshGetIndexAllocation(mesh, 3);
+    _RS_ASSERT(meshI0.p == indexAlloc0.p);
+    _RS_ASSERT(!rsIsObject(meshI1));
+    _RS_ASSERT(meshI2.p == indexAlloc2.p);
+    _RS_ASSERT(!rsIsObject(meshI3));
+
+    rs_primitive p0 = rsMeshGetPrimitive(mesh, 0);
+    rs_primitive p1 = rsMeshGetPrimitive(mesh, 1);
+    rs_primitive p2 = rsMeshGetPrimitive(mesh, 2);
+    rs_primitive p3 = rsMeshGetPrimitive(mesh, 3);
+
+    _RS_ASSERT(p0 == RS_PRIMITIVE_POINT);
+    _RS_ASSERT(p1 == RS_PRIMITIVE_LINE);
+    _RS_ASSERT(p2 == RS_PRIMITIVE_TRIANGLE);
+    _RS_ASSERT(p3 == RS_PRIMITIVE_INVALID);
+
+    if (failed) {
+        rsDebug("test_mesh_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_mesh_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void mesh_test() {
+    bool failed = false;
+    failed |= test_mesh_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/program_raster.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/program_raster.rs
new file mode 100644
index 0000000..11b8c30
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/program_raster.rs
@@ -0,0 +1,37 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+rs_program_raster pointSpriteEnabled;
+rs_program_raster cullMode;
+
+static bool test_program_raster_getters() {
+    bool failed = false;
+
+    _RS_ASSERT(rsgProgramRasterGetPointSpriteEnabled(pointSpriteEnabled) == true);
+    _RS_ASSERT(rsgProgramRasterGetCullMode(pointSpriteEnabled) == RS_CULL_BACK);
+
+    _RS_ASSERT(rsgProgramRasterGetPointSpriteEnabled(cullMode) == false);
+    _RS_ASSERT(rsgProgramRasterGetCullMode(cullMode) == RS_CULL_FRONT);
+
+    if (failed) {
+        rsDebug("test_program_raster_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_program_raster_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void program_raster_test() {
+    bool failed = false;
+    failed |= test_program_raster_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/program_store.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/program_store.rs
new file mode 100644
index 0000000..3cd8a20
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/program_store.rs
@@ -0,0 +1,128 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+rs_program_store ditherEnable;
+rs_program_store colorRWriteEnable;
+rs_program_store colorGWriteEnable;
+rs_program_store colorBWriteEnable;
+rs_program_store colorAWriteEnable;
+rs_program_store blendSrc;
+rs_program_store blendDst;
+rs_program_store depthWriteEnable;
+rs_program_store depthFunc;
+
+static bool test_program_store_getters() {
+    bool failed = false;
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(depthFunc) == RS_DEPTH_FUNC_GREATER);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(depthFunc) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(depthFunc) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(depthWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(depthWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(depthWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(depthWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(colorRWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(colorRWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(colorRWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(colorRWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(colorGWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(colorGWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(colorGWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(colorGWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(colorBWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(colorBWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(colorBWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(colorBWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(colorAWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(colorAWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(colorAWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(colorAWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(ditherEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(ditherEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(ditherEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(ditherEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(blendSrc) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(blendSrc) == RS_BLEND_SRC_DST_COLOR);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(blendSrc) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(blendDst) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(blendDst) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(blendDst) == RS_BLEND_DST_DST_ALPHA);
+
+    if (failed) {
+        rsDebug("test_program_store_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_program_store_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void program_store_test() {
+    bool failed = false;
+    failed |= test_program_store_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/sampler.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/sampler.rs
new file mode 100644
index 0000000..ac9a549
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/sampler.rs
@@ -0,0 +1,63 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+rs_sampler minification;
+rs_sampler magnification;
+rs_sampler wrapS;
+rs_sampler wrapT;
+rs_sampler anisotropy;
+
+static bool test_sampler_getters() {
+    bool failed = false;
+
+    _RS_ASSERT(rsgSamplerGetMagnification(minification) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetMinification(minification) == RS_SAMPLER_LINEAR_MIP_LINEAR);
+    _RS_ASSERT(rsgSamplerGetWrapS(minification) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetWrapT(minification) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(minification) == 1.0f);
+
+    _RS_ASSERT(rsgSamplerGetMagnification(magnification) == RS_SAMPLER_LINEAR);
+    _RS_ASSERT(rsgSamplerGetMinification(magnification) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetWrapS(magnification) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetWrapT(magnification) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(magnification) == 1.0f);
+
+    _RS_ASSERT(rsgSamplerGetMagnification(wrapS) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetMinification(wrapS) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetWrapS(wrapS) == RS_SAMPLER_WRAP);
+    _RS_ASSERT(rsgSamplerGetWrapT(wrapS) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(wrapS) == 1.0f);
+
+    _RS_ASSERT(rsgSamplerGetMagnification(wrapT) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetMinification(wrapT) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetWrapS(wrapT) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetWrapT(wrapT) == RS_SAMPLER_WRAP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(wrapT) == 1.0f);
+
+    _RS_ASSERT(rsgSamplerGetMagnification(anisotropy) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetMinification(anisotropy) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetWrapS(anisotropy) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetWrapT(anisotropy) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(anisotropy) == 8.0f);
+
+    if (failed) {
+        rsDebug("test_sampler_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_sampler_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void sampler_test() {
+    bool failed = false;
+    failed |= test_sampler_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java b/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
index 5f53a9b..1a2dcb9 100644
--- a/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
+++ b/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
@@ -19,12 +19,21 @@
 import com.android.internal.os.RuntimeInit;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessErrorStateInfo;
 import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This smoke test is designed to quickly sniff for any error conditions
@@ -32,53 +41,125 @@
  */
 public class ProcessErrorsTest extends AndroidTestCase {
     
-    private final String TAG = "ProcessErrorsTest";
+    private static final String TAG = "ProcessErrorsTest";
     
     protected ActivityManager mActivityManager;
+    protected PackageManager mPackageManager;
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mActivityManager = (ActivityManager) 
+        mActivityManager = (ActivityManager)
                 getContext().getSystemService(Context.ACTIVITY_SERVICE);
+        mPackageManager = getContext().getPackageManager();
     }
 
     public void testSetUpConditions() throws Exception {
         assertNotNull(mActivityManager);
+        assertNotNull(mPackageManager);
     }
 
     public void testNoProcessErrors() throws Exception {
-        List<ActivityManager.ProcessErrorStateInfo> errList;        
+        final String reportMsg = checkForProcessErrors();
+        if (reportMsg != null) {
+            Log.w(TAG, reportMsg);
+        }
+
+        // report a non-empty list back to the test framework
+        assertNull(reportMsg, reportMsg);
+    }
+
+    private String checkForProcessErrors() throws Exception {
+        List<ProcessErrorStateInfo> errList;
         errList = mActivityManager.getProcessesInErrorState();
         
         // note: this contains information about each process that is currently in an error
         // condition.  if the list is empty (null) then "we're good".  
         
         // if the list is non-empty, then it's useful to report the contents of the list
-        // we'll put a copy in the log, and we'll report it back to the framework via the assert.
         final String reportMsg = reportListContents(errList);
-        if (reportMsg != null) {
-            Log.w(TAG, reportMsg);
-        }
-        
-        // report a non-empty list back to the test framework
-        assertNull(reportMsg, errList);
+        return reportMsg;
     }
-    
+
+    /**
+     * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes
+     * happened while doing so.
+     * <p />
+     * FIXME: Doesn't detect multiple crashing apps properly, since the crash dialog for the
+     * FIXME: first app doesn't go away.
+     */
+    public void testRunAllActivities() throws Exception {
+        final Intent home = new Intent(Intent.ACTION_MAIN);
+        home.addCategory(Intent.CATEGORY_HOME);
+        home.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final Intent launchable = new Intent(Intent.ACTION_MAIN);
+        launchable.addCategory(Intent.CATEGORY_LAUNCHER);
+        final List<ResolveInfo> activities = mPackageManager.queryIntentActivities(launchable, 0);
+        final Set<ProcessError> errSet = new HashSet<ProcessError>();
+
+        for (ResolveInfo info : activities) {
+            Log.i(TAG, String.format("Got %s/%s", info.activityInfo.packageName,
+                    info.activityInfo.name));
+
+            // build an Intent to launch the app
+            final ComponentName component = new ComponentName(info.activityInfo.packageName,
+                    info.activityInfo.name);
+            final Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setComponent(component);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            // launch app, and wait 7 seconds for it to start/settle
+            getContext().startActivity(intent);
+            try {
+                Thread.sleep(7000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+
+            // See if there are any errors
+            Collection<ProcessErrorStateInfo> procs = mActivityManager.getProcessesInErrorState();
+            if (procs != null) {
+                errSet.addAll(ProcessError.fromCollection(procs));
+            }
+
+            // Send the "home" intent and wait 2 seconds for us to get there
+            getContext().startActivity(home);
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+
+        if (!errSet.isEmpty()) {
+            fail(String.format("Got %d errors: %s", errSet.size(),
+                    reportWrappedListContents(errSet)));
+        }
+    }
+
+    private String reportWrappedListContents(Collection<ProcessError> errList) {
+        List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size());
+        for (ProcessError err : errList) {
+            newList.add(err.info);
+        }
+        return reportListContents(newList);
+    }
+
     /**
      * This helper function will dump the actual error reports.
      * 
      * @param errList The error report containing one or more error records.
      * @return Returns a string containing all of the errors.
      */
-    private String reportListContents(List<ActivityManager.ProcessErrorStateInfo> errList) {
+    private String reportListContents(Collection<ProcessErrorStateInfo> errList) {
         if (errList == null) return null;
 
         StringBuilder builder = new StringBuilder();
 
-        Iterator<ActivityManager.ProcessErrorStateInfo> iter = errList.iterator();
+        Iterator<ProcessErrorStateInfo> iter = errList.iterator();
         while (iter.hasNext()) {
-            ActivityManager.ProcessErrorStateInfo entry = iter.next();
+            ProcessErrorStateInfo entry = iter.next();
 
             String condition;
             switch (entry.condition) {
@@ -96,8 +177,77 @@
             builder.append("Process error ").append(condition).append(" ");
             builder.append(" ").append(entry.shortMsg);
             builder.append(" detected in ").append(entry.processName).append(" ").append(entry.tag);
+            builder.append("\n");
         }
         return builder.toString();
     }
-    
+
+    /**
+     * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent
+     * crashes are considered equal).
+     */
+    private static class ProcessError {
+        public final ProcessErrorStateInfo info;
+
+        public ProcessError(ProcessErrorStateInfo newInfo) {
+            info = newInfo;
+        }
+
+        public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in)
+                {
+            List<ProcessError> out = new ArrayList<ProcessError>(in.size());
+            for (ProcessErrorStateInfo info : in) {
+                out.add(new ProcessError(info));
+            }
+            return out;
+        }
+
+        private boolean strEquals(String a, String b) {
+            if ((a == null) && (b == null)) {
+                return true;
+            } else if ((a == null) || (b == null)) {
+                return false;
+            } else {
+                return a.equals(b);
+            }
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other == null) return false;
+            if (!(other instanceof ProcessError)) return false;
+            ProcessError peOther = (ProcessError) other;
+
+            return (info.condition == peOther.info.condition)
+                    && strEquals(info.longMsg, peOther.info.longMsg)
+                    && (info.pid == peOther.info.pid)
+                    && strEquals(info.processName, peOther.info.processName)
+                    && strEquals(info.shortMsg, peOther.info.shortMsg)
+                    && strEquals(info.stackTrace, peOther.info.stackTrace)
+                    && strEquals(info.tag, peOther.info.tag)
+                    && (info.uid == peOther.info.uid);
+        }
+
+        private int hash(Object obj) {
+            if (obj == null) {
+                return 13;
+            } else {
+                return obj.hashCode();
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            int code = 17;
+            code += info.condition;
+            code *= hash(info.longMsg);
+            code += info.pid;
+            code *= hash(info.processName);
+            code *= hash(info.shortMsg);
+            code *= hash(info.stackTrace);
+            code *= hash(info.tag);
+            code += info.uid;
+            return code;
+        }
+    }
 }
diff --git a/tests/SmokeTestApps/Android.mk b/tests/SmokeTestApps/Android.mk
new file mode 100644
index 0000000..3f5f011
--- /dev/null
+++ b/tests/SmokeTestApps/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SmokeTestTriggerApps
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/SmokeTestApps/AndroidManifest.xml b/tests/SmokeTestApps/AndroidManifest.xml
new file mode 100644
index 0000000..0f20107
--- /dev/null
+++ b/tests/SmokeTestApps/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.smoketest.triggers">
+
+    <application android:label="something">
+        <activity android:name=".CrashyApp"
+                  android:label="Test Crashy App">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".CrashyApp2"
+                  android:label="Test Crashy App2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".UnresponsiveApp"
+                  android:label="Test Unresponsive App">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/SmokeTestApps/README b/tests/SmokeTestApps/README
new file mode 100644
index 0000000..04aa366
--- /dev/null
+++ b/tests/SmokeTestApps/README
@@ -0,0 +1,3 @@
+The apps in this folder are intentionally bad-behaving apps that are intended
+to trigger the smoke tests to fail.  They are otherwise not useful.
+
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
new file mode 100644
index 0000000..c11b0f3
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 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.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class CrashyApp extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Crashy Android");
+        setContentView(tv);
+    }
+
+    @Override
+    public void onResume() {
+        ((String) null).length();
+    }
+}
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java
new file mode 100644
index 0000000..3ef5b2b
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class CrashyApp2 extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Other Crashy Android");
+        setContentView(tv);
+    }
+
+
+    @Override
+    public void onResume() {
+        throw new RuntimeException("Two drums and a cymbal fall off a cliff...");
+    }
+}
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java
new file mode 100644
index 0000000..1291897
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 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.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class UnresponsiveApp extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Unresponsive Android");
+        setContentView(tv);
+    }
+
+    @Override
+    public void onResume() {
+        // Attempt to provoke the ire of the ActivityManager
+        while (true) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index bbb74d1..d05e0b8 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -366,17 +366,6 @@
                     handleDriverEvent(eventData);
                 } else if (event == TERMINATING) {
                     /**
-                     * If monitor socket is closed, we have already
-                     * stopped the supplicant, simply exit the monitor thread
-                     */
-                    if (eventData.startsWith(MONITOR_SOCKET_CLOSED_STR)) {
-                        if (false) {
-                            Log.d(TAG, "Monitor socket is closed, exiting thread");
-                        }
-                        break;
-                    }
-
-                    /**
                      * Close the supplicant connection if we see
                      * too many recv errors
                      */
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 48a785c..e3dd3a6 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -39,6 +39,8 @@
  */
 public class WifiNative {
 
+    private static final boolean DBG = false;
+    private final String mTAG;
     private static final int DEFAULT_GROUP_OWNER_INTENT = 7;
 
     static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
@@ -53,9 +55,7 @@
 
     public native static boolean unloadDriver();
 
-    public native static boolean startSupplicant();
-
-    public native static boolean startP2pSupplicant();
+    public native static boolean startSupplicant(boolean p2pSupported);
 
     /* Sends a kill signal to supplicant. To be used when we have lost connection
        or when the supplicant is hung */
@@ -79,6 +79,7 @@
 
     public WifiNative(String iface) {
         mInterface = iface;
+        mTAG = "WifiNative-" + iface;
     }
 
     public boolean connectToSupplicant() {
@@ -94,14 +95,17 @@
     }
 
     private boolean doBooleanCommand(String command) {
+        if (DBG) Log.d(mTAG, "doBoolean: " + command);
         return doBooleanCommand(mInterface, command);
     }
 
     private int doIntCommand(String command) {
+        if (DBG) Log.d(mTAG, "doInt: " + command);
         return doIntCommand(mInterface, command);
     }
 
     private String doStringCommand(String command) {
+        if (DBG) Log.d(mTAG, "doString: " + command);
         return doStringCommand(mInterface, command);
     }
 
@@ -437,6 +441,10 @@
         return doBooleanCommand("P2P_FIND " + timeout);
     }
 
+    public boolean p2pStopFind() {
+       return doBooleanCommand("P2P_STOP_FIND");
+    }
+
     public boolean p2pListen() {
         return doBooleanCommand("P2P_LISTEN");
     }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index fb9286e..0134456 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -45,6 +45,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.DhcpInfoInternal;
@@ -118,6 +119,8 @@
     private INetworkManagementService mNwService;
     private ConnectivityManager mCm;
 
+    private final boolean mP2pSupported;
+
     /* Scan results handling */
     private List<ScanResult> mScanResults;
     private static final Pattern scanResultPattern = Pattern.compile("\t+");
@@ -361,9 +364,9 @@
     /* Reset the WPS state machine */
     static final int CMD_RESET_WPS_STATE                  = BASE + 122;
 
-    /* Interaction with WifiP2pService */
-    public static final int WIFI_ENABLE_PENDING           = BASE + 131;
-    public static final int P2P_ENABLE_PROCEED            = BASE + 132;
+    /* P2p commands */
+    public static final int CMD_ENABLE_P2P                = BASE + 131;
+    public static final int CMD_DISABLE_P2P               = BASE + 132;
 
     private static final int CONNECT_MODE   = 1;
     private static final int SCAN_ONLY_MODE = 2;
@@ -482,9 +485,6 @@
     /* Waiting for untether confirmation to stop soft Ap */
     private State mSoftApStoppingState = new SoftApStoppingState();
 
-    /* Wait till p2p is disabled */
-    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
-
     private class TetherStateChange {
         ArrayList<String> available;
         ArrayList<String> active;
@@ -556,6 +556,9 @@
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         mNwService = INetworkManagementService.Stub.asInterface(b);
 
+        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WIFI_DIRECT);
+
         mWifiNative = new WifiNative(mInterfaceName);
         mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
         mWifiMonitor = new WifiMonitor(this, mWifiNative);
@@ -639,7 +642,6 @@
                 addState(mTetheringState, mSoftApStartedState);
                 addState(mTetheredState, mSoftApStartedState);
             addState(mSoftApStoppingState, mDefaultState);
-            addState(mWaitForP2pDisableState, mDefaultState);
 
         setInitialState(mInitialState);
 
@@ -1896,11 +1898,6 @@
                     mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
                                 new WpsResult(Status.FAILURE));
                     break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    // turn off wifi and defer to be handled in DriverUnloadedState
-                    setWifiEnabled(false);
-                    deferMessage(message);
-                    break;
                 default:
                     loge("Error! unhandled message" + message);
                     break;
@@ -2060,7 +2057,7 @@
                         loge("Unable to change interface settings: " + ie);
                     }
 
-                    if(mWifiNative.startSupplicant()) {
+                    if(mWifiNative.startSupplicant(mP2pSupported)) {
                         if (DBG) log("Supplicant start successful");
                         mWifiMonitor.startMonitoring();
                         transitionTo(mSupplicantStartingState);
@@ -2172,11 +2169,7 @@
             if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
                 case CMD_LOAD_DRIVER:
-                    mWifiP2pChannel.sendMessage(WIFI_ENABLE_PENDING);
-                    transitionTo(mWaitForP2pDisableState);
-                    break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    mReplyChannel.replyToMessage(message, P2P_ENABLE_PROCEED);
+                    transitionTo(mDriverLoadingState);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -2556,13 +2549,15 @@
                 mWifiNative.status();
                 transitionTo(mDisconnectedState);
             }
+
+            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
         }
         @Override
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
             boolean eventLoggingEnabled = true;
             switch(message.what) {
-                case CMD_SET_SCAN_TYPE:
+               case CMD_SET_SCAN_TYPE:
                     mSetScanActive = (message.arg1 == SCAN_ACTIVE);
                     mWifiNative.setScanMode(mSetScanActive);
                     break;
@@ -2675,6 +2670,8 @@
             mIsRunning = false;
             updateBatteryWorkSource(null);
             mScanResults = null;
+
+            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P);
         }
     }
 
@@ -3348,7 +3345,6 @@
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
                 case CMD_TETHER_STATE_CHANGE:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
@@ -3412,55 +3408,6 @@
                         transitionTo(mTetheringState);
                     }
                     break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    // turn of soft Ap and defer to be handled in DriverUnloadedState
-                    setWifiApEnabled(null, false);
-                    deferMessage(message);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-            return HANDLED;
-        }
-    }
-
-    class WaitForP2pDisableState extends State {
-        private int mSavedArg;
-        @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
-            //Preserve the argument arg1 that has information used in DriverLoadingState
-            mSavedArg = getCurrentMessage().arg1;
-        }
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
-            switch(message.what) {
-                case WifiP2pService.WIFI_ENABLE_PROCEED:
-                    //restore argument from original message (CMD_LOAD_DRIVER)
-                    message.arg1 = mSavedArg;
-                    transitionTo(mDriverLoadingState);
-                    break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
-                case CMD_START_SUPPLICANT:
-                case CMD_STOP_SUPPLICANT:
-                case CMD_START_AP:
-                case CMD_STOP_AP:
-                case CMD_START_DRIVER:
-                case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
-                case CMD_SET_SCAN_TYPE:
-                case CMD_SET_HIGH_PERF_MODE:
-                case CMD_SET_COUNTRY_CODE:
-                case CMD_SET_FREQUENCY_BAND:
-                case CMD_START_PACKET_FILTERING:
-                case CMD_STOP_PACKET_FILTERING:
-                    deferMessage(message);
-                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -3510,7 +3457,6 @@
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 default:
@@ -3606,7 +3552,6 @@
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 default:
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 7471a2d..b0cde64 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -301,7 +301,8 @@
     private String trimQuotes(String str) {
         str = str.trim();
         if (str.startsWith("'") && str.endsWith("'")) {
-            return str.substring(1, str.length()-1);
+            if (str.length() <= 2) return "";
+            else return str.substring(1, str.length()-1);
         }
         return str;
     }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 9205300..4fd0a57 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -199,68 +199,61 @@
     private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
 
     /** @hide */
-    public static final int ENABLE_P2P                              = BASE + 1;
+    public static final int DISCOVER_PEERS                          = BASE + 1;
     /** @hide */
-    public static final int ENABLE_P2P_FAILED                       = BASE + 2;
+    public static final int DISCOVER_PEERS_FAILED                   = BASE + 2;
     /** @hide */
-    public static final int ENABLE_P2P_SUCCEEDED                    = BASE + 3;
+    public static final int DISCOVER_PEERS_SUCCEEDED                = BASE + 3;
 
     /** @hide */
-    public static final int DISABLE_P2P                             = BASE + 4;
+    public static final int STOP_DISCOVERY                          = BASE + 4;
     /** @hide */
-    public static final int DISABLE_P2P_FAILED                      = BASE + 5;
+    public static final int STOP_DISCOVERY_FAILED                   = BASE + 5;
     /** @hide */
-    public static final int DISABLE_P2P_SUCCEEDED                   = BASE + 6;
+    public static final int STOP_DISCOVERY_SUCCEEDED                = BASE + 6;
 
     /** @hide */
-    public static final int DISCOVER_PEERS                          = BASE + 7;
+    public static final int CONNECT                                 = BASE + 7;
     /** @hide */
-    public static final int DISCOVER_PEERS_FAILED                   = BASE + 8;
+    public static final int CONNECT_FAILED                          = BASE + 8;
     /** @hide */
-    public static final int DISCOVER_PEERS_SUCCEEDED                = BASE + 9;
+    public static final int CONNECT_SUCCEEDED                       = BASE + 9;
 
     /** @hide */
-    public static final int CONNECT                                 = BASE + 10;
+    public static final int CANCEL_CONNECT                          = BASE + 10;
     /** @hide */
-    public static final int CONNECT_FAILED                          = BASE + 11;
+    public static final int CANCEL_CONNECT_FAILED                   = BASE + 11;
     /** @hide */
-    public static final int CONNECT_SUCCEEDED                       = BASE + 12;
+    public static final int CANCEL_CONNECT_SUCCEEDED                = BASE + 12;
 
     /** @hide */
-    public static final int CANCEL_CONNECT                          = BASE + 13;
+    public static final int CREATE_GROUP                            = BASE + 13;
     /** @hide */
-    public static final int CANCEL_CONNECT_FAILED                   = BASE + 14;
+    public static final int CREATE_GROUP_FAILED                     = BASE + 14;
     /** @hide */
-    public static final int CANCEL_CONNECT_SUCCEEDED                = BASE + 15;
+    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 15;
 
     /** @hide */
-    public static final int CREATE_GROUP                            = BASE + 16;
+    public static final int REMOVE_GROUP                            = BASE + 16;
     /** @hide */
-    public static final int CREATE_GROUP_FAILED                     = BASE + 17;
+    public static final int REMOVE_GROUP_FAILED                     = BASE + 17;
     /** @hide */
-    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 18;
+    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 18;
 
     /** @hide */
-    public static final int REMOVE_GROUP                            = BASE + 19;
+    public static final int REQUEST_PEERS                           = BASE + 19;
     /** @hide */
-    public static final int REMOVE_GROUP_FAILED                     = BASE + 20;
-    /** @hide */
-    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 21;
+    public static final int RESPONSE_PEERS                          = BASE + 20;
 
     /** @hide */
-    public static final int REQUEST_PEERS                           = BASE + 22;
+    public static final int REQUEST_CONNECTION_INFO                 = BASE + 21;
     /** @hide */
-    public static final int RESPONSE_PEERS                          = BASE + 23;
+    public static final int RESPONSE_CONNECTION_INFO                = BASE + 22;
 
     /** @hide */
-    public static final int REQUEST_CONNECTION_INFO                 = BASE + 24;
+    public static final int REQUEST_GROUP_INFO                      = BASE + 23;
     /** @hide */
-    public static final int RESPONSE_CONNECTION_INFO                = BASE + 25;
-
-    /** @hide */
-    public static final int REQUEST_GROUP_INFO                      = BASE + 26;
-    /** @hide */
-    public static final int RESPONSE_GROUP_INFO                     = BASE + 27;
+    public static final int RESPONSE_GROUP_INFO                     = BASE + 24;
 
     /**
      * Create a new WifiP2pManager instance. Applications use
@@ -376,6 +369,7 @@
                         break;
                     /* ActionListeners grouped together */
                     case WifiP2pManager.DISCOVER_PEERS_FAILED:
+                    case WifiP2pManager.STOP_DISCOVERY_FAILED:
                     case WifiP2pManager.CONNECT_FAILED:
                     case WifiP2pManager.CANCEL_CONNECT_FAILED:
                     case WifiP2pManager.CREATE_GROUP_FAILED:
@@ -386,6 +380,7 @@
                         break;
                     /* ActionListeners grouped together */
                     case WifiP2pManager.DISCOVER_PEERS_SUCCEEDED:
+                    case WifiP2pManager.STOP_DISCOVERY_SUCCEEDED:
                     case WifiP2pManager.CONNECT_SUCCEEDED:
                     case WifiP2pManager.CANCEL_CONNECT_SUCCEEDED:
                     case WifiP2pManager.CREATE_GROUP_SUCCEEDED:
@@ -459,26 +454,6 @@
     }
 
     /**
-     * Sends in a request to the system to enable p2p. This will pop up a dialog
-     * to the user and upon authorization will enable p2p.
-     * @hide
-     */
-    public void enableP2p(Channel c) {
-        if (c == null) return;
-        c.mAsyncChannel.sendMessage(ENABLE_P2P);
-    }
-
-    /**
-     * Sends in a request to the system to disable p2p. This will pop up a dialog
-     * to the user and upon authorization will enable p2p.
-     * @hide
-     */
-    public void disableP2p(Channel c) {
-        if (c == null) return;
-        c.mAsyncChannel.sendMessage(DISABLE_P2P);
-    }
-
-    /**
      * Initiate peer discovery. A discovery process involves scanning for available Wi-Fi peers
      * for the purpose of establishing a connection.
      *
@@ -503,6 +478,16 @@
     }
 
     /**
+     * TODO: Add more documentation before opening up
+     * Cancel peer discovery
+     * @hide
+     */
+    public void stopPeerDiscovery(Channel c, ActionListener listener) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, c.putListener(listener));
+    }
+
+    /**
      * Start a p2p connection to a device with the specified configuration.
      *
      * <p> The function call immediately returns after sending a connection request
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 69cbb5c..5b0e424 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -49,6 +49,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
@@ -84,7 +85,7 @@
  */
 public class WifiP2pService extends IWifiP2pManager.Stub {
     private static final String TAG = "WifiP2pService";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final String NETWORKTYPE = "WIFI_P2P";
 
     private Context mContext;
@@ -94,11 +95,6 @@
     INetworkManagementService mNwService;
     private DhcpStateMachine mDhcpStateMachine;
 
-    //Tracked to notify the user about wifi client/hotspot being shut down
-    //during p2p bring up
-    private int mWifiState = WifiManager.WIFI_STATE_DISABLED;
-    private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
-
     private P2pStateMachine mP2pStateMachine;
     private AsyncChannel mReplyChannel = new AsyncChannel();
     private AsyncChannel mWifiChannel;
@@ -110,6 +106,9 @@
     private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
     private static int mGroupCreatingTimeoutIndex = 0;
 
+    /* Set a two minute discover timeout to avoid STA scans from being blocked */
+    private static final int DISCOVER_TIMEOUT_S = 120;
+
     /**
      * Delay between restarts upon failure to setup connection with supplicant
      */
@@ -124,28 +123,13 @@
 
     private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
 
-    /* Message sent to WifiStateMachine to indicate p2p enable is pending */
-    public static final int P2P_ENABLE_PENDING              =   BASE + 1;
-    /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */
-    public static final int WIFI_ENABLE_PROCEED             =   BASE + 2;
-
     /* Delayed message to timeout group creation */
-    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 3;
-
-    /* User accepted to disable Wi-Fi in order to enable p2p */
-    private static final int WIFI_DISABLE_USER_ACCEPT       =   BASE + 4;
-    /* User rejected to disable Wi-Fi in order to enable p2p */
-    private static final int WIFI_DISABLE_USER_REJECT       =   BASE + 5;
+    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 1;
 
     /* User accepted a peer request */
-    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 6;
+    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 2;
     /* User rejected a peer request */
-    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 7;
-
-    /* Airplane mode changed */
-    private static final int AIRPLANE_MODE_CHANGED          =   BASE + 8;
-    /* Emergency callback mode */
-    private static final int EMERGENCY_CALLBACK_MODE        =   BASE + 9;
+    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 3;
 
     private final boolean mP2pSupported;
 
@@ -166,7 +150,7 @@
     public WifiP2pService(Context context) {
         mContext = context;
 
-        //STOPSHIP: fix this
+        //STOPSHIP: get this from native side
         mInterface = "p2p0";
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
 
@@ -179,15 +163,6 @@
 
         mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
         mP2pStateMachine.start();
-
-        // broadcasts
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
-        filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
-        mContext.registerReceiver(new WifiStateReceiver(), filter);
-
     }
 
     public void connectivityServiceReady() {
@@ -195,26 +170,6 @@
         mNwService = INetworkManagementService.Stub.asInterface(b);
     }
 
-    private class WifiStateReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_DISABLED);
-            } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
-                mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
-                        WifiManager.WIFI_AP_STATE_DISABLED);
-            } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
-                mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED);
-            } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
-                if (intent.getBooleanExtra("phoneinECMState", false) == true) {
-                    mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE);
-                }
-            }
-        }
-    }
-
     private void enforceAccessPermission() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
                 "WifiP2pService");
@@ -264,8 +219,6 @@
         private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
         private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
         private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
-        private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState();
-        private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
         private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
         private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
         // Inactive is when p2p is enabled with no connectivity
@@ -299,8 +252,6 @@
                 addState(mP2pNotSupportedState, mDefaultState);
                 addState(mP2pDisablingState, mDefaultState);
                 addState(mP2pDisabledState, mDefaultState);
-                    addState(mWaitForUserActionState, mP2pDisabledState);
-                    addState(mWaitForWifiDisableState, mP2pDisabledState);
                 addState(mP2pEnablingState, mDefaultState);
                 addState(mP2pEnabledState, mDefaultState);
                     addState(mInactiveState, mP2pEnabledState);
@@ -346,23 +297,14 @@
                     AsyncChannel ac = new AsyncChannel();
                     ac.connect(mContext, getHandler(), message.replyTo);
                     break;
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    // Disable p2p operation before we can respond
-                    sendMessage(WifiP2pManager.DISABLE_P2P);
-                    deferMessage(message);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
-                            WifiP2pManager.BUSY);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
-                            WifiP2pManager.BUSY);
-                    break;
                 case WifiP2pManager.DISCOVER_PEERS:
                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                             WifiP2pManager.BUSY);
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                            WifiP2pManager.BUSY);
+                    break;
                 case WifiP2pManager.CONNECT:
                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
                             WifiP2pManager.BUSY);
@@ -388,16 +330,14 @@
                 case WifiP2pManager.REQUEST_GROUP_INFO:
                     replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
                     break;
-                case AIRPLANE_MODE_CHANGED:
-                    if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P);
-                    break;
-                case EMERGENCY_CALLBACK_MODE:
-                    sendMessage(WifiP2pManager.DISABLE_P2P);
-                    break;
                     // Ignore
                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
-                case WIFI_DISABLE_USER_ACCEPT:
-                case WIFI_DISABLE_USER_REJECT:
+                case WifiMonitor.SCAN_RESULTS_EVENT:
+                case WifiMonitor.SUP_CONNECTION_EVENT:
+                case WifiMonitor.SUP_DISCONNECTION_EVENT:
+                case WifiMonitor.NETWORK_CONNECTION_EVENT:
+                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                 case PEER_CONNECTION_USER_ACCEPT:
                 case PEER_CONNECTION_USER_REJECT:
                 case GROUP_CREATING_TIMED_OUT:
@@ -414,22 +354,14 @@
         @Override
         public boolean processMessage(Message message) {
             switch (message.what) {
-                // Allow Wi-Fi to proceed
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    replyToMessage(message, WIFI_ENABLE_PROCEED);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
-                            WifiP2pManager.P2P_UNSUPPORTED);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
-                            WifiP2pManager.P2P_UNSUPPORTED);
-                    break;
-                case WifiP2pManager.DISCOVER_PEERS:
+               case WifiP2pManager.DISCOVER_PEERS:
                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
                 case WifiP2pManager.CONNECT:
                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
@@ -438,7 +370,7 @@
                     replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
-                case WifiP2pManager.CREATE_GROUP:
+               case WifiP2pManager.CREATE_GROUP:
                     replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
@@ -455,26 +387,15 @@
 
     class P2pDisablingState extends State {
         @Override
-        public void enter() {
-            if (DBG) logd(getName());
-            logd("stopping supplicant");
-            if (!mWifiNative.stopSupplicant()) {
-                loge("Failed to stop supplicant, issue kill");
-                mWifiNative.killSupplicant();
-            }
-        }
-
-        @Override
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
-                    logd("Supplicant connection lost");
-                    mWifiNative.closeSupplicantConnection();
+                    if (DBG) logd("p2p socket connection lost");
                     transitionTo(mP2pDisabledState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     deferMessage(message);
                     break;
                 default:
@@ -484,7 +405,6 @@
         }
     }
 
-
     class P2pDisabledState extends State {
        @Override
         public void enter() {
@@ -495,118 +415,19 @@
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
-                case WifiP2pManager.ENABLE_P2P:
-                    OnClickListener listener = new OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            if (which == DialogInterface.BUTTON_POSITIVE) {
-                                sendMessage(WIFI_DISABLE_USER_ACCEPT);
-                            } else {
-                                sendMessage(WIFI_DISABLE_USER_REJECT);
-                            }
-                        }
-                    };
-
-                    // Show a user request dialog if we know Wi-Fi client/hotspot is in operation
-                    if (mWifiState != WifiManager.WIFI_STATE_DISABLED ||
-                            mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
-                        Resources r = Resources.getSystem();
-                        AlertDialog dialog = new AlertDialog.Builder(mContext)
-                            .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
-                            .setMessage(r.getString(R.string.wifi_p2p_turnon_message))
-                            .setPositiveButton(r.getString(R.string.ok), listener)
-                            .setNegativeButton(r.getString(R.string.cancel), listener)
-                            .create();
-                        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-                        dialog.show();
-                        transitionTo(mWaitForUserActionState);
-                    } else {
-                        mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
-                        transitionTo(mWaitForWifiDisableState);
-                    }
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
-                    break;
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    replyToMessage(message, WIFI_ENABLE_PROCEED);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class WaitForUserActionState extends State {
-        @Override
-        public void enter() {
-            if (DBG) logd(getName());
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) logd(getName() + message.toString());
-            switch (message.what) {
-                case WIFI_DISABLE_USER_ACCEPT:
-                    mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
-                    transitionTo(mWaitForWifiDisableState);
-                    break;
-                case WIFI_DISABLE_USER_REJECT:
-                    logd("User rejected enabling p2p");
-                    sendP2pStateChangedBroadcast(false);
-                    transitionTo(mP2pDisabledState);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
-                    deferMessage(message);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class WaitForWifiDisableState extends State {
-        @Override
-        public void enter() {
-            if (DBG) logd(getName());
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) logd(getName() + message.toString());
-            switch (message.what) {
-                case WifiStateMachine.P2P_ENABLE_PROCEED:
+                case WifiStateMachine.CMD_ENABLE_P2P:
                     try {
-                        mNwService.wifiFirmwareReload(mInterface, "P2P");
-                    } catch (Exception e) {
-                        loge("Failed to reload p2p firmware " + e);
-                        // continue
+                        mNwService.setInterfaceUp(mInterface);
+                    } catch (RemoteException re) {
+                        loge("Unable to change interface settings: " + re);
+                    } catch (IllegalStateException ie) {
+                        loge("Unable to change interface settings: " + ie);
                     }
-
-                    //A runtime crash can leave the interface up and
-                    //this affects p2p when supplicant starts up.
-                    //Ensure interface is down before a supplicant start.
-                    try {
-                        mNwService.setInterfaceDown(mInterface);
-                    } catch (Exception e) {
-                        if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
-                    }
-
-                    if (mWifiNative.startP2pSupplicant()) {
-                        mWifiMonitor.startMonitoring();
-                        transitionTo(mP2pEnablingState);
-                    } else {
-                        notifyP2pEnableFailure();
-                        transitionTo(mP2pDisabledState);
-                    }
+                    mWifiMonitor.startMonitoring();
+                    transitionTo(mP2pEnablingState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
-                    deferMessage(message);
+                case WifiStateMachine.CMD_DISABLE_P2P:
+                    //Nothing to do
                     break;
                 default:
                     return NOT_HANDLED;
@@ -626,22 +447,15 @@
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.SUP_CONNECTION_EVENT:
-                    logd("P2p start successful");
+                    if (DBG) logd("P2p socket connection successful");
                     transitionTo(mInactiveState);
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
-                    if (++mP2pRestartCount <= P2P_RESTART_TRIES) {
-                        loge("Failed to start p2p, retry");
-                        mWifiNative.killSupplicant();
-                        sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
-                    } else {
-                        loge("Failed " + mP2pRestartCount + " times to start p2p, quit ");
-                        mP2pRestartCount = 0;
-                    }
+                    loge("P2p socket connection failed");
                     transitionTo(mP2pDisabledState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     deferMessage(message);
                     break;
                 default:
@@ -658,30 +472,36 @@
             sendP2pStateChangedBroadcast(true);
             mNetworkInfo.setIsAvailable(true);
             initializeP2pSettings();
-            showNotification();
         }
 
         @Override
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                    //Nothing to do
                     break;
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
+                    mWifiNative.closeSupplicantConnection();
                     transitionTo(mP2pDisablingState);
                     break;
                 case WifiP2pManager.DISCOVER_PEERS:
-                    int timeout = message.arg1;
-                    if (mWifiNative.p2pFind(timeout)) {
+                    if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
                     } else {
                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                                 WifiP2pManager.ERROR);
                     }
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    if (mWifiNative.p2pStopFind()) {
+                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                                WifiP2pManager.ERROR);
+                    }
+                    break;
                 case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
                     if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
@@ -692,15 +512,7 @@
                     device = (WifiP2pDevice) message.obj;
                     if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
                     break;
-               case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant died */
-                    loge("Connection lost, restart p2p");
-                    mWifiNative.killSupplicant();
-                    mWifiNative.closeSupplicantConnection();
-                    if (mPeers.clear()) sendP2pPeersChangedBroadcast();
-                    transitionTo(mP2pDisabledState);
-                    sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
-                    break;
-                default:
+               default:
                     return NOT_HANDLED;
             }
             return HANDLED;
@@ -710,7 +522,6 @@
         public void exit() {
             sendP2pStateChangedBroadcast(false);
             mNetworkInfo.setIsAvailable(false);
-            clearNotification();
         }
     }
 
@@ -719,7 +530,8 @@
         public void enter() {
             if (DBG) logd(getName());
             //Start listening every time we get inactive
-            mWifiNative.p2pListen();
+            //TODO: Fix listen after driver behavior is fixed
+            //mWifiNative.p2pListen();
         }
 
         @Override
@@ -737,6 +549,8 @@
                         //TODO: if failure, remove config and do a regular p2pConnect()
                         mWifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
                     } else {
+                        //Stop discovery before issuing connect
+                        mWifiNative.p2pStopFind();
                         //If peer is a GO, we do not need to send provisional discovery,
                         //the supplicant takes care of it.
                         if (isGroupOwner(mSavedPeerConfig.deviceAddress)) {
@@ -1114,7 +928,7 @@
                     }
                     // Do the regular device lost handling
                     return NOT_HANDLED;
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     sendMessage(WifiP2pManager.REMOVE_GROUP);
                     deferMessage(message);
                     break;
@@ -1494,54 +1308,5 @@
         Slog.e(TAG, s);
     }
 
-    private void showNotification() {
-        NotificationManager notificationManager =
-            (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager == null || mNotification != null) {
-            return;
-        }
-
-        Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
-
-        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
-        Resources r = Resources.getSystem();
-        CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title);
-        CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message);
-
-        mNotification = new Notification();
-        mNotification.when = 0;
-        //TODO: might change to be a seperate icon
-        mNotification.icon = R.drawable.stat_sys_tether_wifi;
-        mNotification.defaults &= ~Notification.DEFAULT_SOUND;
-        mNotification.flags = Notification.FLAG_ONGOING_EVENT;
-        mNotification.tickerText = title;
-        mNotification.setLatestEventInfo(mContext, title, message, pi);
-
-        notificationManager.notify(mNotification.icon, mNotification);
-    }
-
-    private void clearNotification() {
-        NotificationManager notificationManager =
-            (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager != null && mNotification != null) {
-            notificationManager.cancel(mNotification.icon);
-            mNotification = null;
-        }
-    }
-
-    private boolean isAirplaneSensitive() {
-        String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
-                Settings.System.AIRPLANE_MODE_RADIOS);
-        return airplaneModeRadios == null
-            || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
-    }
-
-    private boolean isAirplaneModeOn() {
-        return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
-    }
-
     }
 }