am 58bf986c: Merge "Use ashmem for CursorWindows. Bug: 5332296" into ics-mr0

* commit '58bf986c3e3948242e89654e6d59b97a21345582':
  Use ashmem for CursorWindows. Bug: 5332296
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index fe0106d..33310df 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -768,61 +768,6 @@
     }
 
     /**
-     * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
-     * @deprecated use {@link CreateNdefMessageCallback} or {@link OnNdefPushCompleteCallback}
-     * @hide
-     */
-    @Deprecated
-    public interface NdefPushCallback {
-        /**
-         * @deprecated use {@link CreateNdefMessageCallback} instead
-         */
-        @Deprecated
-        NdefMessage createMessage();
-        /**
-         * @deprecated use{@link OnNdefPushCompleteCallback} instead
-         */
-        @Deprecated
-        void onMessagePushed();
-    }
-
-    /**
-     * TODO: Remove this
-     * Converts new callbacks to old callbacks.
-     */
-    static final class LegacyCallbackWrapper implements CreateNdefMessageCallback,
-            OnNdefPushCompleteCallback {
-        final NdefPushCallback mLegacyCallback;
-        LegacyCallbackWrapper(NdefPushCallback legacyCallback) {
-            mLegacyCallback = legacyCallback;
-        }
-        @Override
-        public void onNdefPushComplete(NfcEvent event) {
-            mLegacyCallback.onMessagePushed();
-        }
-        @Override
-        public NdefMessage createNdefMessage(NfcEvent event) {
-            return mLegacyCallback.createMessage();
-        }
-    }
-
-    /**
-     * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
-     * @deprecated use {@link #setNdefPushMessageCallback} instead
-     * @hide
-     */
-    @Deprecated
-    public void enableForegroundNdefPush(Activity activity, final NdefPushCallback callback) {
-        if (activity == null || callback == null) {
-            throw new NullPointerException();
-        }
-        enforceResumed(activity);
-        LegacyCallbackWrapper callbackWrapper = new LegacyCallbackWrapper(callback);
-        mNfcActivityManager.setNdefPushMessageCallback(activity, callbackWrapper);
-        mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callbackWrapper);
-    }
-
-    /**
      * Enable NDEF Push feature.
      * <p>This API is for the Settings application.
      * @hide
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 9dea4c4..70ec0af 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -32,12 +32,13 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
- * perform background operations and publish results on the UI thread without
- * having to manipulate threads and/or handlers.</p>
+ * <p>AsyncTask enables proper and easy use of the UI thread (also called main thread) or
+ * any other looper thread. AsyncTask is most commonly used to interact with the UI thread.
+ * This class allows to perform background operations and publish results on a looper
+ * thread without having to manipulate threads and/or handlers.</p>
  *
  * <p>An asynchronous task is defined by a computation that runs on a background thread and
- * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
+ * whose result is published on a looper thread. An asynchronous task is defined by 3 generic
  * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
  * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
  * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
@@ -101,7 +102,7 @@
  * <h2>The 4 steps</h2>
  * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
  * <ol>
- *     <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task
+ *     <li>{@link #onPreExecute()}, invoked on the looper thread immediately after the task
  *     is executed. This step is normally used to setup the task, for instance by
  *     showing a progress bar in the user interface.</li>
  *     <li>{@link #doInBackground}, invoked on the background thread
@@ -110,14 +111,14 @@
  *     of the asynchronous task are passed to this step. The result of the computation must
  *     be returned by this step and will be passed back to the last step. This step
  *     can also use {@link #publishProgress} to publish one or more units
- *     of progress. These values are published on the UI thread, in the
+ *     of progress. These values are published on the looper thread, in the
  *     {@link #onProgressUpdate} step.</li>
- *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a
+ *     <li>{@link #onProgressUpdate}, invoked on the looper thread after a
  *     call to {@link #publishProgress}. The timing of the execution is
  *     undefined. This method is used to display any form of progress in the user
  *     interface while the background computation is still executing. For instance,
  *     it can be used to animate a progress bar or show logs in a text field.</li>
- *     <li>{@link #onPostExecute}, invoked on the UI thread after the background
+ *     <li>{@link #onPostExecute}, invoked on the looper thread after the background
  *     computation finishes. The result of the background computation is passed to
  *     this step as a parameter.</li>
  * </ol>
@@ -135,8 +136,8 @@
  * <p>There are a few threading rules that must be followed for this class to
  * work properly:</p>
  * <ul>
- *     <li>The task instance must be created on the UI thread.</li>
- *     <li>{@link #execute} must be invoked on the UI thread.</li>
+ *     <li>The task instance must be created on the looper thread.</li>
+ *     <li>{@link #execute} must be invoked on the looper thread.</li>
  *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
  *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
  *     <li>The task can be executed only once (an exception will be thrown if
@@ -152,6 +153,9 @@
  *     <li>Set member fields in {@link #doInBackground}, and refer to them in
  *     {@link #onProgressUpdate} and {@link #onPostExecute}.
  * </ul>
+ * 
+ * @see Looper
+ * @see Handler
  */
 public abstract class AsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
@@ -187,7 +191,13 @@
     private static final int MESSAGE_POST_RESULT = 0x1;
     private static final int MESSAGE_POST_PROGRESS = 0x2;
 
-    private static final InternalHandler sHandler = new InternalHandler();
+    private static final ThreadLocal<InternalHandler> sHandler =
+            new ThreadLocal<InternalHandler>() {
+                @Override
+                protected InternalHandler initialValue() {
+                    return new InternalHandler();
+                }
+            };
 
     private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
     private final WorkerRunnable<Params, Result> mWorker;
@@ -196,6 +206,7 @@
     private volatile Status mStatus = Status.PENDING;
     
     private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
+    private final InternalHandler mHandler;
 
     private static class SerialExecutor implements Executor {
         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
@@ -242,9 +253,8 @@
         FINISHED,
     }
 
-    /** @hide Used to force static handler to be created. */
+    /** @hide */
     public static void init() {
-        sHandler.getLooper();
     }
 
     /** @hide */
@@ -253,14 +263,26 @@
     }
 
     /**
-     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+     * Creates a new asynchronous task. This constructor must be invoked on the looper thread.
+     * 
+     * @throws IllegalStateException if this constructor is invoked on a non-looper thread
+     * 
+     * @see Looper
      */
     public AsyncTask() {
+        if (Looper.myLooper() == null) {
+            throw new IllegalStateException("AsyncTask can be only instanciated on a " 
+                    + "looper thread. The current thread is " + Thread.currentThread());
+        }
+
+        mHandler = sHandler.get();
+
         mWorker = new WorkerRunnable<Params, Result>() {
             public Result call() throws Exception {
                 mTaskInvoked.set(true);
 
                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                //noinspection unchecked
                 return postResult(doInBackground(mParams));
             }
         };
@@ -295,7 +317,8 @@
     }
 
     private Result postResult(Result result) {
-        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
+        @SuppressWarnings({"unchecked"})
+        Message message = mHandler.obtainMessage(MESSAGE_POST_RESULT,
                 new AsyncTaskResult<Result>(this, result));
         message.sendToTarget();
         return result;
@@ -316,7 +339,7 @@
      * by the caller of this task.
      *
      * This method can call {@link #publishProgress} to publish updates
-     * on the UI thread.
+     * on the looper thread.
      *
      * @param params The parameters of the task.
      *
@@ -329,7 +352,7 @@
     protected abstract Result doInBackground(Params... params);
 
     /**
-     * Runs on the UI thread before {@link #doInBackground}.
+     * Runs on the looper thread before {@link #doInBackground}.
      *
      * @see #onPostExecute
      * @see #doInBackground
@@ -338,7 +361,7 @@
     }
 
     /**
-     * <p>Runs on the UI thread after {@link #doInBackground}. The
+     * <p>Runs on the looper thread after {@link #doInBackground}. The
      * specified result is the value returned by {@link #doInBackground}.</p>
      * 
      * <p>This method won't be invoked if the task was cancelled.</p>
@@ -354,7 +377,7 @@
     }
 
     /**
-     * Runs on the UI thread after {@link #publishProgress} is invoked.
+     * Runs on the looper thread after {@link #publishProgress} is invoked.
      * The specified values are the values passed to {@link #publishProgress}.
      *
      * @param values The values indicating progress.
@@ -367,7 +390,7 @@
     }
 
     /**
-     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+     * <p>Runs on the looper thread after {@link #cancel(boolean)} is invoked and
      * {@link #doInBackground(Object[])} has finished.</p>
      * 
      * <p>The default implementation simply invokes {@link #onCancelled()} and
@@ -390,7 +413,7 @@
      * This method is invoked by the default implementation of
      * {@link #onCancelled(Object)}.</p>
      * 
-     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+     * <p>Runs on the looper thread after {@link #cancel(boolean)} is invoked and
      * {@link #doInBackground(Object[])} has finished.</p>
      *
      * @see #onCancelled(Object) 
@@ -425,7 +448,7 @@
      * an attempt to stop the task.</p>
      * 
      * <p>Calling this method will result in {@link #onCancelled(Object)} being
-     * invoked on the UI thread after {@link #doInBackground(Object[])}
+     * invoked on the looper thread after {@link #doInBackground(Object[])}
      * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
      * is never invoked. After invoking this method, you should check the
      * value returned by {@link #isCancelled()} periodically from
@@ -498,14 +521,15 @@
      * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings on
      * its use.
      *
-     * <p>This method must be invoked on the UI thread.
+     * <p>This method must be invoked on the looper thread.
      *
      * @param params The parameters of the task.
      *
      * @return This instance of AsyncTask.
      *
      * @throws IllegalStateException If {@link #getStatus()} returns either
-     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED} or
+     *         the current thread is not a looper thread.
      */
     public final AsyncTask<Params, Progress, Result> execute(Params... params) {
         return executeOnExecutor(sDefaultExecutor, params);
@@ -531,7 +555,7 @@
      * executed in serial; to guarantee such work is serialized regardless of
      * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
      *
-     * <p>This method must be invoked on the UI thread.
+     * <p>This method must be invoked on the looper thread.
      *
      * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
      *              convenient process-wide thread pool for tasks that are loosely coupled.
@@ -540,10 +564,16 @@
      * @return This instance of AsyncTask.
      *
      * @throws IllegalStateException If {@link #getStatus()} returns either
-     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED} or
+     *         the current thread is not a looper thread.
      */
     public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
             Params... params) {
+        if (Looper.myLooper() == null) {
+            throw new IllegalStateException("AsyncTask can be only instanciated on a " 
+                    + "looper thread. The current thread is " + Thread.currentThread());
+        }
+        
         if (mStatus != Status.PENDING) {
             switch (mStatus) {
                 case RUNNING:
@@ -576,9 +606,9 @@
 
     /**
      * This method can be invoked from {@link #doInBackground} to
-     * publish updates on the UI thread while the background computation is
+     * publish updates on the looper thread while the background computation is
      * still running. Each call to this method will trigger the execution of
-     * {@link #onProgressUpdate} on the UI thread.
+     * {@link #onProgressUpdate} on the looper thread.
      *
      * {@link #onProgressUpdate} will note be called if the task has been
      * canceled.
@@ -590,7 +620,7 @@
      */
     protected final void publishProgress(Progress... values) {
         if (!isCancelled()) {
-            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
+            mHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                     new AsyncTaskResult<Progress>(this, values)).sendToTarget();
         }
     }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 4d7a9bb..cc2fa85 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -35,7 +35,6 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -902,15 +901,13 @@
             return false;
         }
 
+        // Thread policy controls BlockGuard.
         int threadPolicyMask = StrictMode.DETECT_DISK_WRITE |
                 StrictMode.DETECT_DISK_READ |
                 StrictMode.DETECT_NETWORK;
 
         if (!IS_USER_BUILD) {
             threadPolicyMask |= StrictMode.PENALTY_DROPBOX;
-            if (IS_ENG_BUILD) {
-                threadPolicyMask |= StrictMode.PENALTY_LOG;
-            }
         }
         if (doFlashes) {
             threadPolicyMask |= StrictMode.PENALTY_FLASH;
@@ -918,6 +915,8 @@
 
         StrictMode.setThreadPolicyMask(threadPolicyMask);
 
+        // VM Policy controls CloseGuard, detection of Activity leaks,
+        // and instance counting.
         if (IS_USER_BUILD) {
             setCloseGuardEnabled(false);
         } else {
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index 564f41c..c964961 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -24,6 +24,3 @@
 
 PRODUCT_COPY_FILES += $(foreach file,$(keyconfigs),\
     frameworks/base/data/keyboards/$(file):system/usr/idc/$(file))
-
-PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps) $(keyconfigs)
-
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 349b9e3..07995085 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -264,7 +264,7 @@
     layer->setFbo(0);
     layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
     layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f);
-    layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f);
+    layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f);
     layer->region.clear();
     layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer()
 
@@ -400,6 +400,18 @@
             renderer.setViewport(bitmap->width(), bitmap->height());
             renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
                     bitmap->width(), bitmap->height(), !layer->isBlend());
+
+            glDisable(GL_SCISSOR_TEST);
+            renderer.translate(0.0f, bitmap->height());
+            renderer.scale(1.0f, -1.0f);
+
+            mat4 texTransform(layer->getTexTransform());
+
+            mat4 invert;
+            invert.translate(0.0f, 1.0f, 0.0f);
+            invert.scale(1.0f, -1.0f, 1.0f);
+            layer->getTexTransform().multiply(invert);
+
             if ((error = glGetError()) != GL_NO_ERROR) goto error;
 
             {
@@ -413,6 +425,7 @@
                 if ((error = glGetError()) != GL_NO_ERROR) goto error;
             }
 
+            layer->getTexTransform().load(texTransform);
             status = true;
         }
 
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 198ae4c..77acfe6 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -35,14 +35,27 @@
 import java.lang.IllegalArgumentException;
 
 /**
- * TODO javadoc update for ComponentName - PendingIntent change
  * RemoteControlClient enables exposing information meant to be consumed by remote controls
  * capable of displaying metadata, artwork and media transport control buttons.
- * A remote control client object is associated with a media button event receiver. This
+ *
+ * <p>A remote control client object is associated with a media button event receiver. This
  * event receiver must have been previously registered with
  * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} before the
  * RemoteControlClient can be registered through
  * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
+ *
+ * <p>Here is an example of creating a RemoteControlClient instance after registering a media
+ * button event receiver:
+ * <pre>ComponentName myEventReceiver = new ComponentName(getPackageName(), MyRemoteControlEventReceiver.class.getName());
+ * AudioManager myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ * myAudioManager.registerMediaButtonEventReceiver(myEventReceiver);
+ * // build the PendingIntent for the remote control client
+ * Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ * mediaButtonIntent.setComponent(myEventReceiver);
+ * PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
+ * // create and register the remote control client
+ * RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
+ * myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre>
  */
 public class RemoteControlClient
 {
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 8569143..11b6c15 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -57,8 +56,6 @@
 
     private static final String TAG = "GlobalActions";
 
-    private StatusBarManager mStatusBar;
-
     private final Context mContext;
     private final AudioManager mAudioManager;
 
@@ -103,13 +100,12 @@
         mKeyguardShowing = keyguardShowing;
         mDeviceProvisioned = isDeviceProvisioned;
         if (mDialog == null) {
-            mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE);
             mDialog = createDialog();
         }
         prepareDialog();
 
-        mStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
         mDialog.show();
+        mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
     }
 
     /**
@@ -249,7 +245,6 @@
 
     /** {@inheritDoc} */
     public void onDismiss(DialogInterface dialog) {
-        mStatusBar.disable(StatusBarManager.DISABLE_NONE);
     }
 
     /** {@inheritDoc} */
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index b05705e..bcb1aa2 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -238,6 +238,11 @@
      * Notify our observers of an interface removal.
      */
     private void notifyInterfaceRemoved(String iface) {
+        // netd already clears out quota and alerts for removed ifaces; update
+        // our sanity-checking state.
+        mActiveAlertIfaces.remove(iface);
+        mActiveQuotaIfaces.remove(iface);
+
         for (INetworkManagementEventObserver obs : mObservers) {
             try {
                 obs.interfaceRemoved(iface);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
index 3232eedc..414ae0d 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
@@ -22,9 +22,11 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
 import android.graphics.SurfaceTexture;
 import android.opengl.GLUtils;
 import android.os.Bundle;
+import android.os.Environment;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.TextureView;
@@ -39,6 +41,7 @@
 import javax.microedition.khronos.egl.EGLSurface;
 import javax.microedition.khronos.opengles.GL;
 import java.io.BufferedOutputStream;
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -65,7 +68,8 @@
                 Bitmap b = mTextureView.getBitmap(800, 800);
                 BufferedOutputStream out = null;
                 try {
-                    out = new BufferedOutputStream(new FileOutputStream("/sdcard/out.png"));
+                    File dump = new File(Environment.getExternalStorageDirectory(), "out.png");
+                    out = new BufferedOutputStream(new FileOutputStream(dump));
                     b.compress(Bitmap.CompressFormat.PNG, 100, out);
                 } catch (FileNotFoundException e) {
                     e.printStackTrace();
@@ -168,10 +172,10 @@
         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
         private final float[] mTriangleVerticesData = {
                 // X, Y, Z, U, V
-                -1.0f, -1.0f, 0, 0.f, 0.f,
-                1.0f, -1.0f, 0, 1.f, 0.f,
-                -1.0f,  1.0f, 0, 0.f, 1.f,
-                1.0f,   1.0f, 0, 1.f, 1.f,
+                -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
+                 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
+                -1.0f,  1.0f, 0.0f, 0.0f, 1.0f,
+                 1.0f,  1.0f, 0.0f, 1.0f, 1.0f,
         };
 
         @Override
@@ -212,8 +216,6 @@
             while (!mFinished) {
                 checkCurrent();
 
-                Log.d(LOG_TAG, "Rendering frame");
-
                 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
                 checkGlError();
 
@@ -237,7 +239,7 @@
                 checkEglError();
 
                 try {
-                    Thread.sleep(20);
+                    Thread.sleep(2000);
                 } catch (InterruptedException e) {
                     // Ignore
                 }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
index fcb57d9..0f4c668 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
@@ -17,16 +17,23 @@
 package com.android.test.hwui;
 
 import android.app.Activity;
+import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.os.Bundle;
+import android.os.Environment;
 import android.view.Gravity;
+import android.view.Surface;
 import android.view.TextureView;
 import android.view.View;
 import android.widget.Button;
 import android.widget.FrameLayout;
 
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 
 @SuppressWarnings({"UnusedDeclaration"})
@@ -44,6 +51,26 @@
 
         mTextureView = new TextureView(this);
         mTextureView.setSurfaceTextureListener(this);
+        mTextureView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Bitmap b = mTextureView.getBitmap(800, 800);
+                BufferedOutputStream out = null;
+                try {
+                    File dump = new File(Environment.getExternalStorageDirectory(), "out.png");
+                    out = new BufferedOutputStream(new FileOutputStream(dump));
+                    b.compress(Bitmap.CompressFormat.PNG, 100, out);
+                } catch (FileNotFoundException e) {
+                    e.printStackTrace();
+                } finally {
+                    if (out != null) try {
+                        out.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        });
 
         Button button = new Button(this);
         button.setText("Remove/Add");
@@ -73,6 +100,8 @@
     @Override
     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
         mCamera = Camera.open();
+        mCamera.setDisplayOrientation(getCameraOrientation());
+
         Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
         mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
                 previewSize.width, previewSize.height, Gravity.CENTER));
@@ -86,6 +115,34 @@
         mCamera.startPreview();
     }
 
+    private int getCameraOrientation() {
+        Camera.CameraInfo info = new Camera.CameraInfo();
+        for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
+            Camera.getCameraInfo(i, info);
+            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) break;
+        }
+        
+        int rotation = getWindowManager().getDefaultDisplay().getRotation();
+        int degrees = 0;
+
+        switch (rotation) {
+            case Surface.ROTATION_0:
+                degrees = 0;
+                break;
+            case Surface.ROTATION_90:
+                degrees = 90;
+                break;
+            case Surface.ROTATION_180:
+                degrees = 180;
+                break;
+            case Surface.ROTATION_270:
+                degrees = 270;
+                break;
+        }
+
+        return (info.orientation - degrees + 360) % 360;
+    }
+
     @Override
     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
         // Ignored, the Camera does all the work for us