Merge "Add comments on remote control stack traversal order"
diff --git a/api/current.txt b/api/current.txt
index 98eeaeb..1eec50e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20248,6 +20248,7 @@
     method public android.renderscript.Type.Builder setMipmaps(boolean);
     method public android.renderscript.Type.Builder setX(int);
     method public android.renderscript.Type.Builder setY(int);
+    method public android.renderscript.Type.Builder setZ(int);
   }
 
   public static final class Type.CubemapFace extends java.lang.Enum {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d880817..60d4363 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4277,7 +4277,7 @@
 
         // Enable OpenGL tracing if required
         if (data.enableOpenGlTrace) {
-            GLUtils.enableTracing();
+            GLUtils.setTracingLevel(1);
         }
 
         /**
diff --git a/core/java/android/ddm/DdmHandleGlTracing.java b/core/java/android/ddm/DdmHandleGlTracing.java
new file mode 100644
index 0000000..511cf44
--- /dev/null
+++ b/core/java/android/ddm/DdmHandleGlTracing.java
@@ -0,0 +1,60 @@
+/*
+ * 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 android.ddm;
+
+import android.opengl.GLUtils;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
+import java.nio.ByteBuffer;
+
+public class DdmHandleGlTracing extends ChunkHandler {
+    /** GL TRace control packets. Packet data controls starting/stopping the trace. */
+    public static final int CHUNK_GLTR = type("GLTR");
+
+    private static final DdmHandleGlTracing sInstance = new DdmHandleGlTracing();
+
+    /** singleton, do not instantiate. */
+    private DdmHandleGlTracing() {}
+
+    public static void register() {
+        DdmServer.registerHandler(CHUNK_GLTR, sInstance);
+    }
+
+    @Override
+    public void connected() {
+    }
+
+    @Override
+    public void disconnected() {
+    }
+
+    @Override
+    public Chunk handleChunk(Chunk request) {
+        int type = request.type;
+
+        if (type != CHUNK_GLTR) {
+            throw new RuntimeException("Unknown packet " + ChunkHandler.name(type));
+        }
+
+        ByteBuffer in = wrapChunk(request);
+        GLUtils.setTracingLevel(in.getInt());
+        return null;    // empty response
+    }
+}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index e99fa92..70ad648 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -36,6 +36,7 @@
 
     private static DdmHandleHello mInstance = new DdmHandleHello();
 
+    private static final String[] NATIVE_FEATURES = new String[] { "opengl-tracing" };
 
     /* singleton, do not instantiate */
     private DdmHandleHello() {}
@@ -149,21 +150,27 @@
     private Chunk handleFEAT(Chunk request) {
         // TODO: query the VM to ensure that support for these features
         // is actually compiled in
-        final String[] features = Debug.getVmFeatureList();
+        final String[] vmFeatures = Debug.getVmFeatureList();
 
         if (false)
             Log.v("ddm-heap", "Got feature list request");
 
-        int size = 4 + 4 * features.length;
-        for (int i = features.length-1; i >= 0; i--)
-            size += features[i].length() * 2;
+        int size = 4 + 4 * (vmFeatures.length + NATIVE_FEATURES.length);
+        for (int i = vmFeatures.length-1; i >= 0; i--)
+            size += vmFeatures[i].length() * 2;
+        for (int i = NATIVE_FEATURES.length-1; i>= 0; i--)
+            size += NATIVE_FEATURES[i].length() * 2;
 
         ByteBuffer out = ByteBuffer.allocate(size);
         out.order(ChunkHandler.CHUNK_ORDER);
-        out.putInt(features.length);
-        for (int i = features.length-1; i >= 0; i--) {
-            out.putInt(features[i].length());
-            putString(out, features[i]);
+        out.putInt(vmFeatures.length + NATIVE_FEATURES.length);
+        for (int i = vmFeatures.length-1; i >= 0; i--) {
+            out.putInt(vmFeatures[i].length());
+            putString(out, vmFeatures[i]);
+        }
+        for (int i = NATIVE_FEATURES.length-1; i >= 0; i--) {
+            out.putInt(NATIVE_FEATURES[i].length());
+            putString(out, NATIVE_FEATURES[i]);
         }
 
         return new Chunk(CHUNK_FEAT, out);
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index ecd450d..2c82967 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -51,6 +51,7 @@
         DdmHandleNativeHeap.register();
         DdmHandleProfiling.register();
         DdmHandleExit.register();
+        DdmHandleGlTracing.register();
 
         DdmServer.registrationComplete();
     }
diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java
index 9c0f754..bc3d035 100644
--- a/core/java/android/webkit/GeolocationPermissions.java
+++ b/core/java/android/webkit/GeolocationPermissions.java
@@ -61,7 +61,8 @@
     };
 
     /**
-     * Gets the singleton instance of this class.
+     * Gets the singleton instance of this class. This method cannot be
+     * called before the application instantiates a {@link WebView} instance.
      *
      * @return the singleton {@link GeolocationPermissions} instance
      */
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 634efa6..44af199 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -557,9 +557,9 @@
 }
 
 extern void setGLDebugLevel(int level);
-void nativeEnableTracing(JNIEnv *env, jclass clazz)
+void setTracingLevel(JNIEnv *env, jclass clazz, jint level)
 {
-    setGLDebugLevel(1);
+    setGLDebugLevel(level);
 }
 
 static int checkFormat(SkBitmap::Config config, int format, int type)
@@ -1032,7 +1032,7 @@
     { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
     { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
     { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
-    { "native_enableTracing", "()V",                    (void*)nativeEnableTracing },
+    { "setTracingLevel", "(I)V",                        (void*)setTracingLevel },
 };
 
 static JNINativeMethod gEtc1Methods[] = {
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index 28746ce..463d3c0 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -52,6 +52,7 @@
 
     if (osb == NULL || osb->getError() != NO_ERROR) {
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        delete osb;
         return 0;
     }
 
diff --git a/docs/html/guide/topics/security/permissions.jd b/docs/html/guide/topics/security/permissions.jd
index 3013e38..4ad9b7c 100644
--- a/docs/html/guide/topics/security/permissions.jd
+++ b/docs/html/guide/topics/security/permissions.jd
@@ -61,6 +61,7 @@
 are sandboxed in the same way and have the same degree of security from each
 other.</p>
 
+
 <a name="signing"></a>
 <h2>Application Signing</h2>
 
@@ -112,7 +113,7 @@
 <a name="permissions"></a>
 <h2>Using Permissions</h2>
 
-<p>A basic Android application has no permissions associated with it,
+<p>A basic Android application has no permissions associated with it by default,
 meaning it can not do anything that would adversely impact the user experience
 or any data on the device.  To make use of protected features of the device,
 you must include in your <code>AndroidManifest.xml</code> one or more
@@ -165,6 +166,33 @@
 </ul>
 
 
+
+<div class="caution">
+<p><strong>Caution:</strong> Over time,
+new restrictions may be added to the platform such that, in order
+to use certain APIs, your app must request a permission that it previously did not need.
+Because existing apps assume access to those APIs is freely available,
+Android may apply the new permission request to the app's manifest to avoid
+breaking the app on the new platform version.
+Android makes the decision as to whether an app might need the permission based on
+the value provided for the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
+attribute. If the value is lower than the version in which the permission was added, then
+Android adds the permission.</p>
+<p>For example, the {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission was
+added in API level 4 to restrict access to the shared storage space. If your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
+is 3 or lower, this permission is added to your app on newer versions of Android.</p>
+<p>Beware that if this happens to your app, your app listing on Google Play will show these
+required permissions even though your app might not actually require them.</p>
+<p>To avoid this and remove the default permissions you don't need, always update your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
+to be as high as possible. You can see which permissions were added with each release in the
+{@link android.os.Build.VERSION_CODES} documentation.</p>
+</div>
+
+
+
 <a name="declaring"></a>
 <h2>Declaring and Enforcing Permissions</h2>
 
diff --git a/docs/html/guide/topics/ui/controls/text.jd b/docs/html/guide/topics/ui/controls/text.jd
index 2d9d215..654883d 100644
--- a/docs/html/guide/topics/ui/controls/text.jd
+++ b/docs/html/guide/topics/ui/controls/text.jd
@@ -79,15 +79,23 @@
 </pre>
 
 
-<p>There are several different input types available for different situations. You can find
-them all listed with the documentation for <a
-href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType">{@code
-android:inputType}</a>.</p>
+<p>There are several different input types available for different situations.
+Here are some of the more common values for
+<a href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType"
+>{@code android:inputType}</a>:</p>
 
-<p class="note"><strong>Tip:</strong> To allow users to input long strings of text with line
-breaks, use the {@code "textMultiLine"} input type. By default, an {@link android.widget.EditText}
-object is restricted to one line of text and scrolls horizontally when the text exceeds the
-available width.</p>
+<dl>
+  <dt>{@code "text"}</dt>
+    <dd>Normal text keyboard.</dd>
+  <dt>{@code "textEmailAddress"}</dt>
+    <dd>Normal text keyboard with the @ character.</dd>
+  <dt>{@code "textUri"}</dt>
+    <dd>Normal text keyboard with the / character.</dd>
+  <dt>{@code "number"}</dt>
+    <dd>Basic number keypad.</dd>
+  <dt>{@code "phone"}</dt>
+    <dd>Phone-style keypad.</dd>
+</dl>
 
 
 <h3 id="Behaviors">Controlling other behaviors</h3>
@@ -98,7 +106,25 @@
 
 <p>The <a href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType">{@code
 android:inputType}</a> attribute allows bitwise combinations so you can specify both a keyboard
-layout and one or more behaviors at once. For example, here's how you can collect a postal
+layout and one or more behaviors at once.</p>
+
+<p>Here are some of the common input type values that define keyboard behaviors:</p>
+
+<dl>
+  <dt>{@code "textCapSentences"}</dt>
+    <dd>Normal text keyboard that capitalizes the first letter for each new sentence.</dd>
+  <dt>{@code "textCapWords"}</dt>
+    <dd>Normal text keyboard that capitalizes every word. Good for titles or person names.</dd>
+  <dt>{@code "textAutoCorrect"}</dt>
+    <dd>Normal text keyboard that corrects commonly misspelled words.</dd>
+  <dt>{@code "textPassword"}</dt>
+    <dd>Normal text keyboard, but the characters entered turn into dots.</dd>
+  <dt>{@code "textMultiLine"}</dt>
+    <dd>Normal text keyboard that allow users to input long strings of text that include line
+breaks (carriage returns).</dd>
+</dl>
+
+<p>For example, here's how you can collect a postal
 address, capitalize each word, and disable text suggestions:</p>
 
 <pre>
@@ -177,7 +203,7 @@
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
         boolean handled = false;
         if (actionId == EditorInfo.IME_ACTION_SEND) {
-            // Send the user message
+            sendMessage();
             handled = true;
         }
         return handled;
diff --git a/docs/html/guide/webapps/webview.jd b/docs/html/guide/webapps/webview.jd
index 9ea0a10..ce7fe27 100644
--- a/docs/html/guide/webapps/webview.jd
+++ b/docs/html/guide/webapps/webview.jd
@@ -162,7 +162,7 @@
     Context mContext;
 
     /** Instantiate the interface and set the context */
-    JavaScriptInterface(Context c) {
+    WebAppInterface(Context c) {
         mContext = c;
     }
 
diff --git a/docs/html/images/training/input/ime_autocorrect.png b/docs/html/images/training/input/ime_autocorrect.png
new file mode 100644
index 0000000..fd8371b
--- /dev/null
+++ b/docs/html/images/training/input/ime_autocorrect.png
Binary files differ
diff --git a/docs/html/images/training/input/ime_password.png b/docs/html/images/training/input/ime_password.png
new file mode 100644
index 0000000..6270c30
--- /dev/null
+++ b/docs/html/images/training/input/ime_password.png
Binary files differ
diff --git a/docs/html/training/basics/activity-lifecycle/recreating.jd b/docs/html/training/basics/activity-lifecycle/recreating.jd
index 1b88e19..1a65e71 100644
--- a/docs/html/training/basics/activity-lifecycle/recreating.jd
+++ b/docs/html/training/basics/activity-lifecycle/recreating.jd
@@ -39,7 +39,7 @@
 <p>When your activity is destroyed because the user presses <em>Back</em> or the activity finishes
 itself, the system's concept of that {@link android.app.Activity} instance is gone forever because
 the behavior indicates the activity is no longer needed. However, if the system destroys
-the activity due to system constraints (rather than normal app behavior), then althought the actual
+the activity due to system constraints (rather than normal app behavior), then although the actual
 {@link android.app.Activity} instance is gone, the system remembers that it existed such that if
 the user navigates back to it, the system creates a new instance of the activity using a set of
 saved data that describes the state of the activity when it was destroyed. The saved data that the
diff --git a/docs/html/training/best-user-input.jd b/docs/html/training/best-user-input.jd
new file mode 100644
index 0000000..7f5ed15
--- /dev/null
+++ b/docs/html/training/best-user-input.jd
@@ -0,0 +1,9 @@
+page.title=Best Practices for User Input
+page.trainingcourse=true
+
+@jd:body
+
+
+
+<p>These classes cover various subjects of user input, such as
+touch screen gestures and text input through on-screen input methods and hardware keyboards.</p>
\ No newline at end of file
diff --git a/docs/html/training/gestures/detector.jd b/docs/html/training/gestures/detector.jd
new file mode 100644
index 0000000..06d0e98
--- /dev/null
+++ b/docs/html/training/gestures/detector.jd
@@ -0,0 +1,341 @@
+page.title=Detecting Common Gestures
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Tracking Movement
+next.link=movement.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#data">Gather Data</a></li>
+  <li><a href="#detect">Detect Gestures</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+
+<ul>
+   <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
+    </li>
+    <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
+    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
+    <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
+    <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
+    <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
+</ul>
+
+
+</div>
+</div>
+
+<p>A "touch gesture" occurs when a user places one or more fingers on the touch
+screen, and your application interprets
+that pattern of touches as a particular gesture. There are correspondingly two
+phases to gesture detection:</p>
+
+<ol>
+  <li>Gathering data about touch events.</li>
+  
+  <li>Interpreting the data to see if it meets the criteria for any of the
+gestures your app supports. </li> 
+
+</ol>
+
+<h4>Support Library Classes</h4>
+
+<p>The examples in this lesson use the {@link android.support.v4.view.GestureDetectorCompat}
+and {@link android.support.v4.view.MotionEventCompat} classes. These classes are in the 
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a>. You should use
+Support Library classes where possible to provide compatibility with devices 
+running Android 1.6 and higher. Note that {@link android.support.v4.view.MotionEventCompat} is <em>not</em> a 
+replacement for the {@link android.view.MotionEvent} class. Rather, it provides static utility 
+methods to which you pass your {@link android.view.MotionEvent} object in order to receive 
+the desired action associated with that event.</p>
+
+<h2 id="data">Gather Data</h2>
+
+<p>When a user places one or more fingers on the screen, this  triggers the
+callback {@link android.view.View#onTouchEvent onTouchEvent()} 
+on the View that received the touch events.
+For each sequence of touch events (position, pressure, size, addition of another finger, etc.) 
+that is ultimately identified as a gesture,
+{@link android.view.View#onTouchEvent onTouchEvent()} is fired several times.</p>
+
+<p>The gesture starts when the user first touches the screen, continues as the system tracks
+the position of the user's finger(s), and ends by capturing the final event of
+the user's fingers leaving the screen. Throughout this interaction, 
+the {@link android.view.MotionEvent} delivered to {@link android.view.View#onTouchEvent onTouchEvent()} 
+provides the details of every interaction. Your app can use the data provided by the {@link android.view.MotionEvent} 
+to determine if a gesture it cares
+about happened.</p>
+
+<h3>Capturing touch events for an Activity or View</h3>
+
+<p><p>To intercept touch events in an Activity or View, override 
+the {@link android.view.View#onTouchEvent onTouchEvent()} callback.</p>
+
+<p>The following snippet uses 
+{@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()}
+to extract the action the user performed from the {@code event} parameter. This gives you the raw 
+data you need to determine if a gesture you care about occurred:</p>
+
+<pre>
+public class MainActivity extends Activity {
+...
+// This example shows an Activity, but you would use the same approach if
+// you were subclassing a View.
+&#64;Override
+public boolean onTouchEvent(MotionEvent event){ 
+        
+    int action = MotionEventCompat.getActionMasked(event);
+        
+    switch(action) {
+        case (MotionEvent.ACTION_DOWN) :
+            Log.d(DEBUG_TAG,"Action was DOWN");
+            return true;
+        case (MotionEvent.ACTION_MOVE) :
+            Log.d(DEBUG_TAG,"Action was MOVE");
+            return true;
+        case (MotionEvent.ACTION_UP) :
+            Log.d(DEBUG_TAG,"Action was UP");
+            return true;
+        case (MotionEvent.ACTION_CANCEL) :
+            Log.d(DEBUG_TAG,"Action was CANCEL");
+            return true;
+        case (MotionEvent.ACTION_OUTSIDE) :
+            Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
+                    "of current screen element");
+            return true;      
+        default : 
+            return super.onTouchEvent(event);
+    }      
+}</pre>
+
+<p>You can then do your own processing on these events to determine if a
+gesture occurred. This is the kind of processing you would have to do for a
+custom gesture. However, if your app uses
+common gestures such as double tap, long press, fling, and so on, you can
+take advantage of the {@link
+android.view.GestureDetector} class. {@link
+android.view.GestureDetector} makes it easy for you to detect common
+gestures without processing the individual touch events yourself. This is
+discussed below in <a href="#detect">Detect Gestures</a>.</p>
+
+<h3>Capturing touch events for a single view</h3>
+
+<p>As an alternative to {@link android.view.View#onTouchEvent onTouchEvent()},
+you can attach an {@link android.view.View.OnTouchListener} object to any {@link
+android.view.View} object using the {@link android.view.View#setOnTouchListener
+setOnTouchListener()} method. This makes it possible to to listen for touch
+events without subclassing an existing {@link android.view.View}. For
+example:</p>
+
+<pre>View myView = findViewById(R.id.my_view); 
+myView.setOnTouchListener(new OnTouchListener() {
+    public boolean onTouch(View v, MotionEvent event) {
+        // ... Respond to touch events       
+        return true;
+    }
+});</pre>
+
+<p>Beware of creating a listener that returns {@code false} for the 
+{@link android.view.MotionEvent#ACTION_DOWN} event. If you do this, the listener will 
+not be called for the subsequent {@link android.view.MotionEvent#ACTION_MOVE} 
+and {@link android.view.MotionEvent#ACTION_UP} string of events. This is because
+{@link android.view.MotionEvent#ACTION_DOWN} is the starting point for all touch events.</p>
+
+<p>If you are creating a custom View, you can override 
+{@link android.view.View#onTouchEvent onTouchEvent()}, 
+as described above.</p>
+
+<h2 id="detect">Detect Gestures</h2>
+
+<p>Android provides the {@link android.view.GestureDetector} class for detecting
+common gestures. Some of the gestures it supports include {@link
+android.view.GestureDetector.OnGestureListener#onDown onDown()}, {@link
+android.view.GestureDetector.OnGestureListener#onLongPress onLongPress()},
+{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}, and so
+on. You can use {@link android.view.GestureDetector} in conjunction with the 
+{@link android.view.View#onTouchEvent onTouchEvent()}
+method described above.</p>
+
+
+<h3>Detecting All Supported Gestures</h3>
+
+<p>When you instantiate a {@link android.support.v4.view.GestureDetectorCompat} 
+object, one of the parameters it takes is a class that implements the 
+{@link android.view.GestureDetector.OnGestureListener} interface.  
+{@link android.view.GestureDetector.OnGestureListener} notifies users when 
+a particular touch event has occurred. To make it possible for your 
+{@link android.view.GestureDetector} object to receive events, you override 
+the View or Activity's {@link android.view.View#onTouchEvent onTouchEvent()} method, 
+and pass along all observed events to the detector instance.</p>
+
+
+<p>In the following snippet, a return value of {@code true} from the individual 
+{@code on<em>&lt;TouchEvent&gt;</em>} methods indicates that you
+have handled the touch event. A return value of {@code false} passes events down
+through the view stack until the touch has been successfully handled.</p>
+
+<p>Run the following snippet to get a feel for how actions are triggered when
+you interact with the touch screen, and what the contents of the {@link
+android.view.MotionEvent} are for each touch event. You will realize how much
+data is being generated for even simple interactions.</p>
+
+<pre>public class MainActivity extends Activity implements 
+        GestureDetector.OnGestureListener,
+        GestureDetector.OnDoubleTapListener{
+    
+    private static final String DEBUG_TAG = "Gestures";
+    private GestureDetectorCompat mDetector; 
+
+    // Called when the activity is first created. 
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        // Instantiate the gesture detector with the
+        // application context and an implementation of
+        // GestureDetector.OnGestureListener
+        mDetector = new GestureDetectorCompat(this,this);
+        // Set the gesture detector as the double tap
+        // listener.
+        mDetector.setOnDoubleTapListener(this);
+    }
+
+    &#64;Override 
+    public boolean onTouchEvent(MotionEvent event){ 
+        this.mDetector.onTouchEvent(event);
+        // Be sure to call the superclass implementation
+        return super.onTouchEvent(event);
+    }
+
+    &#64;Override
+    public boolean onDown(MotionEvent event) { 
+        Log.d(DEBUG_TAG,"onDown: " + event.toString()); 
+        return true;
+    }
+
+    &#64;Override
+    public boolean onFling(MotionEvent event1, MotionEvent event2, 
+            float velocityX, float velocityY) {
+        Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
+        return true;
+    }
+
+    &#64;Override
+    public void onLongPress(MotionEvent event) {
+        Log.d(DEBUG_TAG, "onLongPress: " + event.toString()); 
+    }
+
+    &#64;Override
+    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+            float distanceY) {
+        Log.d(DEBUG_TAG, "onScroll: " + e1.toString()+e2.toString());
+        return true;
+    }
+
+    &#64;Override
+    public void onShowPress(MotionEvent event) {
+        Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
+    }
+
+    &#64;Override
+    public boolean onSingleTapUp(MotionEvent event) {
+        Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
+        return true;
+    }
+
+    &#64;Override
+    public boolean onDoubleTap(MotionEvent event) {
+        Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
+        return true;
+    }
+
+    &#64;Override
+    public boolean onDoubleTapEvent(MotionEvent event) {
+        Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
+        return true;
+    }
+
+    &#64;Override
+    public boolean onSingleTapConfirmed(MotionEvent event) {
+        Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
+        return true;
+    }
+}</pre>
+
+<h3>Detecting a Subset of Supported Gestures</h3>
+
+<p>If you only want to process a few gestures, you can extend {@link
+android.view.GestureDetector.SimpleOnGestureListener} instead of implementing
+the {@link android.view.GestureDetector.OnGestureListener} interface. </p>
+<p>
+{@link
+android.view.GestureDetector.SimpleOnGestureListener} provides an implementation
+for all of the {@code on<em>&lt;TouchEvent&gt;</em>} methods by returning {@code false}
+for all of them. Thus you can override only the methods you care about.
+For
+example, the snippet below creates a class that extends {@link
+android.view.GestureDetector.SimpleOnGestureListener} and overrides {@link
+android.view.GestureDetector.OnGestureListener#onFling onFling()} and {@link
+android.view.GestureDetector.OnGestureListener#onDown onDown()}.</p>
+
+<p>Whether or not you use {@link android.view.GestureDetector.OnGestureListener}, 
+it's best practice to implement an 
+{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} 
+method that returns {@code true}. This is because all gestures begin with an 
+{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} message. If you return 
+{@code false} from {@link android.view.GestureDetector.OnGestureListener#onDown onDown()}, 
+as {@link android.view.GestureDetector.SimpleOnGestureListener} does by default, 
+the system assumes that you want to ignore the rest of the gesture, and the other methods of 
+{@link android.view.GestureDetector.OnGestureListener} never get called. 
+This has the potential to cause unexpected problems in your app. 
+The only time you should return {@code false} from 
+{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} 
+is if you truly want to ignore an entire gesture. </p>
+
+<pre>public class MainActivity extends Activity { 
+    
+    private GestureDetectorCompat mDetector; 
+
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        mDetector = new GestureDetectorCompat(this, new MyGestureListener());
+    }
+
+    &#64;Override 
+    public boolean onTouchEvent(MotionEvent event){ 
+        this.mDetector.onTouchEvent(event);
+        return super.onTouchEvent(event);
+    }
+    
+    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
+        private static final String DEBUG_TAG = "Gestures"; 
+        
+        &#64;Override
+        public boolean onDown(MotionEvent event) { 
+            Log.d(DEBUG_TAG,"onDown: " + event.toString()); 
+            return true;
+        }
+
+        &#64;Override
+        public boolean onFling(MotionEvent event1, MotionEvent event2, 
+                float velocityX, float velocityY) {
+            Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
+            return true;
+        }
+    }
+}
+</pre>
+
+
diff --git a/docs/html/training/gestures/index.jd b/docs/html/training/gestures/index.jd
new file mode 100644
index 0000000..0191450
--- /dev/null
+++ b/docs/html/training/gestures/index.jd
@@ -0,0 +1,94 @@
+page.title=Using Touch Gestures
+trainingnavtop=true
+startpage=true
+next.title=Detect Built-in Gestures
+next.link=detector.html
+
+
+@jd:body
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+
+<ul>
+  <li>Android 1.6 (API Level 4) or higher</li>
+</ul>
+<h2>You should also read</h2>
+<ul>
+    <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
+    </li>
+    <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
+    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
+    <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
+    <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
+    <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
+</ul>
+
+
+</div>
+</div>
+
+<p> This class describes how to write apps that allow users to interact with an
+app via touch gestures. Android provides a variety of APIs to
+help you create and detect gestures.</p>
+
+<p>Although your app should not depend on touch gestures for basic behaviors (since the gestures
+may not be available to all users in all contexts), adding touch-based
+interaction to your app can greatly increase its usefulness and appeal.</p>
+
+<p>To
+provide users with a consistent, intuitive experience, your app should follow
+the accepted Android conventions for touch gestures. The <a
+href="http://developer.android.com/design/patterns/gestures.html">Gestures
+design guide</a><a href="{@docRoot}design/patterns/notifications.html"></a>
+shows you how to use common gestures in Android apps. Also see the Design Guide
+for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a>.  </p>
+
+
+<h2>Lessons</h2>
+
+<dl>
+    <dt>
+        <strong><a href="detector.html">Detecting Common Gestures</a></strong>
+    </dt>
+    <dd>
+        Learn how to detect basic touch gestures such as scrolling, flinging, and double-tapping, using 
+       {@link android.view.GestureDetector}.
+    </dd>
+
+<dt>
+        <strong><a href="movement.html">Tracking Movement</a></strong>
+    </dt>
+    <dd>
+        Learn how to track movement.
+    </dd>
+
+<dt>
+        <strong><a href="scroll.html">Animating a Scroll Gesture</a></strong>
+    </dt>
+    <dd>
+        Learn how to use scrollers ({@link android.widget.Scroller} or {@link
+android.widget.OverScroller}) to produce a scrolling animation in response to a
+touch event. </dd>
+
+<dt>
+        <strong><a href="multi.html">Handling Multi-Touch Gestures</a></strong>
+    </dt>
+    <dd>
+        Learn how to detect multi-pointer (finger) gestures.
+    </dd> 
+<dt>
+        <strong><a href="scale.html">Dragging and Scaling</a></strong>
+    </dt>
+    <dd>
+        Learn how to implement touch-based dragging and scaling.
+    </dd>
+
+
+<dt><strong><a href="viewgroup.html">Managing Touch Events in a ViewGroup</a></strong></dt>
+
+    <dd>Learn how to manage touch events in a {@link android.view.ViewGroup} to
+ensure that touch events are correctly dispatched to their target views.</dd>
+</dl>
diff --git a/docs/html/training/gestures/movement.jd b/docs/html/training/gestures/movement.jd
new file mode 100644
index 0000000..f2c49d7
--- /dev/null
+++ b/docs/html/training/gestures/movement.jd
@@ -0,0 +1,151 @@
+page.title=Tracking Movement
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Animating a Scroll Gesture
+next.link=scroll.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#velocity">Track Velocity</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+
+<ul>
+    <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
+    </li>
+    <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
+    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
+    <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
+    <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
+    <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
+</ul>
+
+
+</div>
+</div>
+
+<p>This lesson describes how to track movement in touch events.</p>
+
+<p>A new {@link
+android.view.View#onTouchEvent onTouchEvent()} is triggered with an {@link
+android.view.MotionEvent#ACTION_MOVE} event whenever the current touch contact
+position, pressure, or size changes. As described in <a
+href="detector.html">Detecting Common Gestures</a>, all of these events are
+recorded in the {@link android.view.MotionEvent} parameter of {@link
+android.view.View#onTouchEvent onTouchEvent()}.</p>
+
+<p>Because finger-based touch isn't always the most precise form of interaction,
+detecting touch events is often based more on movement than on simple contact.
+To help apps distinguish between movement-based gestures (such as a swipe) and
+non-movement gestures (such as a single tap), Android includes the notion of
+"touch slop." Touch slop refers to the distance in pixels a user's touch can wander
+before the gesture is interpreted as a movement-based gesture. For more discussion of this 
+topic, see <a href="viewgroup.html#vc">Managing Touch Events in a ViewGroup</a>.</p>
+
+
+
+<p>There are several different ways to track movement in a gesture, depending on
+the needs of your application. For example:</p> 
+
+<ul>
+
+<li>The starting and ending position of a pointer (for example, move an
+on-screen object from point A to point B).</li>
+
+<li>The direction the pointer is traveling in, as determined by the x and y coordinates.</li>
+
+<li>History. You can find the size of a gesture's history by calling the {@link
+android.view.MotionEvent} method {@link android.view.MotionEvent#getHistorySize
+getHistorySize()}. You can then obtain the positions, sizes, time, and pressures
+of each of the historical events by using the motion event's {@code
+getHistorical<em>&lt;Value&gt;</em>} methods. History is useful when rendering a trail of the user's finger, 
+such as for touch drawing. See the {@link android.view.MotionEvent} reference for
+details.</li>
+
+<li>The velocity of the pointer as it moves across the touch screen.</li>
+
+</ul>
+
+
+
+<h2 id="velocity">Track Velocity</h2>
+
+<p> You could have a movement-based gesture that is simply based on the distance and/or direction the pointer traveled. But velocity often is a
+determining factor in tracking a gesture's characteristics or even deciding
+whether the gesture occurred. To make velocity calculation easier, Android
+provides the {@link android.view.VelocityTracker} class and the  	
+{@link android.support.v4.view.VelocityTrackerCompat} class in the 
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a>.
+{@link
+android.view.VelocityTracker} helps you track the velocity of touch events. This
+is useful for gestures in which velocity is part of the criteria for the
+gesture, such as a fling.</p>
+
+
+<p>Here is a simple example that illustrates the purpose of the methods in the 
+{@link android.view.VelocityTracker} API:</p>
+
+<pre>public class MainActivity extends Activity {
+    private static final String DEBUG_TAG = "Velocity";
+        ...
+    private VelocityTracker mVelocityTracker = null;
+    &#64;Override
+    public boolean onTouchEvent(MotionEvent event) {
+        int index = event.getActionIndex();
+        int action = event.getActionMasked();
+        int pointerId = event.getPointerId(index);
+
+        switch(action) {
+            case MotionEvent.ACTION_DOWN:
+                if(mVelocityTracker == null) {
+                    // Retrieve a new VelocityTracker object to watch the velocity of a motion.
+                    mVelocityTracker = VelocityTracker.obtain();
+                }
+                else {
+                    // Reset the velocity tracker back to its initial state.
+                    mVelocityTracker.clear();
+                }
+                // Add a user's movement to the tracker.
+                mVelocityTracker.addMovement(event);
+                break;
+            case MotionEvent.ACTION_MOVE:
+                mVelocityTracker.addMovement(event);
+                // When you want to determine the velocity, call 
+                // computeCurrentVelocity(). Then call getXVelocity() 
+                // and getYVelocity() to retrieve the velocity for each pointer ID. 
+                mVelocityTracker.computeCurrentVelocity(1000);
+                // Log velocity of pixels per second
+                // Best practice to use VelocityTrackerCompat where possible.
+                Log.d("", "X velocity: " + 
+                        VelocityTrackerCompat.getXVelocity(mVelocityTracker, 
+                        pointerId));
+                Log.d("", "Y velocity: " + 
+                        VelocityTrackerCompat.getYVelocity(mVelocityTracker,
+                        pointerId));
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                // Return a VelocityTracker object back to be re-used by others.
+                mVelocityTracker.recycle();
+                break;
+        }
+        return true;
+    }
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> Note that you should calculate velocity after an 
+{@link android.view.MotionEvent#ACTION_MOVE} event, 
+not after {@link android.view.MotionEvent#ACTION_UP}. After an {@link android.view.MotionEvent#ACTION_UP}, 
+the X and Y velocities will be 0.
+</p>
diff --git a/docs/html/training/gestures/multi.jd b/docs/html/training/gestures/multi.jd
new file mode 100644
index 0000000..d4c5b1d
--- /dev/null
+++ b/docs/html/training/gestures/multi.jd
@@ -0,0 +1,168 @@
+page.title=Handling Multi-Touch Gestures
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Dragging and Scaling
+next.link=scale.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#track">Track Multiple Pointers</a></li>
+  <li><a href="#action">Get a MotionEvent's Action</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+
+<ul>
+   <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
+    </li>
+    <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
+    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
+    <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
+    <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
+    <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
+</ul>
+
+
+</div>
+</div>
+
+<p>A multi-touch gesture is when multiple pointers (fingers) touch the screen
+at the same time. This lesson describes how to detect gestures that involve
+multiple pointers.</p>
+
+<h2 id="track">Track Multiple Pointers</h2>
+
+<p>When multiple pointers touch the screen at the same time, the system generates the 
+following touch events:</p>
+
+<ul>
+  <li>{@link android.view.MotionEvent#ACTION_DOWN}&mdash;For the first pointer that 
+touches the screen. This starts the gesture. The pointer data for this pointer is 
+always at index 0 in the {@link android.view.MotionEvent}.</li>
+  <li>{@link android.support.v4.view.MotionEventCompat#ACTION_POINTER_DOWN}&mdash;For 
+extra pointers that enter the screen beyond the first. The pointer data for this 
+pointer is at the index returned by {@link android.support.v4.view.MotionEventCompat#getActionIndex getActionIndex()}.</li>
+  <li>{@link android.view.MotionEvent#ACTION_MOVE}&mdash;A change has happened during a press gesture.</li>
+  <li>{@link android.support.v4.view.MotionEventCompat#ACTION_POINTER_UP}&mdash;Sent when a non-primary pointer goes up.</li>
+  <li>{@link android.view.MotionEvent#ACTION_UP}&mdash;Sent when the last pointer leaves the screen.</li>
+</ul>
+
+<p>You keep track of individual pointers within a {@link
+android.view.MotionEvent} via each pointer's index and ID:</p>
+
+<ul>
+<li><strong>Index</strong>: A {@link android.view.MotionEvent} effectively 
+stores information about each pointer in an array. The index of a pointer is its position 
+within this array. Most of the {@link
+android.view.MotionEvent} methods you use to interact with pointers take the
+pointer index as a parameter, not the pointer ID. </li>
+  
+  
+  <li><strong>ID</strong>: Each pointer also has an ID mapping that stays
+persistent across touch events to allow tracking an individual pointer across 
+the entire gesture.</li>
+  
+</ul>
+
+<p>The  order in which individual pointers appear within a motion event is 
+undefined. Thus the index of a pointer can change from one event to the
+next, but the pointer ID of a pointer is guaranteed to remain  constant as long
+as the pointer remains active. Use the  {@link
+android.view.MotionEvent#getPointerId getPointerId()} method to obtain a
+pointer's ID to track the pointer across all subsequent motion events in a
+gesture. Then for successive  motion events, use the {@link
+android.view.MotionEvent#findPointerIndex findPointerIndex()} method to obtain
+the pointer index for a given pointer ID in that motion event. For example:</p>
+
+
+<pre>private int mActivePointerId;
+ 
+public boolean onTouchEvent(MotionEvent event) {
+    ....
+    // Get the pointer ID
+    mActivePointerId = event.getPointerId(0);
+
+    // ... Many touch events later...
+
+    // Use the pointer ID to find the index of the active pointer 
+    // and fetch its position
+    int pointerIndex = event.findPointerIndex(mActivePointerId);
+    // Get the pointer's current position
+    float x = event.getX(pointerIndex);
+    float y = event.getY(pointerIndex);
+}</pre>
+
+<h2 id="action">Get a MotionEvent's Action</h2>
+
+<p>You should always use the method  
+{@link android.view.MotionEvent#getActionMasked getActionMasked()} (or better yet, the compatability version 
+{@link android.support.v4.view.MotionEventCompat#getActionMasked MotionEventCompat.getActionMasked()}) to retrieve 
+the action of a
+{@link android.view.MotionEvent}. Unlike the older {@link android.view.MotionEvent#getAction getAction()} 
+method, {@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()} is designed to work with 
+multiple pointers. It returns the masked action 
+being performed, without including the pointer index bits. You can then use 
+{@link android.support.v4.view.MotionEventCompat#getActionIndex getActionIndex()} to return the index of 
+the pointer associated with the action. This is illustrated in the snippet below.</p>
+
+<p class="note"><strong>Note:</strong> This example uses the 
+{@link android.support.v4.view.MotionEventCompat}
+class. This class is in the 
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a>. You should use
+{@link android.support.v4.view.MotionEventCompat} to provide the best support for a wide range of
+platforms. Note that {@link android.support.v4.view.MotionEventCompat} is <em>not</em> a 
+replacement for the {@link android.view.MotionEvent} class. Rather, it provides static utility 
+methods to which you pass your {@link android.view.MotionEvent} object in order to receive 
+the desired action associated with that event.</p>
+
+<pre>int action = MotionEventCompat.getActionMasked(event);
+// Get the index of the pointer associated with the action.
+int index = MotionEventCompat.getActionIndex(event);
+int xPos = -1;
+int yPos = -1;
+
+Log.d(DEBUG_TAG,"The action is " + actionToString(action));
+	    
+if (event.getPointerCount() > 1) {
+    Log.d(DEBUG_TAG,"Multitouch event"); 
+    // The coordinates of the current screen contact, relative to 
+    // the responding View or Activity.  
+    xPos = (int)MotionEventCompat.getX(event, index);
+    yPos = (int)MotionEventCompat.getY(event, index);
+
+} else {
+    // Single touch event
+    Log.d(DEBUG_TAG,"Single touch event"); 
+    xPos = (int)MotionEventCompat.getX(event, index);
+    yPos = (int)MotionEventCompat.getY(event, index);
+}
+...
+
+// Given an action int, returns a string description
+public static String actionToString(int action) {
+    switch (action) {
+	        
+        case MotionEvent.ACTION_DOWN: return "Down";
+	case MotionEvent.ACTION_MOVE: return "Move";
+	case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
+	case MotionEvent.ACTION_UP: return "Up";
+	case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
+	case MotionEvent.ACTION_OUTSIDE: return "Outside";
+	case MotionEvent.ACTION_CANCEL: return "Cancel";
+    }
+    return "";
+}</pre>
+
+ 
+
+
+<p>For more discussion of multi-touch and some examples, see the lesson <a href="scale.html">Dragging and Scaling</a>.
diff --git a/docs/html/training/gestures/scale.jd b/docs/html/training/gestures/scale.jd
new file mode 100644
index 0000000..17e4085
--- /dev/null
+++ b/docs/html/training/gestures/scale.jd
@@ -0,0 +1,240 @@
+page.title=Dragging and Scaling
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Managing Touch Events in a ViewGroup
+next.link=viewgroup.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#drag">Drag an Object</a></li>
+  <li><a href="#scale">Use Touch to Perform Scaling</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+
+<ul>
+    <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
+    </li>
+    <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
+    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
+    <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
+    <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
+    <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
+</ul>
+
+
+</div>
+</div>
+<p>This lesson describes how to use touch gestures to drag and scale on-screen
+objects, using {@link android.view.View#onTouchEvent onTouchEvent()} to intercept
+touch events. Here is the original <a
+href="http://code.google.com/p/android-touchexample/">source code</a>
+for the examples used in this lesson.
+</p>
+
+<h2 id="drag">Drag an Object</h2>
+
+<p class="note">If you are targeting Android 3.0 or higher, you can use the built-in drag-and-drop event 
+listeners with {@link android.view.View.OnDragListener}, as described in 
+<a href="{@docRoot}guide/topics/ui/drag-drop.html">Drag and Drop</a>.
+
+<p>A common operation for a touch gesture is to use it to drag an object across
+the screen. The following snippet lets the user drag an on-screen image. Note
+the following:</p>
+
+<ul>
+
+<li>In a drag (or scroll) operation, the app has to keep track of the original pointer
+(finger), even if additional fingers get placed on the screen. For example,
+imagine that while dragging the image around, the user places a second finger on
+the touch screen and lifts the first finger. If your app is just tracking
+individual pointers, it will regard the second pointer as the default and move
+the image to that location.</li>
+
+<li>To prevent this from happening, your app needs to distinguish between the 
+original pointer and any follow-on pointers. To do this, it tracks the 
+{@link android.view.MotionEvent#ACTION_POINTER_DOWN} and 
+{@link android.view.MotionEvent#ACTION_POINTER_UP} events described in 
+<a href="multi.html">Handling Multi-Touch Gestures</a>. 
+{@link android.view.MotionEvent#ACTION_POINTER_DOWN} and 
+{@link android.view.MotionEvent#ACTION_POINTER_UP} are 
+passed to the {@link android.view.View#onTouchEvent onTouchEvent()} callback 
+whenever a secondary pointer goes down or up. </li>
+
+
+<li>In the {@link android.view.MotionEvent#ACTION_POINTER_UP} case, the example
+extracts this index and ensures that the active pointer ID is not referring to a
+pointer that is no longer touching the screen. If it is, the app selects a
+different pointer to be active and saves its current X and Y position. Since
+this saved position is used in the {@link android.view.MotionEvent#ACTION_MOVE}
+case to calculate the distance to move the onscreen object, the app will always
+calculate the distance to move using data from the correct pointer.</li>
+
+</ul>
+
+<p>The following snippet enables a user to drag an object around on the screen. It records the initial
+position of the active pointer, calculates the distance the pointer traveled, and moves the object to the
+new position. It correctly manages the possibility of additional pointers, as described
+above.</p> 
+
+<p>Notice that the snippet uses the {@link android.view.MotionEvent#getActionMasked getActionMasked()} method. 
+You should always use this method (or better yet, the compatability version 
+{@link android.support.v4.view.MotionEventCompat#getActionMasked MotionEventCompat.getActionMasked()}) 
+to retrieve the action of a
+{@link android.view.MotionEvent}. Unlike the older 
+{@link android.view.MotionEvent#getAction getAction()} 
+method, {@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()} 
+is designed to work with multiple pointers. It returns the masked action 
+being performed, without including the pointer index bits.</p>
+
+<pre>// The ‘active pointer’ is the one currently moving our object.
+private int mActivePointerId = INVALID_POINTER_ID;
+
+&#64;Override
+public boolean onTouchEvent(MotionEvent ev) {
+    // Let the ScaleGestureDetector inspect all events.
+    mScaleDetector.onTouchEvent(ev);
+             
+    final int action = MotionEventCompat.getActionMasked(ev); 
+        
+    switch (action) { 
+    case MotionEvent.ACTION_DOWN: {
+        final int pointerIndex = MotionEventCompat.getActionIndex(ev); 
+        final float x = MotionEventCompat.getX(ev, pointerIndex); 
+        final float y = MotionEventCompat.getY(ev, pointerIndex); 
+            
+        // Remember where we started (for dragging)
+        mLastTouchX = x;
+        mLastTouchY = y;
+        // Save the ID of this pointer (for dragging)
+        mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+        break;
+    }
+            
+    case MotionEvent.ACTION_MOVE: {
+        // Find the index of the active pointer and fetch its position
+        final int pointerIndex = 
+                MotionEventCompat.findPointerIndex(ev, mActivePointerId);  
+            
+        final float x = MotionEventCompat.getX(ev, pointerIndex);
+        final float y = MotionEventCompat.getY(ev, pointerIndex);
+            
+        // Only move if the ScaleGestureDetector isn't processing a gesture.
+        if (!mScaleDetector.isInProgress()) {
+            // Calculate the distance moved
+            final float dx = x - mLastTouchX;
+            final float dy = y - mLastTouchY;
+
+            mPosX += dx;
+            mPosY += dy;
+
+            invalidate();
+        }
+        // Remember this touch position for the next move event
+        mLastTouchX = x;
+        mLastTouchY = y;
+
+        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 = MotionEventCompat.getActionIndex(ev); 
+        final int pointerId = MotionEventCompat.getPointerId(ev, 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;
+            mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex); 
+            mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex); 
+            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+        }
+        break;
+    }
+    }       
+    return true;
+}</pre>
+
+<h2 id="scale">Use Touch to Perform Scaling</h2>
+
+<p>As discussed in <a href="detector.html">Detecting Common Gestures</a>,
+{@link android.view.GestureDetector} helps you detect common gestures used by
+Android such as scrolling, flinging, and long press. For scaling, Android
+provides {@link android.view.ScaleGestureDetector}. {@link
+android.view.GestureDetector} and {@link android.view.ScaleGestureDetector} can
+be used together when you  want a view to recognize additional gestures.</p>
+
+<p>To report detected  gesture events, gesture detectors use listener objects 
+passed to their constructors. {@link android.view.ScaleGestureDetector} uses 
+{@link android.view.ScaleGestureDetector.OnScaleGestureListener}. 
+Android provides 
+{@link android.view.ScaleGestureDetector.SimpleOnScaleGestureListener} 
+as a helper class that you can extend if you don’t care about all of the reported events.</p>
+
+<p>Here is a snippet that gives you the basic idea of how to perform scaling.
+Here is the original <a
+href="http://code.google.com/p/android-touchexample/">source code</a>
+for the examples.</p>
+
+<pre>private ScaleGestureDetector mScaleDetector;
+private float mScaleFactor = 1.f;
+
+public MyCustomView(Context mContext){
+    ...
+    // View code goes here
+    ...
+    mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
+}
+
+&#64;Override
+public boolean onTouchEvent(MotionEvent ev) {
+    // Let the ScaleGestureDetector inspect all events.
+    mScaleDetector.onTouchEvent(ev);
+    return true;
+}
+
+&#64;Override
+public void onDraw(Canvas canvas) {
+    super.onDraw(canvas);
+
+    canvas.save();
+    canvas.scale(mScaleFactor, mScaleFactor);
+    ...
+    // onDraw() code goes here
+    ...
+    canvas.restore();
+}
+
+private class ScaleListener 
+        extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+    &#64;Override
+    public boolean onScale(ScaleGestureDetector detector) {
+        mScaleFactor *= detector.getScaleFactor();
+
+        // Don't let the object get too small or too large.
+        mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
+
+        invalidate();
+        return true;
+    }
+}</pre>
diff --git a/docs/html/training/gestures/scroll.jd b/docs/html/training/gestures/scroll.jd
new file mode 100644
index 0000000..955495a
--- /dev/null
+++ b/docs/html/training/gestures/scroll.jd
@@ -0,0 +1,161 @@
+page.title=Animating a Scroll Gesture
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Handling Multi-Touch Gestures
+next.link=multi.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#scroll">Implement Touch-Based Scrolling</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+
+<ul>
+    <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
+    </li>
+    <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
+    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
+    <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
+    <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
+    <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
+</ul>
+
+
+</div>
+</div>
+
+<p>In Android, scrolling is typically achieved by using the 
+{@link android.widget.ScrollView}
+class. Any standard layout that might extend beyond the bounds of its container should be 
+nested in a {@link android.widget.ScrollView} to provide a scrollable view that's 
+managed by the framework. Implementing a custom scroller should only be 
+necessary for special scenarios. This lesson describes such a scenario: displaying 
+a scrolling effect in response to touch gestures using <em>scrollers</em>.
+
+
+<p>You can use scrollers ({@link android.widget.Scroller} or {@link
+android.widget.OverScroller}) to collect the data you need to produce a
+scrolling animation in response to a touch event. {@link
+android.widget.Scroller} and {@link android.widget.OverScroller} are largely
+interchangeable&mdash;the difference is that {@link android.widget.OverScroller}
+allows temporarily scrolling beyond the minimum/maximum boundaries and springing
+back to the bounds. This is normally rendered using a "glow" effect, provided by
+the {@link android.widget.EdgeEffect} or {@link
+android.support.v4.widget.EdgeEffectCompat} classes. </p>
+
+<p>A scroller is used  to animate scrolling over time, using platform-standard
+scrolling physics (friction, velocity, etc.). The scroller itself doesn't
+actually draw anything. Scrollers track scroll offsets for you over time, but
+they don't  automatically apply those positions to your view. It's your
+responsibility to get and apply new coordinates at a rate that will make the
+scrolling animation look smooth.</p>
+
+<p class="note"><strong>Note:</strong> You generally only need to use scrollers
+when implementing scrolling yourself. {@link android.widget.ScrollView} and
+{@link android.widget.HorizontalScrollView} do all this for you do all of this for you if you nest your layout within them.</p>
+
+<h2 id = "scroll">Implement Touch-Based Scrolling</h2>
+
+
+<p>This snippet illustrates the basics of using a scroller. It uses a 
+{@link android.view.GestureDetector}, and overrides the  
+{@link android.view.GestureDetector.SimpleOnGestureListener} methods 
+{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} and 
+{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}. It also 
+overrides {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} 
+to return {@code false} since you don't need to animate a scroll.</p>
+
+
+<p>It's common to use scrollers in conjunction with a fling gesture, but they
+can be used in pretty much any context where you want the UI to display
+scrolling in response to a touch event. For example, you could override {@link
+android.view.View#onTouchEvent onTouchEvent()} to process touch events directly,
+and produce a scrolling effect in response to those touch events.</p>
+
+<pre>
+private OverScroller mScroller = new OverScroller(context);
+
+private GestureDetector.SimpleOnGestureListener mGestureListener
+        = new GestureDetector.SimpleOnGestureListener() {
+    &#64;Override
+    public boolean onDown(MotionEvent e) {
+        // Abort any active scroll animations and invalidate.
+        mScroller.forceFinished(true);
+        // There is also a compatibility version: 
+        // ViewCompat.postInvalidateOnAnimation
+        postInvalidateOnAnimation();
+        return true;
+    }
+
+    &#64;Override
+    public boolean onScroll(MotionEvent e1, MotionEvent e2, 
+            float distanceX, float distanceY) {
+        // You don't use a scroller in onScroll because you don't need to animate
+        // a scroll. The scroll occurs instantly in response to touch feedback.
+        return false;
+    }
+
+    &#64;Override
+    public boolean onFling(MotionEvent e1, MotionEvent e2, 
+            float velocityX, float velocityY) {
+        // Before flinging, abort the current animation.
+        mScroller.forceFinished(true);
+        // Begin the scroll animation
+        mScroller.fling(
+                // Current scroll position
+                startX,
+                startY,
+                // Velocities, negated for natural touch response
+                (int) -velocityX,
+                (int) -velocityY,
+                // Minimum and maximum scroll positions. The minimum scroll 
+                // position is generally zero and the maximum scroll position 
+                // is generally the content size less the screen size. So if the 
+                // content width is 1000 pixels and the screen width is 200  
+                // pixels, the maximum scroll offset should be 800 pixels.
+                minX, maxX,
+                minY, maxY,
+                // The maximum overscroll bounds. This is useful when using
+                // the EdgeEffect class to draw overscroll "glow" overlays.
+                mContentRect.width() / 2,
+                mContentRect.height() / 2);
+        // Invalidate to trigger computeScroll()
+        postInvalidateOnAnimation();
+        return true;
+    }
+};
+
+&#64;Override
+public void computeScroll() {
+    super.computeScroll();
+
+    // Compute the current scroll offsets. If this returns true, then the 
+    // scroll has not yet finished.
+    if (mScroller.computeScrollOffset()) {
+        int currX = mScroller.getCurrX();
+        int currY = mScroller.getCurrY();
+
+        // Actually render the scrolled viewport, or actually scroll the 
+        // view using View.scrollTo.
+
+        // If currX or currY are outside the bounds, render the overscroll 
+        // glow using EdgeEffect.
+
+    } else {
+        // The scroll has finished.
+    }
+}</pre>
+
+<p>For another example of scroller usage, see the <a href="http://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/view/ViewPager.java">source code</a> for the 
+{@link android.support.v4.view.ViewPager} class. It scrolls in response to flings, 
+and uses scrolling to implement the "snapping to page" animation.</p>
diff --git a/docs/html/training/gestures/viewgroup.jd b/docs/html/training/gestures/viewgroup.jd
new file mode 100644
index 0000000..257a5d8
--- /dev/null
+++ b/docs/html/training/gestures/viewgroup.jd
@@ -0,0 +1,302 @@
+page.title=Managing Touch Events in a ViewGroup
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=
+next.link=
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#intercept">Intercept Touch Events in a ViewGroup</a></li>
+  <li><a href="#vc">Use ViewConfiguration Constants</a></li>
+  <li><a href="#delegate">Extend a Child View's Touchable Area</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+
+<ul>
+    <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
+    </li>
+    <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
+    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
+    <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
+    <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
+    <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
+</ul>
+
+
+</div>
+</div>
+
+<p>Handling touch events in a {@link android.view.ViewGroup} takes special care,
+because it's common for a {@link android.view.ViewGroup} to have children that
+are targets for different touch events than the {@link android.view.ViewGroup}
+itself. To make sure that each view correctly receives the touch events intended
+for it, override the {@link android.view.ViewGroup#onInterceptTouchEvent
+onInterceptTouchEvent()} method.</p>
+
+<h2 id="intercept">Intercept Touch Events in a ViewGroup</h2>
+
+<p>The {@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()} 
+method is called whenever a touch event is detected on the surface of a 
+{@link android.view.ViewGroup}, including on the surface of its children. If 
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()} 
+returns {@code true}, the {@link android.view.MotionEvent} is intercepted, 
+meaning it will be not be passed on to the child, but rather to the 
+{@link android.view.View#onTouchEvent onTouchEvent()} method of the parent.</p>
+
+<p>The {@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()} 
+method gives a parent the chance to see any touch event before its children do. 
+If you return {@code true} from 
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()}, 
+the child view that was previously handling touch events 
+receives an {@link android.view.MotionEvent#ACTION_CANCEL}, and the events from that 
+point forward are sent to the parent's 
+{@link android.view.View#onTouchEvent onTouchEvent()} method for the usual handling. 
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()} can also 
+return {@code false} and simply spy on events as they travel down the view hierarchy 
+to their usual targets, which will handle the events with their own
+{@link android.view.View#onTouchEvent onTouchEvent()}.
+
+
+<p>In the following snippet, the class {@code MyViewGroup} extends  
+{@link android.view.ViewGroup}. 
+{@code MyViewGroup} contains multiple child views. If you drag your finger across 
+a child view horizontally, the child view should no longer get touch events, and 
+{@code MyViewGroup} should handle touch events by scrolling its contents. However, 
+if you press buttons in the child view, or scroll the child view vertically, 
+the parent shouldn't intercept those touch events, because the child is the 
+intended target. In those cases, 
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()} should
+return {@code false}, and {@code MyViewGroup}'s 
+{@link android.view.View#onTouchEvent onTouchEvent()} won't be called.</p>
+
+<pre>public class MyViewGroup extends ViewGroup {
+
+    private int mTouchSlop;
+
+    ...
+
+    ViewConfiguration vc = ViewConfiguration.get(view.getContext());
+    mTouchSlop = vc.getScaledTouchSlop();
+
+    ...
+
+    &#64;Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        /*
+         * This method JUST determines whether we want to intercept the motion.
+         * If we return true, onTouchEvent will be called and we do the actual
+         * scrolling there.
+         */
+
+
+        final int action = MotionEventCompat.getActionMasked(ev);
+
+        // Always handle the case of the touch gesture being complete.
+        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+            // Release the scroll.
+            mIsScrolling = false;
+            return false; // Do not intercept touch event, let the child handle it
+        }
+
+        switch (action) {
+            case MotionEvent.ACTION_MOVE: {
+                if (mIsScrolling) {
+                    // We're currently scrolling, so yes, intercept the 
+                    // touch event!
+                    return true;
+                }
+
+                // If the user has dragged her finger horizontally more than 
+                // the touch slop, start the scroll
+
+                // left as an exercise for the reader
+                final int xDiff = calculateDistanceX(ev); 
+
+                // Touch slop should be calculated using ViewConfiguration 
+                // constants.
+                if (xDiff > mTouchSlop) { 
+                    // Start scrolling!
+                    mIsScrolling = true;
+                    return true;
+                }
+                break;
+            }
+            ...
+        }
+
+        // In general, we don't want to intercept touch events. They should be 
+        // handled by the child view.
+        return false;
+    }
+
+    &#64;Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        // Here we actually handle the touch event (e.g. if the action is ACTION_MOVE, 
+        // scroll this container).
+        // This method will only be called if the touch event was intercepted in 
+        // onInterceptTouchEvent
+        ...
+    }
+}</pre>
+
+<p>Note that {@link android.view.ViewGroup} also provides a 
+{@link android.view.ViewGroup#requestDisallowInterceptTouchEvent requestDisallowInterceptTouchEvent()} method. 
+The {@link android.view.ViewGroup} calls this method when a child does not want the parent and its 
+ancestors to intercept touch events with 
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()}. 
+</p>
+
+<h2 id="vc">Use ViewConfiguration Constants</h2>
+
+<p>The above snippet uses the current {@link android.view.ViewConfiguration} to initialize
+a variable called {@code mTouchSlop}. You can use the {@link
+android.view.ViewConfiguration} class to access common distances, speeds, and
+times used by the Android system.</p>
+
+
+<p>"Touch slop" refers to the distance in pixels a user's touch can wander
+before the gesture is interpreted as scrolling. Touch slop is typically used to
+prevent accidental scrolling when the user is performing some other touch
+operation, such as touching on-screen elements.</p>
+
+<p>Two other commonly used {@link android.view.ViewConfiguration} methods are 
+{@link android.view.ViewConfiguration#getScaledMinimumFlingVelocity getScaledMinimumFlingVelocity()} 
+and {@link android.view.ViewConfiguration#getScaledMaximumFlingVelocity getScaledMaximumFlingVelocity()}.
+These methods  return the minimum and maximum velocity (respectively) to initiate a fling, 
+as measured in pixels per second. For example:</p>
+
+<pre>ViewConfiguration vc = ViewConfiguration.get(view.getContext());
+private int mSlop = vc.getScaledTouchSlop();
+private int mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
+private int mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
+
+...
+
+case MotionEvent.ACTION_MOVE: {
+    ...
+    float deltaX = motionEvent.getRawX() - mDownX;
+    if (Math.abs(deltaX) > mSlop) {
+        // A swipe occurred, do something
+    }
+
+...
+
+case MotionEvent.ACTION_UP: {
+    ...
+    } if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity
+            && velocityY < velocityX) {
+        // The criteria have been satisfied, do something
+    }
+}</pre>
+
+
+<h2 id="delegate">Extend a Child View's Touchable Area</h2>
+
+<p>Android provides the {@link android.view.TouchDelegate} class to make it possible 
+for a parent to extend the touchable area of a child view beyond the child's bounds. 
+
+This is useful when the child has to be small, but should have a larger touch region. You can
+also use this approach to shrink the child's touch region if need be.</p>
+
+<p>In the following example, an {@link android.widget.ImageButton} is the  
+"delegate view" (that is, the child whose touch area the parent will extend). 
+Here is the layout file:</p>
+
+<pre>
+&lt;RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
+     android:id=&quot;@+id/parent_layout&quot;
+     android:layout_width=&quot;match_parent&quot;
+     android:layout_height=&quot;match_parent&quot;
+     tools:context=&quot;.MainActivity&quot; &gt;
+ 
+     &lt;ImageButton android:id=&quot;@+id/button&quot;
+          android:layout_width=&quot;wrap_content&quot;
+          android:layout_height=&quot;wrap_content&quot;
+          android:background=&quot;@null&quot;
+          android:src=&quot;@drawable/icon&quot; /&gt;
+&lt;/RelativeLayout&gt;
+</pre>
+
+<p>The snippet below does the following:</p>
+
+<ul>
+<li>Gets the parent view and posts a {@link java.lang.Runnable} on the UI thread. This ensures that the parent lays out its children before calling the {@link android.view.View#getHitRect getHitRect()} method. The {@link android.view.View#getHitRect getHitRect()} method gets the child's hit rectangle (touchable area) in the parent's coordinates.</li>
+<li>Finds the {@link android.widget.ImageButton} child view and calls {@link android.view.View#getHitRect getHitRect()} to get the bounds of the child's touchable area.</li>
+<li>Extends the bounds of the {@link android.widget.ImageButton}'s hit rectangle.</li>
+<li>Instantiates a {@link android.view.TouchDelegate}, passing in the expanded hit rectangle and the {@link android.widget.ImageButton} child view as parameters.</li>
+<li>Sets the {@link android.view.TouchDelegate} on the parent view, such that touches within the touch delegate bounds are routed to the child.</li>
+
+</ul>
+
+In its capacity as touch delegate for the {@link android.widget.ImageButton} child view, the 
+parent view will receive all touch events. If the touch event occurred within the child's hit
+rectangle, the parent will pass the touch 
+event to the child for handling.</p>
+
+
+
+<pre>
+public class MainActivity extends Activity {
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        // Get the parent view
+        View parentView = findViewById(R.id.parent_layout);
+        
+        parentView.post(new Runnable() {
+            // Post in the parent's message queue to make sure the parent
+            // lays out its children before you call getHitRect()
+            &#64;Override
+            public void run() {
+                // The bounds for the delegate view (an ImageButton
+                // in this example)
+                Rect delegateArea = new Rect();
+                ImageButton myButton = (ImageButton) findViewById(R.id.button);
+                myButton.setEnabled(true);
+                myButton.setOnClickListener(new View.OnClickListener() {
+                    &#64;Override
+                    public void onClick(View view) {
+                        Toast.makeText(MainActivity.this, 
+                                "Touch occurred within ImageButton touch region.", 
+                                Toast.LENGTH_SHORT).show();
+                    }
+                });
+     
+                // The hit rectangle for the ImageButton
+                myButton.getHitRect(delegateArea);
+            
+                // Extend the touch area of the ImageButton beyond its bounds
+                // on the right and bottom.
+                delegateArea.right += 100;
+                delegateArea.bottom += 100;
+            
+                // Instantiate a TouchDelegate.
+                // "delegateArea" is the bounds in local coordinates of 
+                // the containing view to be mapped to the delegate view.
+                // "myButton" is the child view that should receive motion
+                // events.
+                TouchDelegate touchDelegate = new TouchDelegate(delegateArea, 
+                        myButton);
+     
+                // Sets the TouchDelegate on the parent view, such that touches 
+                // within the touch delegate bounds are routed to the child.
+                if (View.class.isInstance(myButton.getParent())) {
+                    ((View) myButton.getParent()).setTouchDelegate(touchDelegate);
+                }
+            }
+        });
+    }
+}</pre>
+
diff --git a/docs/html/training/keyboard-input/commands.jd b/docs/html/training/keyboard-input/commands.jd
new file mode 100644
index 0000000..9d2de41
--- /dev/null
+++ b/docs/html/training/keyboard-input/commands.jd
@@ -0,0 +1,113 @@
+page.title=Handling Keyboard Actions
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#SingleKey">Handle Single Key Events</a></li>
+  <li><a href="#ModifierKey">Handle Modifier Keys</a></li>
+</ol>
+
+</div>
+</div>
+
+
+<p>When the user gives focus to an editable text view such as an {@link android.widget.EditText}
+element and the user has a hardware keyboard attached, all
+input is handled by the system. If, however, you'd like to intercept
+or directly handle the keyboard input yourself, you can do so by implementing callback methods
+from the {@link android.view.KeyEvent.Callback} interface, such as {@link
+android.view.KeyEvent.Callback#onKeyDown onKeyDown()} and {@link
+android.view.KeyEvent.Callback#onKeyMultiple onKeyMultiple()}.</p>
+
+<p>Both the {@link
+android.app.Activity} and {@link android.view.View} class implement the
+{@link android.view.KeyEvent.Callback} interface, so you
+should generally override the callback methods in your extension of these classes as
+appropriate.</p>
+
+<p class="note"><strong>Note:</strong> When handling keyboard events with the {@link
+android.view.KeyEvent} class and related APIs, you should expect that such keyboard
+events come only from a hardware keyboard. You should never rely on receiving key events
+for any key on a soft input method (an on-screen keyboard).</p>
+
+
+<h2 id="SingleKey">Handle Single Key Events</h2>
+
+<p>To handle an individual key press, implement {@link
+android.app.Activity#onKeyDown onKeyDown()} or {@link
+android.app.Activity#onKeyUp onKeyUp()} as appropriate. Usually, you should
+use {@link android.app.Activity#onKeyUp onKeyUp()} if you want to be sure that you receive
+only one event. If the user presses and holds the button, then {@link
+android.app.Activity#onKeyDown onKeyDown()} is called multiple times.</p>
+
+<p>For example, this implementation responds to some keyboard keys to control a game:</p>
+
+<pre>
+&#64;Override
+public boolean onKeyUp(int keyCode, KeyEvent event) {
+    switch (keyCode) {
+        case KeyEvent.KEYCODE_D:
+            moveShip(MOVE_LEFT);
+            return true;
+        case KeyEvent.KEYCODE_F:
+            moveShip(MOVE_RIGHT);
+            return true;
+        case KeyEvent.KEYCODE_J:
+            fireMachineGun();
+            return true;
+        case KeyEvent.KEYCODE_K:
+            fireMissile();
+            return true;
+        default:
+            return super.onKeyUp(keyCode, event);
+    }
+}
+</pre>
+
+
+<h2 id="ModifierKey">Handle Modifier Keys</h2>
+
+<p>To respond to modifier key events such as when a key is combined with Shift or Control, you can
+query the {@link android.view.KeyEvent} that's passed to the callback method. Several methods
+provide information about modifier keys such as {@link android.view.KeyEvent#getModifiers()}
+and {@link android.view.KeyEvent#getMetaState()}. However, the simplest solution is to check whether
+the exact modifier key you care about is being pressed with methods such as
+{@link android.view.KeyEvent#isShiftPressed()} and {@link android.view.KeyEvent#isCtrlPressed()}.
+</p>
+
+<p>For example, here's the {@link android.app.Activity#onKeyDown onKeyDown()} implementation
+again, with some extra handling for when the Shift key is held down with one of the keys:</p>
+
+<pre>
+&#64;Override
+public boolean onKeyUp(int keyCode, KeyEvent event) {
+    switch (keyCode) {
+        ...
+        case KeyEvent.KEYCODE_J:
+            if (event.isShiftPressed()) {
+                fireLaser();
+            } else {
+                fireMachineGun();
+            }
+            return true;
+        case KeyEvent.KEYCODE_K:
+            if (event.isShiftPressed()) {
+                fireSeekingMissle();
+            } else {
+                fireMissile();
+            }
+            return true;
+        default:
+            return super.onKeyUp(keyCode, event);
+    }
+}
+</pre>
+
+
+
diff --git a/docs/html/training/keyboard-input/index.jd b/docs/html/training/keyboard-input/index.jd
new file mode 100644
index 0000000..ba4e598
--- /dev/null
+++ b/docs/html/training/keyboard-input/index.jd
@@ -0,0 +1,54 @@
+page.title=Handling Keyboard Input
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+<ul>
+  <li>Android 1.6 (API Level 3) or higher</li>
+</ul>
+
+</div>
+</div>
+
+<p>The Android system shows an on-screen keyboard&mdash;known as a
+<em>soft input method</em>&mdash;when a text field in your UI receives focus.
+To provide the best user experience, you can specify characteristics
+about the type of input you expect (such as
+whether it's a phone number or email address) and how the input method should behave (such as
+whether it performs auto-correct for spelling mistakes).</p>
+
+<p>In addition to the on-screen input methods, Android also supports hardware keyboards, so it's
+important that your app optimize its user experience for interaction that might occur
+through an attached keyboard.</p>
+
+<p>These topics and more are discussed in the following lessons.</p>
+
+
+<h2>Lessons</h2> 
+ 
+<dl> 
+  <dt><b><a href="style.html">Specifying the Input Method Type</a></b></dt>
+    <dd>Learn how to show certain soft input methods, such as those designed for phone numbers, web
+    addresses, or other formats. Also learn how to specify characteristics such
+    as spelling suggestion behavior and action buttons such as <b>Done</b> or <b>Next</b>.
+    </dd>
+  <dt><b><a href="visibility.html">Handling Input Method Visibility</a></b></dt>
+    <dd>Learn how to specify when to show the soft input method and how
+    your layout should adjust to the reduced screen space.
+    </dd>
+  <dt><b><a href="navigation.html">Supporting Keyboard Navigation</a></b></dt>
+    <dd>Learn how to verify that users can navigate your app using a keyboard
+    and how to make any necessary changes to the navigation order.
+    </dd>
+  <dt><b><a href="commands.html">Handling Keyboard Actions</a></b></dt>
+    <dd>Learn how to respond directly to keyboard input for user actions.
+    </dd>
+ 
+</dl> 
\ No newline at end of file
diff --git a/docs/html/training/keyboard-input/navigation.jd b/docs/html/training/keyboard-input/navigation.jd
new file mode 100644
index 0000000..6e26ab2
--- /dev/null
+++ b/docs/html/training/keyboard-input/navigation.jd
Binary files differ
diff --git a/docs/html/training/keyboard-input/style.jd b/docs/html/training/keyboard-input/style.jd
new file mode 100644
index 0000000..b0e506c
--- /dev/null
+++ b/docs/html/training/keyboard-input/style.jd
@@ -0,0 +1,171 @@
+page.title=Specifying the Input Method Type
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#Type">Specify the Keyboard Type</a></li>
+  <li><a href="#Spelling">Enable Spelling Suggestions and Other Behaviors</a></li>
+  <li><a href="#Action">Specify the Input Method Action</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}guide/topics/ui/controls/text.html">Text Fields</a></li>
+</ul>
+
+</div>
+</div>
+
+
+<p>Every text field expects a certain type of text input, such as an
+email address, phone number, or just plain text. So it's important
+that you specify the input type for each text field in your app
+so the system displays the appropriate soft input method (such as an on-screen keyboard).</p>
+
+<p>Beyond the type of buttons available with an input method, you should specify
+behaviors such as whether the input method provides spelling suggestions,
+capitalizes new sentences, and replaces the carriage return button with an
+action button such as a <b>Done</b> or <b>Next</b>.
+This lesson shows how to specify these characteristics.</p>
+
+
+
+<h2 id="Type">Specify the Keyboard Type</h2>
+
+<p>You should always declare the input method for your text fields by adding
+the <a href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType"
+>{@code android:inputType}</a> attribute to the {@link android.widget.EditText
+&lt;EditText&gt;} element.</p>
+
+<div class="figure" style="width:300px">
+<img src="{@docRoot}images/ui/edittext-phone.png" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> The {@code phone} input type.</p>
+</div>
+
+<p>For example, if you'd like an input method for entering a phone number,
+use the {@code "phone"} value:</p>
+<pre>
+&lt;EditText
+    android:id="@+id/phone"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:hint="@string/phone_hint"
+    android:inputType="phone" />
+</pre>
+
+<div class="figure" style="width:300px">
+<img src="{@docRoot}images/training/input/ime_password.png" alt="" />
+<p class="img-caption"><strong>Figure 2.</strong> The {@code textPassword} input type.</p>
+</div>
+
+<p>Or if the text field is for a password, use the {@code "textPassword"} value
+so the text field conceals the user's input:</p>
+<pre>
+&lt;EditText
+    android:id="@+id/password"
+    android:hint="@string/password_hint"
+    android:inputType="textPassword"
+    ... />    
+</pre>
+
+<p>There are several possible values documented with the
+<a href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType"
+>{@code android:inputType}</a> attribute and
+some of the values can be combined to specify the input method
+appearance and additional behaviors.</p>
+
+
+
+<h2 id="Spelling">Enable Spelling Suggestions and Other Behaviors</h2>
+
+<div class="figure" style="width:300px">
+<img src="{@docRoot}images/training/input/ime_autocorrect.png" alt="" />
+<p class="img-caption"><strong>Figure 3.</strong> Adding {@code textAutoCorrect}
+provides auto-correction for misspellings.</p>
+</div>
+
+<p>The <a href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType"
+>{@code android:inputType}</a> attribute allows you to specify various behaviors for the
+input method. Most importantly, if your text field is intended for basic text input (such
+as for a text message), you should enable auto spelling correction with the
+{@code "textAutoCorrect"} value.</p>
+
+<p>You can combine different behaviors and input method styles with the
+<a href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType"
+>{@code android:inputType}</a> attribute. For example,
+here's how to create a text field that capitalizes the first word of a sentence
+and also auto-corrects misspellings:</p>
+
+<pre>
+&lt;EditText
+    android:id="@+id/message"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:inputType=
+        "textCapSentences|textAutoCorrect"
+    ... />
+</pre>
+
+
+
+
+<h2 id="Action">Specify the Input Method Action</h2>
+
+<p>Most soft input methods provide a user action button in the
+bottom corner that's appropriate for the current text field.
+By default, the system uses this button for either a <b>Next</b> or
+<b>Done</b> action unless your text field allows multi-line text (such as with {@code
+android:inputType="textMultiLine"}), in which case the action button is a carriage return.
+However, you can specify additional actions that might be more appropriate for your
+text field, such as <b>Send</b> or <b>Go</b>.</p>
+
+<p>To specify the keyboard action button, use the <a
+href="{@docRoot}reference/android/widget/TextView.html#attr_android:imeOptions">{@code
+android:imeOptions}</a> attribute with an action value such as {@code "actionSend"} or
+{@code "actionSearch"}. For example:</p>
+
+<div class="figure" style="width:300px">
+<img src="{@docRoot}images/ui/edittext-actionsend.png" alt="" />
+<p class="img-caption"><strong>Figure 4.</strong> The Send button appears when you declare
+{@code android:imeOptions="actionSend"}.</p>
+</div>
+
+<pre>
+&lt;EditText
+    android:id="@+id/search"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:hint="@string/search_hint"
+    android:inputType="text"
+    android:imeOptions="actionSend" />
+</pre>
+
+<p>You can then listen for presses on the action button by defining a
+{@link android.widget.TextView.OnEditorActionListener} for the {@link android.widget.EditText}
+element. In your listener, respond to the appropriate IME action ID defined in the
+{@link android.view.inputmethod.EditorInfo} class, such as
+{@link android.view.inputmethod.EditorInfo#IME_ACTION_SEND}. For example:</p>
+
+<pre>
+EditText editText = (EditText) findViewById(R.id.search);
+editText.setOnEditorActionListener(new OnEditorActionListener() {
+    &#64;Override
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        boolean handled = false;
+        if (actionId == EditorInfo.IME_ACTION_SEND) {
+            sendMessage();
+            handled = true;
+        }
+        return handled;
+    }
+});
+</pre>
+
+
+
diff --git a/docs/html/training/keyboard-input/visibility.jd b/docs/html/training/keyboard-input/visibility.jd
new file mode 100644
index 0000000..5dc6fc260
--- /dev/null
+++ b/docs/html/training/keyboard-input/visibility.jd
Binary files differ
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 78b0dce..9518046 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -194,6 +194,8 @@
     </ul>
   </li><!-- end getting started -->
   
+  
+  
 
   <li class="nav-section">
     <div class="nav-section-header">
@@ -799,7 +801,81 @@
   </li>
   <!-- End best UX and UI -->
   
+
+  <li class="nav-section">
+    <div class="nav-section-header">
+      <a href="<?cs var:toroot ?>training/best-user-input.html">
+      <span class="small">Best Practices for</span><br/>
+              User Input
+      </a>
+    </div>
+    <ul>
+         
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>training/gestures/index.html"
+             description=
+             "How to write apps that allow users to interact with the touch screen via touch gestures."
+            >Using Touch Gestures</a>
+        </div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/gestures/detector.html">
+            Detecting Common Gestures
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/gestures/movement.html">
+            Tracking Movement
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/gestures/scroll.html">
+            Animating a Scroll Gesture
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/gestures/multi.html">
+            Handling Multi-Touch Gestures
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/gestures/scale.html">
+            Dragging and Scaling
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/gestures/viewgroup.html">
+            Managing Touch Events in a ViewGroup
+          </a>
+          </li>
+        </ul>
+      </li>
       
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>training/keyboard-input/index.html"
+             description=
+             "How to specify the appearance and behaviors of soft input methods (such
+             as on-screen keyboards) and how to optimize the experience with
+             hardware keyboards."
+            >Handling Keyboard Input</a>
+        </div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/keyboard-input/style.html">
+            Specifying the Input Method Type
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/keyboard-input/visibility.html">
+            Handling Input Method Visibility
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/keyboard-input/navigation.html">
+            Supporting Keyboard Navigation
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/keyboard-input/commands.html">
+            Handling Keyboard Actions
+          </a>
+          </li>
+        </ul>
+      </li>
+    </ul>
+  </li> <!-- end of User Input -->    
 
   <li class="nav-section">
     <div class="nav-section-header">
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index a707df2..b36aee2 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -244,6 +244,14 @@
             return this;
         }
 
+        public Builder setZ(int value) {
+            if(value < 1) {
+                throw new RSIllegalArgumentException("Values of less than 1 for Dimension Z are not valid.");
+            }
+            mDimZ = value;
+            return this;
+        }
+
         public Builder setMipmaps(boolean value) {
             mDimMipmaps = value;
             return this;
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 48bea52..e0430b4 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -177,12 +177,6 @@
          *  is applied.
          */
         public static final int VOICE_COMMUNICATION = 7;
-
-        /**
-         * @hide
-         * Audio source for remote submix.
-         */
-        public static final int REMOTE_SUBMIX_SOURCE = 8;
     }
 
     /**
@@ -298,10 +292,7 @@
      * @see android.media.MediaRecorder.AudioSource
      */
     public static final int getAudioSourceMax() {
-        // FIXME disable selection of the remote submxi source selection once test code
-        //       doesn't rely on it
-        return AudioSource.REMOTE_SUBMIX_SOURCE;
-        //return AudioSource.VOICE_COMMUNICATION;
+        return AudioSource.VOICE_COMMUNICATION;
     }
 
     /**
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 423a109..fbbcb0a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -442,6 +442,7 @@
             mMimeType = mimeType;
             mFileType = 0;
             mFileSize = fileSize;
+            mIsDrm = false;
 
             if (!isDirectory) {
                 if (!noMedia && isNoMediaFile(path)) {
@@ -504,7 +505,6 @@
             mLastModified = lastModified;
             mWriter = null;
             mCompilation = 0;
-            mIsDrm = false;
             mWidth = 0;
             mHeight = 0;
 
@@ -1041,6 +1041,7 @@
             }
 
             if (mDrmManagerClient.canHandle(path, null)) {
+                mIsDrm = true;
                 String drmMimetype = mDrmManagerClient.getOriginalMimeType(path);
                 if (drmMimetype != null) {
                     mMimeType = drmMimetype;
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 3b325b7..bcab4f3 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -705,7 +705,7 @@
 {
     effect_descriptor_t desc;
     char str[EFFECT_STRING_LEN_MAX];
-    uint32_t numEffects;
+    uint32_t numEffects = 0;
     uint32_t i = 0;
     jstring jdescType;
     jstring jdescUuid;
@@ -714,7 +714,10 @@
     jstring jdescImplementor;
     jobject jdesc;
 
-    AudioEffect::queryNumberEffects(&numEffects);
+    if (AudioEffect::queryNumberEffects(&numEffects) != NO_ERROR) {
+        return NULL;
+    }
+
     jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
     if (ret == NULL) {
         return ret;
diff --git a/opengl/java/android/opengl/GLUtils.java b/opengl/java/android/opengl/GLUtils.java
index 1527f22..a9d33dd 100644
--- a/opengl/java/android/opengl/GLUtils.java
+++ b/opengl/java/android/opengl/GLUtils.java
@@ -270,12 +270,10 @@
     }
 
     /**
-     * Enable tracing of OpenGL functions for this application.
+     * Set OpenGL Tracing level for this application.
      * @hide
      */
-    public static void enableTracing() {
-        native_enableTracing();
-    }
+    native public static void setTracingLevel(int level);
 
     native private static void nativeClassInit();
 
@@ -285,5 +283,4 @@
             Bitmap bitmap, int type, int border);
     native private static int native_texSubImage2D(int target, int level, int xoffset, int yoffset,
             Bitmap bitmap, int format, int type);
-    native private static void native_enableTracing();
 }
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
index 1b9742c..faa72a2 100644
--- a/services/java/com/android/server/NsdService.java
+++ b/services/java/com/android/server/NsdService.java
@@ -389,8 +389,10 @@
                         break;
                     case NsdManager.NATIVE_DAEMON_EVENT:
                         NativeEvent event = (NativeEvent) msg.obj;
-                        handleNativeEvent(event.code, event.raw,
-                                NativeDaemonEvent.unescapeArgs(event.raw));
+                        if (!handleNativeEvent(event.code, event.raw,
+                                NativeDaemonEvent.unescapeArgs(event.raw))) {
+                            result = NOT_HANDLED;
+                        }
                         break;
                     default:
                         result = NOT_HANDLED;
@@ -398,6 +400,127 @@
                 }
                 return result;
             }
+
+            private boolean handleNativeEvent(int code, String raw, String[] cooked) {
+                boolean handled = true;
+                NsdServiceInfo servInfo;
+                int id = Integer.parseInt(cooked[1]);
+                ClientInfo clientInfo = mIdToClientInfoMap.get(id);
+                if (clientInfo == null) {
+                    Slog.e(TAG, "Unique id with no client mapping: " + id);
+                    handled = false;
+                    return handled;
+                }
+
+                /* This goes in response as msg.arg2 */
+                int clientId = -1;
+                int keyId = clientInfo.mClientIds.indexOfValue(id);
+                if (keyId != -1) {
+                    clientId = clientInfo.mClientIds.keyAt(keyId);
+                }
+                switch (code) {
+                    case NativeResponseCode.SERVICE_FOUND:
+                        /* NNN uniqueId serviceName regType domain */
+                        if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
+                        servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
+                        clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
+                                clientId, servInfo);
+                        break;
+                    case NativeResponseCode.SERVICE_LOST:
+                        /* NNN uniqueId serviceName regType domain */
+                        if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
+                        servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
+                        clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
+                                clientId, servInfo);
+                        break;
+                    case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
+                        /* NNN uniqueId errorCode */
+                        if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
+                        clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
+                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+                        break;
+                    case NativeResponseCode.SERVICE_REGISTERED:
+                        /* NNN regId serviceName regType */
+                        if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
+                        servInfo = new NsdServiceInfo(cooked[2], null, null);
+                        clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
+                                id, clientId, servInfo);
+                        break;
+                    case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
+                        /* NNN regId errorCode */
+                        if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
+                        clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
+                               NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+                        break;
+                    case NativeResponseCode.SERVICE_UPDATED:
+                        /* NNN regId */
+                        break;
+                    case NativeResponseCode.SERVICE_UPDATE_FAILED:
+                        /* NNN regId errorCode */
+                        break;
+                    case NativeResponseCode.SERVICE_RESOLVED:
+                        /* NNN resolveId fullName hostName port txtlen txtdata */
+                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
+                        int index = cooked[2].indexOf(".");
+                        if (index == -1) {
+                            Slog.e(TAG, "Invalid service found " + raw);
+                            break;
+                        }
+                        String name = cooked[2].substring(0, index);
+                        String rest = cooked[2].substring(index);
+                        String type = rest.replace(".local.", "");
+
+                        clientInfo.mResolvedService.setServiceName(name);
+                        clientInfo.mResolvedService.setServiceType(type);
+                        clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
+
+                        stopResolveService(id);
+                        if (!getAddrInfo(id, cooked[3])) {
+                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
+                                    NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+                            removeRequestMap(clientId, id, clientInfo);
+                            clientInfo.mResolvedService = null;
+                        }
+                        break;
+                    case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
+                        /* NNN resolveId errorCode */
+                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
+                        stopResolveService(id);
+                        removeRequestMap(clientId, id, clientInfo);
+                        clientInfo.mResolvedService = null;
+                        clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
+                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+                        break;
+                    case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
+                        /* NNN resolveId errorCode */
+                        stopGetAddrInfo(id);
+                        removeRequestMap(clientId, id, clientInfo);
+                        clientInfo.mResolvedService = null;
+                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
+                        clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
+                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+                        break;
+                    case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
+                        /* NNN resolveId hostname ttl addr */
+                        if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
+                        try {
+                            clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
+                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
+                                   0, clientId, clientInfo.mResolvedService);
+                        } catch (java.net.UnknownHostException e) {
+                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
+                                    NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+                        }
+                        stopGetAddrInfo(id);
+                        removeRequestMap(clientId, id, clientInfo);
+                        clientInfo.mResolvedService = null;
+                        break;
+                    default:
+                        handled = false;
+                        break;
+                }
+                return handled;
+            }
        }
     }
 
@@ -483,8 +606,8 @@
     }
 
     private class NativeEvent {
-        int code;
-        String raw;
+        final int code;
+        final String raw;
 
         NativeEvent(int code, String raw) {
             this.code = code;
@@ -506,123 +629,6 @@
         }
     }
 
-    private void handleNativeEvent(int code, String raw, String[] cooked) {
-        NsdServiceInfo servInfo;
-        int id = Integer.parseInt(cooked[1]);
-        ClientInfo clientInfo = mIdToClientInfoMap.get(id);
-        if (clientInfo == null) {
-            Slog.e(TAG, "Unique id with no client mapping: " + id);
-            return;
-        }
-
-        /* This goes in response as msg.arg2 */
-        int clientId = -1;
-        int keyId = clientInfo.mClientIds.indexOfValue(id);
-        if (keyId != -1) {
-            clientId = clientInfo.mClientIds.keyAt(keyId);
-        }
-        switch (code) {
-            case NativeResponseCode.SERVICE_FOUND:
-                /* NNN uniqueId serviceName regType domain */
-                if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
-                servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
-                clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
-                        clientId, servInfo);
-                break;
-            case NativeResponseCode.SERVICE_LOST:
-                /* NNN uniqueId serviceName regType domain */
-                if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
-                servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
-                clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
-                        clientId, servInfo);
-                break;
-            case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
-                /* NNN uniqueId errorCode */
-                if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
-                clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
-                        NsdManager.FAILURE_INTERNAL_ERROR, clientId);
-                break;
-            case NativeResponseCode.SERVICE_REGISTERED:
-                /* NNN regId serviceName regType */
-                if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
-                servInfo = new NsdServiceInfo(cooked[2], null, null);
-                clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
-                        id, clientId, servInfo);
-                break;
-            case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
-                /* NNN regId errorCode */
-                if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
-                clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
-                        NsdManager.FAILURE_INTERNAL_ERROR, clientId);
-                break;
-            case NativeResponseCode.SERVICE_UPDATED:
-                /* NNN regId */
-                break;
-            case NativeResponseCode.SERVICE_UPDATE_FAILED:
-                /* NNN regId errorCode */
-                break;
-            case NativeResponseCode.SERVICE_RESOLVED:
-                /* NNN resolveId fullName hostName port txtlen txtdata */
-                if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
-                int index = cooked[2].indexOf(".");
-                if (index == -1) {
-                    Slog.e(TAG, "Invalid service found " + raw);
-                    break;
-                }
-                String name = cooked[2].substring(0, index);
-                String rest = cooked[2].substring(index);
-                String type = rest.replace(".local.", "");
-
-                clientInfo.mResolvedService.setServiceName(name);
-                clientInfo.mResolvedService.setServiceType(type);
-                clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
-
-                stopResolveService(id);
-                if (!getAddrInfo(id, cooked[3])) {
-                    clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
-                            NsdManager.FAILURE_INTERNAL_ERROR, clientId);
-                    mIdToClientInfoMap.remove(id);
-                    clientInfo.mResolvedService = null;
-                }
-                break;
-            case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
-                /* NNN resolveId errorCode */
-                if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
-                stopResolveService(id);
-                mIdToClientInfoMap.remove(id);
-                clientInfo.mResolvedService = null;
-                clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
-                        NsdManager.FAILURE_INTERNAL_ERROR, clientId);
-                break;
-            case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
-                /* NNN resolveId errorCode */
-                stopGetAddrInfo(id);
-                mIdToClientInfoMap.remove(id);
-                clientInfo.mResolvedService = null;
-                if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
-                clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
-                        NsdManager.FAILURE_INTERNAL_ERROR, clientId);
-                break;
-            case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
-                /* NNN resolveId hostname ttl addr */
-                if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
-                try {
-                    clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
-                    clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
-                            0, clientId, clientInfo.mResolvedService);
-                } catch (java.net.UnknownHostException e) {
-                    clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
-                            NsdManager.FAILURE_INTERNAL_ERROR, clientId);
-                }
-                stopGetAddrInfo(id);
-                mIdToClientInfoMap.remove(id);
-                clientInfo.mResolvedService = null;
-                break;
-            default:
-                break;
-        }
-    }
-
     private boolean startMDnsDaemon() {
         if (DBG) Slog.d(TAG, "startMDnsDaemon");
         try {
@@ -801,8 +807,8 @@
     private class ClientInfo {
 
         private static final int MAX_LIMIT = 10;
-        private AsyncChannel mChannel;
-        private Messenger mMessenger;
+        private final AsyncChannel mChannel;
+        private final Messenger mMessenger;
         /* Remembers a resolved service until getaddrinfo completes */
         private NsdServiceInfo mResolvedService;
 
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index 34513a1..ad68307 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -58,9 +58,9 @@
     }
 
     public void setAnimation(Animation anim, int width, int height) {
-        if (WindowManagerService.localLOGV) Slog.v(
-            TAG, "Setting animation in " + mAppToken + ": " + anim
-                    + " wxh=" + width + "x" + height);
+        if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
+                + ": " + anim + " wxh=" + width + "x" + height
+                + " isVisible=" + mAppToken.isVisible());
         animation = anim;
         animating = false;
         if (!anim.isInitialized()) {
@@ -82,16 +82,17 @@
         }
         // Start out animation gone if window is gone, or visible if window is visible.
         transformation.clear();
-        transformation.setAlpha(mAppToken.reportedVisible ? 1 : 0);
+        transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
         hasTransformation = true;
     }
 
     public void setDummyAnimation() {
-        if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken);
+        if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken
+                + " isVisible=" + mAppToken.isVisible());
         animation = sDummyAnimation;
         hasTransformation = true;
         transformation.clear();
-        transformation.setAlpha(mAppToken.reportedVisible ? 1 : 0);
+        transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
     }
 
     public void clearAnimation() {
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 275c9bf..adad09d 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -223,6 +223,22 @@
         return null;
     }
 
+    boolean isVisible() {
+        final int N = allAppWindows.size();
+        // TODO: Consider using allDrawn instead of a single window.
+        for (int i=0; i<N; i++) {
+            WindowState win = allAppWindows.get(i);
+            if (!win.mAppFreezing
+                    && (win.mViewVisibility == View.VISIBLE ||
+                        (win.mWinAnimator.isAnimating() &&
+                                !service.mAppTransition.isTransitionSet()))
+                    && !win.mDestroying && win.isDrawnLw()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 3e50614..c67a465 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4476,8 +4476,7 @@
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_REORDER) Slog.v(TAG,
                     "Start moving token " + wtoken + " initially at "
                     + oldIndex);
-            if (oldIndex > index && mAppTransition.isTransitionSet()
-                    && !mAppTransition.isRunning()) {
+            if (oldIndex > index && mAppTransition.isTransitionSet()) {
                 // animation towards back has not started, copy old list for duration of animation.
                 mAnimatingAppTokens.clear();
                 mAnimatingAppTokens.addAll(mAppTokens);
@@ -4491,7 +4490,7 @@
             if (DEBUG_REORDER) Slog.v(TAG, "Moved " + token + " to " + index + ":");
             else if (DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "Moved " + token + " to " + index);
             if (DEBUG_REORDER) dumpAppTokensLocked();
-            if (!mAppTransition.isTransitionSet() && !mAppTransition.isRunning()) {
+            if (!mAppTransition.isTransitionSet()) {
                 // Not animating, bring animating app list in line with mAppTokens.
                 mAnimatingAppTokens.clear();
                 mAnimatingAppTokens.addAll(mAppTokens);
@@ -4612,11 +4611,9 @@
                 }
             }
 
-            if (!mAppTransition.isRunning()) {
-                mAnimatingAppTokens.clear();
-                mAnimatingAppTokens.addAll(mAppTokens);
-                moveAppWindowsLocked(tokens, mAppTokens.size());
-            }
+            mAnimatingAppTokens.clear();
+            mAnimatingAppTokens.addAll(mAppTokens);
+            moveAppWindowsLocked(tokens, mAppTokens.size());
         }
         Binder.restoreCallingIdentity(origId);
     }
@@ -4631,7 +4628,7 @@
         final long origId = Binder.clearCallingIdentity();
         synchronized(mWindowMap) {
             final int N = tokens.size();
-            if (N > 0 && !mAppTransition.isRunning()) {
+            if (N > 0) {
                 // animating towards back, hang onto old list for duration of animation.
                 mAnimatingAppTokens.clear();
                 mAnimatingAppTokens.addAll(mAppTokens);
@@ -4651,11 +4648,9 @@
                 }
             }
 
-            if (!mAppTransition.isRunning()) {
-                mAnimatingAppTokens.clear();
-                mAnimatingAppTokens.addAll(mAppTokens);
-                moveAppWindowsLocked(tokens, 0);
-            }
+            mAnimatingAppTokens.clear();
+            mAnimatingAppTokens.addAll(mAppTokens);
+            moveAppWindowsLocked(tokens, 0);
         }
         Binder.restoreCallingIdentity(origId);
     }
@@ -7949,7 +7944,6 @@
                 final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
                 appAnimator.clearThumbnail();
-                wtoken.reportedVisible = false;
                 wtoken.inPendingTransaction = false;
                 appAnimator.animation = null;
                 setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 9acdd49..5b7cb99 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -1223,7 +1223,7 @@
         if (mIsWallpaper && !mWin.mWallpaperVisible) {
             // Wallpaper is no longer visible and there is no wp target => hide it.
             hide();
-        } else if (w.mAttachedHidden || !w.isReadyForDisplay()) {
+        } else if (w.mAttachedHidden || !w.isOnScreen()) {
             hide();
             mAnimator.hideWallpapersLocked(w);
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorCube.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorCube.java
new file mode 100644
index 0000000..d1dd47c
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorCube.java
@@ -0,0 +1,83 @@
+/*
+ * 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.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicColorMatrix;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class ColorCube extends TestBase {
+    private Allocation mCube;
+    private ScriptC_colorcube mScript;
+
+    public ColorCube() {
+    }
+
+    private void initCube() {
+        final int sx = 32;
+        final int sy = 32;
+        final int sz = 16;
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
+        tb.setX(sx);
+        tb.setY(sy);
+        tb.setZ(sz);
+        Type t = tb.create();
+        mCube = Allocation.createTyped(mRS, t);
+
+        int dat[] = new int[sx * sy * sz];
+        for (int z = 0; z < sz; z++) {
+            for (int y = 0; y < sy; y++) {
+                for (int x = 0; x < sx; x++ ) {
+
+                    dat[z*sy*sx + y*sx + x] = //0xff000000 |
+                        (((x >> 2) | (x<<3)) << 0) |
+                        (((y >> 2) | (y<<3)) << 8) |
+                        ((z | (z<<4)) << 16);
+
+
+                }
+            }
+        }
+
+        mCube.copyFromUnchecked(dat);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_colorcube(mRS, res, R.raw.colorcube);
+
+        initCube();
+        mScript.invoke_setCube(mCube);
+
+
+        //mScript.invoke_setMatrix(m);
+    }
+
+    public void runTest() {
+        mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
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 ac72688..18f438a 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -291,6 +291,9 @@
         case 35:
             mTest = new WhiteBalance();
             break;
+        case 36:
+            mTest = new ColorCube();
+            break;
         }
 
         mTest.createBaseTest(this, mBitmapIn, mBitmapIn2, mBitmapOut);
@@ -302,7 +305,7 @@
     }
 
     void setupTests() {
-        mTestNames = new String[36];
+        mTestNames = new String[37];
         mTestNames[0] = "Levels Vec3 Relaxed";
         mTestNames[1] = "Levels Vec4 Relaxed";
         mTestNames[2] = "Levels Vec3 Full";
@@ -339,6 +342,7 @@
         mTestNames[33] = "Contrast";
         mTestNames[34] = "Exposure";
         mTestNames[35] = "White Balance";
+        mTestNames[36] = "Color Cube";
 
         mTestSpinner.setAdapter(new ArrayAdapter<String>(
             this, R.layout.spinner_layout, mTestNames));
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colorcube.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colorcube.rs
new file mode 100644
index 0000000..09b3f69
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colorcube.rs
@@ -0,0 +1,97 @@
+/*
+ * 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.rs.image)
+#pragma rs_fp_relaxed
+
+
+static rs_allocation gCube;
+static short4 gDims;
+static short4 gFracMask;
+static short4 gFracBits;
+static short4 gFracShift;
+static int4 gFinalShift;
+static int4 gFinalAdd;
+
+void setCube(rs_allocation c) {
+    gCube = c;
+    gDims.x = rsAllocationGetDimX(gCube) - 1;
+    gDims.y = rsAllocationGetDimY(gCube) - 1;
+    gDims.z = rsAllocationGetDimZ(gCube) - 1;
+    gDims.w = 0;
+
+    gFracMask = gDims;
+    gFracBits = (short4)32 - clz(gFracMask);
+    gFracShift = (short4)8 - gFracBits;
+
+    rsDebug("dims", gDims);
+    rsDebug("gFracMask", gFracMask);
+    rsDebug("gFracBits", gFracBits);
+
+    gFinalShift = gFracShift.x + gFracShift.y + gFracShift.z;
+    gFinalAdd = (((int4)1 << gFinalShift) - (int4)1) >> (int4)1;
+
+    rsDebug("gFinalShift", gFinalShift);
+    rsDebug("gFinalAdd", gFinalAdd);
+
+}
+
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+    //rsDebug("root", in);
+
+    short4 baseCoord = convert_short4(*in);
+    short4 coord1 = baseCoord >> gFracShift;
+    short4 coord2 = min(coord1 + (short4)1, gDims);
+
+    short4 weight2 = baseCoord - (coord1 << gFracShift);
+    short4 weight1 = ((short4)1 << gFracShift) - weight2;
+
+    ushort4 v000 = convert_ushort4(rsGetElementAt_uchar4(gCube, coord1.x, coord1.y, coord1.z));
+    ushort4 v100 = convert_ushort4(rsGetElementAt_uchar4(gCube, coord2.x, coord1.y, coord1.z));
+    ushort4 v010 = convert_ushort4(rsGetElementAt_uchar4(gCube, coord1.x, coord2.y, coord1.z));
+    ushort4 v110 = convert_ushort4(rsGetElementAt_uchar4(gCube, coord2.x, coord2.y, coord1.z));
+    ushort4 v001 = convert_ushort4(rsGetElementAt_uchar4(gCube, coord1.x, coord1.y, coord2.z));
+    ushort4 v101 = convert_ushort4(rsGetElementAt_uchar4(gCube, coord2.x, coord1.y, coord2.z));
+    ushort4 v011 = convert_ushort4(rsGetElementAt_uchar4(gCube, coord1.x, coord2.y, coord2.z));
+    ushort4 v111 = convert_ushort4(rsGetElementAt_uchar4(gCube, coord2.x, coord2.y, coord2.z));
+
+    uint4 yz00 = convert_uint4((v000 * weight1.x) + (v100 * weight2.x));
+    uint4 yz10 = convert_uint4((v010 * weight1.x) + (v110 * weight2.x));
+    uint4 yz01 = convert_uint4((v001 * weight1.x) + (v101 * weight2.x));
+    uint4 yz11 = convert_uint4((v011 * weight1.x) + (v111 * weight2.x));
+
+    uint4 z0 = (yz00 * weight1.y) + (yz10 * weight2.y);
+    uint4 z1 = (yz01 * weight1.y) + (yz11 * weight2.y);
+
+    uint4 v = (z0 * weight1.z) + (z1 * weight2.z);
+
+    #if 0
+    if (x + y < 100) {
+        rsDebug("coord1", coord1);
+        rsDebug("coord2", coord2);
+        rsDebug("weight1", weight1);
+        rsDebug("weight2", weight2);
+        rsDebug("yz00", yz00);
+        rsDebug("z0", z0);
+        rsDebug("v", v);
+    }
+    #endif
+
+    *out = convert_uchar4((v + gFinalAdd) >> gFinalShift);
+    out->a = 0xff;
+}
+