Merge changes I49b383d3,I70809401

* changes:
  MTP: Don't dump data packets twice in debug output.
  Add support for range and enum forms in MTP ObjectPropDescs
diff --git a/api/current.xml b/api/current.xml
index 99c62a8..63215a0 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -245987,7 +245987,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index f55b746..f0758fd 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -59,6 +59,7 @@
 static bool gPreferSoftwareCodec;
 static bool gPlaybackAudio;
 static bool gWriteMP4;
+static bool gDisplayHistogram;
 static String8 gWriteMP4Filename;
 
 static int64_t getNowUs() {
@@ -68,6 +69,44 @@
     return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
 }
 
+static int CompareIncreasing(const int64_t *a, const int64_t *b) {
+    return (*a) < (*b) ? -1 : (*a) > (*b) ? 1 : 0;
+}
+
+static void displayDecodeHistogram(Vector<int64_t> *decodeTimesUs) {
+    printf("decode times:\n");
+
+    decodeTimesUs->sort(CompareIncreasing);
+
+    size_t n = decodeTimesUs->size();
+    int64_t minUs = decodeTimesUs->itemAt(0);
+    int64_t maxUs = decodeTimesUs->itemAt(n - 1);
+
+    printf("min decode time %lld us (%.2f secs)\n", minUs, minUs / 1E6);
+    printf("max decode time %lld us (%.2f secs)\n", maxUs, maxUs / 1E6);
+
+    size_t counts[100];
+    for (size_t i = 0; i < 100; ++i) {
+        counts[i] = 0;
+    }
+
+    for (size_t i = 0; i < n; ++i) {
+        int64_t x = decodeTimesUs->itemAt(i);
+
+        size_t slot = ((x - minUs) * 100) / (maxUs - minUs);
+        if (slot == 100) { slot = 99; }
+
+        ++counts[slot];
+    }
+
+    for (size_t i = 0; i < 100; ++i) {
+        int64_t slotUs = minUs + (i * (maxUs - minUs) / 100);
+
+        double fps = 1E6 / slotUs;
+        printf("[%.2f fps]: %d\n", fps, counts[i]);
+    }
+}
+
 static void playSource(OMXClient *client, sp<MediaSource> &source) {
     sp<MetaData> meta = source->getFormat();
 
@@ -201,6 +240,8 @@
     int64_t sumDecodeUs = 0;
     int64_t totalBytes = 0;
 
+    Vector<int64_t> decodeTimesUs;
+
     while (numIterationsLeft-- > 0) {
         long numFrames = 0;
 
@@ -224,9 +265,17 @@
                 break;
             }
 
-            if (buffer->range_length() > 0 && (n++ % 16) == 0) {
-                printf(".");
-                fflush(stdout);
+            if (buffer->range_length() > 0) {
+                if (gDisplayHistogram && n > 0) {
+                    // Ignore the first time since it includes some setup
+                    // cost.
+                    decodeTimesUs.push(delayDecodeUs);
+                }
+
+                if ((n++ % 16) == 0) {
+                    printf(".");
+                    fflush(stdout);
+                }
             }
 
             sumDecodeUs += delayDecodeUs;
@@ -266,6 +315,10 @@
                (double)sumDecodeUs / n);
 
         printf("decoded a total of %d frame(s).\n", n);
+
+        if (gDisplayHistogram) {
+            displayDecodeHistogram(&decodeTimesUs);
+        }
     } else if (!strncasecmp("audio/", mime, 6)) {
         // Frame count makes less sense for audio, as the output buffer
         // sizes may be different across decoders.
@@ -466,6 +519,8 @@
     fprintf(stderr, "       -o playback audio\n");
     fprintf(stderr, "       -w(rite) filename (write to .mp4 file)\n");
     fprintf(stderr, "       -k seek test\n");
+    fprintf(stderr, "       -x display a histogram of decoding times/fps "
+                    "(video only)\n");
 }
 
 int main(int argc, char **argv) {
@@ -482,12 +537,13 @@
     gPreferSoftwareCodec = false;
     gPlaybackAudio = false;
     gWriteMP4 = false;
+    gDisplayHistogram = false;
 
     sp<ALooper> looper;
     sp<ARTSPController> rtspController;
 
     int res;
-    while ((res = getopt(argc, argv, "han:lm:b:ptsow:k")) >= 0) {
+    while ((res = getopt(argc, argv, "han:lm:b:ptsow:kx")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -560,6 +616,12 @@
                 break;
             }
 
+            case 'x':
+            {
+                gDisplayHistogram = true;
+                break;
+            }
+
             case '?':
             case 'h':
             default:
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 7f24d27..03a98d1 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -35,6 +35,7 @@
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.util.AndroidRuntimeException;
 import android.util.Slog;
 
@@ -285,10 +286,16 @@
                 if (ActivityThread.localLOGV)
                     Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + mLibDir);
 
+                // Temporarily disable logging of disk reads on the Looper thread
+                // as this is early and necessary.
+                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+
                 mClassLoader =
                     ApplicationLoaders.getDefault().getClassLoader(
                         zip, mLibDir, mBaseClassLoader);
                 initializeJavaContextClassLoader();
+
+                StrictMode.setThreadPolicy(oldPolicy);
             } else {
                 if (mBaseClassLoader == null) {
                     mClassLoader = ClassLoader.getSystemClassLoader();
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 79425d6..41bb364 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1023,12 +1023,14 @@
 
     private void setJournalMode(final String dbPath, final String mode) {
         // journal mode can be set only for non-memory databases
-        if (!dbPath.equalsIgnoreCase(MEMORY_DB_PATH)) {
-            String s = DatabaseUtils.stringForQuery(this, "PRAGMA journal_mode=" + mode, null);
-            if (!s.equalsIgnoreCase(mode)) {
-                Log.e(TAG, "setting journal_mode to " + mode + " failed for db: " + dbPath +
-                        " (on pragma set journal_mode, sqlite returned:" + s);
-            }
+        // AND can't be set for readonly databases
+        if (dbPath.equalsIgnoreCase(MEMORY_DB_PATH) || isReadOnly()) {
+            return;
+        }
+        String s = DatabaseUtils.stringForQuery(this, "PRAGMA journal_mode=" + mode, null);
+        if (!s.equalsIgnoreCase(mode)) {
+            Log.e(TAG, "setting journal_mode to " + mode + " failed for db: " + dbPath +
+                    " (on pragma set journal_mode, sqlite returned:" + s);
         }
     }
 
@@ -2317,6 +2319,10 @@
      * @return true if write-ahead-logging is set. false otherwise
      */
     public boolean enableWriteAheadLogging() {
+        // make sure the database is not READONLY. WAL doesn't make sense for readonly-databases.
+        if (isReadOnly()) {
+            return false;
+        }
         // acquire lock - no that no other thread is enabling WAL at the same time
         lock();
         try {
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 98bf632..32ff3b3 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -111,6 +111,27 @@
      * This can be achieved by applying a <i>high-pass</i> filter. Conversely, a
      * <i>low-pass</i> filter can be used to isolate the force of gravity.
      * </p>
+     *
+     * <pre class="prettyprint">
+     *
+     *     public void onSensorChanged(SensorEvent event)
+     *     {
+     *          // alpha is calculated as t / (t + dT)
+     *          // with t, the low-pass filter's time-constant
+     *          // and dT, the event delivery rate
+     *
+     *          final float alpha = 0.8;
+     *
+     *          gravity[0] = alpha * gravity[0] + (1 - alpha) * event.data[0];
+     *          gravity[1] = alpha * gravity[1] + (1 - alpha) * event.data[1];
+     *          gravity[2] = alpha * gravity[2] + (1 - alpha) * event.data[2];
+     *
+     *          linear_acceleration[0] = event.data[0] - gravity[0];
+     *          linear_acceleration[1] = event.data[1] - gravity[1];
+     *          linear_acceleration[2] = event.data[2] - gravity[2];
+     *     }
+     * </pre>
+     *
      * <p>
      * <u>Examples</u>:
      * <ul>
@@ -143,8 +164,41 @@
      *  standard mathematical definition of positive rotation and does not agree with the
      *  definition of roll given earlier.
      *
+     * <ul>
+     * <p>
+     * values[0]: Angular speed around the x-axis
+     * </p>
+     * <p>
+     * values[1]: Angular speed around the y-axis
+     * </p>
+     * <p>
+     * values[2]: Angular speed around the z-axis
+     * </p>
+     * </ul>
+     * <p>
+     * Typically the output of the gyroscope is integrated over time to calculate
+     * an angle, for example:
+     * </p>
+     * <pre class="prettyprint">
+     *     private static final float NS2S = 1.0f / 1000000000.0f;
+     *     private float timestamp;
+     *     public void onSensorChanged(SensorEvent event)
+     *     {
+     *          if (timestamp != 0) {
+     *              final float dT = (event.timestamp - timestamp) * NS2S;
+     *              angle[0] += event.data[0] * dT;
+     *              angle[1] += event.data[1] * dT;
+     *              angle[2] += event.data[2] * dT;
+     *          }
+     *          timestamp = event.timestamp;
+     *     }
+     * </pre>
+     *
+     * <p>In practice, the gyroscope noise and offset will introduce some errors which need
+     * to be compensated for. This is usually done using the information from other
+     * sensors, but is beyond the scope of this document.</p>
+     *
      * <h4>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h4>
-     * 
      * <ul>
      * <p>
      * values[0]: Ambient light level in SI lux units
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index b8a1113..f8f8a29 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -99,6 +99,7 @@
     private final boolean mSecure;
 
     /** @deprecated Use {@link #getDefault(int)} instead. */
+    @Deprecated
     public SSLCertificateSocketFactory(int handshakeTimeoutMillis) {
         this(handshakeTimeoutMillis, null, true);
     }
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index 363a311..92be373 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -102,14 +102,15 @@
             }
         }
 
-        return verifyServerDomainAndCertificates((X509Certificate[]) peerCertificates, domain);
+        return verifyServerDomainAndCertificates((X509Certificate[]) peerCertificates, domain, "RSA");
     }
 
     /**
      * Similar to doHandshakeAndValidateServerCertificates but exposed to JNI for use
      * by Chromium HTTPS stack to validate the cert chain.
-     * @param certChain The bytes for certificates in ASN.1 DER encoded certficates format.
+     * @param certChain The bytes for certificates in ASN.1 DER encoded certificates format.
      * @param domain The full website hostname and domain
+     * @param authType The authentication type for the cert chain
      * @return An SSL error object if there is an error and null otherwise
      */
     public static SslError verifyServerCertificates(
@@ -126,18 +127,19 @@
             serverCertificates[i] = new X509CertImpl(certChain[i]);
         }
 
-        return verifyServerDomainAndCertificates(serverCertificates, domain);
+        return verifyServerDomainAndCertificates(serverCertificates, domain, authType);
     }
 
     /**
      * Common code of doHandshakeAndValidateServerCertificates and verifyServerCertificates.
-     * Calls DomainNamevalidator to valide the domain, and TrustManager to valide the certs.
+     * Calls DomainNamevalidator to verify the domain, and TrustManager to verify the certs.
      * @param chain the cert chain in X509 cert format.
      * @param domain The full website hostname and domain
+     * @param authType The authentication type for the cert chain
      * @return An SSL error object if there is an error and null otherwise
      */
     private static SslError verifyServerDomainAndCertificates(
-            X509Certificate[] chain, String domain)
+            X509Certificate[] chain, String domain, String authType)
             throws IOException {
         // check if the first certificate in the chain is for this site
         X509Certificate currCertificate = chain[0];
@@ -153,7 +155,7 @@
         }
 
         try {
-            SSLParametersImpl.getDefaultTrustManager().checkServerTrusted(chain, "RSA");
+            SSLParametersImpl.getDefaultTrustManager().checkServerTrusted(chain, authType);
             return null;  // No errors.
         } catch (CertificateException e) {
             if (HttpLog.LOGV) {
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index c29926c..30f25a2 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -112,6 +112,7 @@
      * @param validNotAfter The not-after date from the certificate validity period in ISO 8601 format
      * @deprecated Use {@link #SslCertificate(String, String, Date, Date)}
      */
+    @Deprecated
     public SslCertificate(
             String issuedTo, String issuedBy, String validNotBefore, String validNotAfter) {
         this(issuedTo, issuedBy, parseDate(validNotBefore), parseDate(validNotAfter));
@@ -157,6 +158,7 @@
      *
      * @deprecated Use {@link #getValidNotBeforeDate()}
      */
+    @Deprecated
     public String getValidNotBefore() {
         return formatDate(mValidNotBefore);
     }
@@ -175,6 +177,7 @@
      *
      * @deprecated Use {@link #getValidNotAfterDate()}
      */
+    @Deprecated
     public String getValidNotAfter() {
         return formatDate(mValidNotAfter);
     }
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 88ce0f0..c007605 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -861,6 +861,8 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
+                        Events.SYNC_ADAPTER_DATA);
 
                 Entity entity = new Entity(cv);
                 Cursor subCursor;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1fe2c5a..ddfcb06 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2396,14 +2396,6 @@
                 "selected_input_method_subtype";
 
         /**
-         * Setting to record the history of input method subtype, holding the pair of ID of IME
-         * and its last used subtype.
-         * @hide
-         */
-        public static final String INPUT_METHODS_SUBTYPE_HISTORY =
-                "input_methods_subtype_history";
-
-        /**
          * Whether the device has been provisioned (0 = false, 1 = true)
          */
         public static final String DEVICE_PROVISIONED = "device_provisioned";
diff --git a/core/java/android/util/EventLogTags.java b/core/java/android/util/EventLogTags.java
index 5cf5332..8c18417 100644
--- a/core/java/android/util/EventLogTags.java
+++ b/core/java/android/util/EventLogTags.java
@@ -29,6 +29,7 @@
  * @deprecated This class is no longer functional.
  * Use {@link android.util.EventLog} instead.
  */
+@Deprecated
 public class EventLogTags {
     public static class Description {
         public final int mTag;
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 8691714..0e9d9b7 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -951,8 +951,12 @@
                 }
                 equalIndex = cookieString.indexOf(EQUAL, index);
                 if (equalIndex > 0) {
-                    String name = cookieString.substring(index, equalIndex)
-                            .toLowerCase();
+                    String name = cookieString.substring(index, equalIndex).toLowerCase();
+                    int valueIndex = equalIndex + 1;
+                    while (valueIndex < length && cookieString.charAt(valueIndex) == WHITE_SPACE) {
+                        valueIndex++;
+                    }
+
                     if (name.equals(EXPIRES)) {
                         int comaIndex = cookieString.indexOf(COMMA, equalIndex);
 
@@ -960,7 +964,7 @@
                         // (Weekday, DD-Mon-YY HH:MM:SS GMT) if it applies.
                         // "Wednesday" is the longest Weekday which has length 9
                         if ((comaIndex != -1) &&
-                                (comaIndex - equalIndex <= 10)) {
+                                (comaIndex - valueIndex <= 10)) {
                             index = comaIndex + 1;
                         }
                     }
@@ -975,8 +979,7 @@
                     } else {
                         index = Math.min(semicolonIndex, commaIndex);
                     }
-                    String value =
-                            cookieString.substring(equalIndex + 1, index);
+                    String value = cookieString.substring(valueIndex, index);
                     
                     // Strip quotes if they exist
                     if (value.length() > 2 && value.charAt(0) == QUOTATION) {
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 3f6b10a..c788a53 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -564,7 +564,7 @@
                 && exceedsMinScaleIncrement(mTextWrapScale, newTextWrapScale)) {
             mTextWrapScale = newTextWrapScale;
             refreshZoomScale(true);
-        } else if (!mInZoomOverview) {
+        } else if (!mInZoomOverview && willScaleTriggerZoom(getZoomOverviewScale())) {
             zoomToOverview();
         } else {
             zoomToReadingLevel();
@@ -589,8 +589,6 @@
     }
 
     private void zoomToOverview() {
-        if (!willScaleTriggerZoom(getZoomOverviewScale())) return;
-
         // Force the titlebar fully reveal in overview mode
         int scrollY = mWebView.getScrollY();
         if (scrollY < mWebView.getTitleHeight()) {
@@ -800,9 +798,9 @@
      */
     public void onNewPicture(WebViewCore.DrawData drawData) {
         final int viewWidth = mWebView.getViewWidth();
-
-        if (mWebView.getSettings().getUseWideViewPort()) {
-            if (!mWebView.getSettings().getUseFixedViewport()) {
+        WebSettings settings = mWebView.getSettings();
+        if (settings.getUseWideViewPort()) {
+            if (!settings.getUseFixedViewport()) {
                 // limit mZoomOverviewWidth upper bound to
                 // sMaxViewportWidth so that if the page doesn't behave
                 // well, the WebView won't go insane. limit the lower
@@ -812,7 +810,13 @@
                             Math.max(drawData.mMinPrefWidth, drawData.mViewSize.x))));
             } else {
                 final int contentWidth = Math.max(drawData.mContentSize.x, drawData.mMinPrefWidth);
-                setZoomOverviewWidth(Math.min(WebView.sMaxViewportWidth, contentWidth));
+                final int newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, contentWidth);
+                if (newZoomOverviewWidth != mZoomOverviewWidth) {
+                    setZoomOverviewWidth(newZoomOverviewWidth);
+                    if (settings.isNarrowColumnLayout() && (mInitialZoomOverview || mInZoomOverview)) {
+                        mTextWrapScale = getReadingLevelScale();
+                    }
+                }
             }
         }
 
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index ec31dd4..50c88db 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -27,7 +27,6 @@
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
 import android.net.Uri;
-import android.os.PowerManager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -35,7 +34,7 @@
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
-import android.widget.MediaController.*;
+import android.widget.MediaController.MediaPlayerControl;
 
 import java.io.IOException;
 import java.util.Map;
@@ -458,6 +457,10 @@
                 }
                 start();
                 if (mMediaController != null) {
+                    if (mMediaController.isShowing()) {
+                        // ensure the controller will get repositioned later
+                        mMediaController.hide();
+                    }
                     mMediaController.show();
                 }
             }
diff --git a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
index e961116..d2e53b3 100644
--- a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
+++ b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.service.wallpaper;
 
+import java.io.IOException;
+
 import com.android.internal.view.WindowManagerPolicyThread;
 
 import android.app.WallpaperManager;
@@ -169,10 +171,26 @@
 
         void updateWallpaper() {
             synchronized (mLock) {
+                Throwable exception = null;
                 try {
                     mBackground = mWallpaperManager.getFastDrawable();
                 } catch (RuntimeException e) {
-                    Log.w("ImageWallpaper", "Unable to load wallpaper!", e);
+                    exception = e;
+                } catch (OutOfMemoryError e) {
+                    exception = e;
+                }
+                if (exception != null) {
+                    mBackground = null;
+                    // Note that if we do fail at this, and the default wallpaper can't
+                    // be loaded, we will go into a cycle.  Don't do a build where the
+                    // default wallpaper can't be loaded.
+                    Log.w("ImageWallpaper", "Unable to load wallpaper!", exception);
+                    try {
+                        mWallpaperManager.clear();
+                    } catch (IOException ex) {
+                        // now we're really screwed.
+                        Log.w("ImageWallpaper", "Unable reset to default wallpaper!", ex);
+                    }
                 }
             }
         }
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index 3c971da..9bec8a3 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -18,8 +18,17 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <!-- Do nothing. -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+        android:detachWallpaper="true" android:shareInterpolator="false">
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="0.9" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:fillEnabled="true" android:fillBefore="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="@android:integer/config_mediumAnimTime"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:fillEnabled="true" android:fillBefore="true"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:startOffset="@android:integer/config_mediumAnimTime"
             android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index 2001e20..ca60214 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -22,9 +22,15 @@
     <scale android:fromXScale="1.0" android:toXScale="1.0"
             android:fromYScale="1.0" android:toYScale="0.9"
             android:pivotX="50%p" android:pivotY="50%p"
+            android:fillEnabled="true" android:fillAfter="true"
             android:interpolator="@anim/decelerate_quint_interpolator"
             android:duration="@android:integer/config_mediumAnimTime" />
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:fillEnabled="true" android:fillAfter="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+    <!-- This is just to keep the animation running for the entire duration. -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+            android:startOffset="@android:integer/config_mediumAnimTime"
             android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 6c62e61..4f72ba2 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -22,9 +22,13 @@
     <scale android:fromXScale="1.0" android:toXScale="1.0"
             android:fromYScale="0.9" android:toYScale="1.0"
             android:pivotX="50%p" android:pivotY="50%p"
+            android:fillEnabled="true" android:fillBefore="true"
             android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="@android:integer/config_mediumAnimTime"
             android:duration="@android:integer/config_mediumAnimTime" />
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:fillEnabled="true" android:fillBefore="true"
             android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:startOffset="@android:integer/config_mediumAnimTime"
             android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 3c971da..6174151 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -18,8 +18,19 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <!-- Do nothing. -->
+        android:detachWallpaper="true" android:shareInterpolator="false">
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="1.0" android:toYScale="0.9"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:fillEnabled="true" android:fillAfter="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+            android:fillEnabled="true" android:fillAfter="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+    <!-- This is just to keep the animation running for the entire duration. -->
     <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+            android:startOffset="@android:integer/config_mediumAnimTime"
             android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/layout/preference.xml b/core/res/res/layout/preference.xml
index ba0e225..46c3e9c 100644
--- a/core/res/res/layout/preference.xml
+++ b/core/res/res/layout/preference.xml
@@ -24,9 +24,13 @@
     android:gravity="center_vertical"
     android:paddingRight="?android:attr/scrollbarSize">
 
-    <View
-        android:layout_width="@dimen/preference_widget_width"
-        android:layout_height="match_parent" />
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@+android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/preference_widget_width"
+        android:gravity="center"
+        android:orientation="vertical" />
 
     <RelativeLayout
         android:layout_width="wrap_content"
@@ -54,12 +58,5 @@
             android:maxLines="4" />
 
     </RelativeLayout>
-    
-    <!-- Preference should place its actual preference widget here. -->
-    <LinearLayout android:id="@+android:id/widget_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center_vertical"
-        android:orientation="vertical" />
 
 </LinearLayout>
diff --git a/core/res/res/layout/preference_child.xml b/core/res/res/layout/preference_child.xml
index d6f1182..713aa17 100644
--- a/core/res/res/layout/preference_child.xml
+++ b/core/res/res/layout/preference_child.xml
@@ -22,9 +22,13 @@
     android:gravity="center_vertical"
     android:paddingRight="?android:attr/scrollbarSize">
 
-    <View
-        android:layout_width="@dimen/preference_widget_width"
-        android:layout_height="match_parent" />
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@+android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/preference_widget_width"
+        android:gravity="center"
+        android:orientation="vertical" />
 
     <RelativeLayout
         android:layout_width="wrap_content"
@@ -54,11 +58,4 @@
 
     </RelativeLayout>
 
-    <!-- Preference should place its actual preference widget here. -->
-    <LinearLayout android:id="@+android:id/widget_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center_vertical"
-        android:orientation="vertical" />
-
 </LinearLayout>
diff --git a/core/res/res/layout/preference_dialog.xml b/core/res/res/layout/preference_dialog.xml
index 035b411..9e988ad 100644
--- a/core/res/res/layout/preference_dialog.xml
+++ b/core/res/res/layout/preference_dialog.xml
@@ -15,52 +15,9 @@
 -->
 
 <!-- Layout used by DialogPreference in a PreferenceActivity. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
-    android:layout_width="match_parent"
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
-    android:gravity="center_vertical"
-    android:paddingRight="?android:attr/scrollbarSize">
-    
-    <!-- Preference should place its actual preference widget here. -->
-    <LinearLayout android:id="@+android:id/widget_frame"
-        android:layout_width="@dimen/preference_widget_width"
-        android:layout_height="match_parent"
-        android:gravity="center"
-        android:orientation="horizontal" >
-        <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:background="@drawable/btn_circle"
-            android:src="@drawable/ic_btn_round_more" />
-    </LinearLayout>
-
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginRight="6dip"
-        android:layout_marginTop="6dip"
-        android:layout_marginBottom="6dip"
-        android:layout_weight="1">
-    
-        <TextView android:id="@+android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal" />
-            
-        <TextView android:id="@+android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_alignLeft="@android:id/title"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="4" />
-
-    </RelativeLayout>
-</LinearLayout>
-
+    android:layout_gravity="center"
+    android:background="@drawable/btn_circle"
+    android:src="@drawable/ic_btn_round_more" />
diff --git a/core/res/res/layout/preference_widget_checkbox.xml b/core/res/res/layout/preference_widget_checkbox.xml
index 1ca4393..33a43f4 100644
--- a/core/res/res/layout/preference_widget_checkbox.xml
+++ b/core/res/res/layout/preference_widget_checkbox.xml
@@ -16,54 +16,10 @@
 
 <!-- Layout used by CheckBoxPreference for the checkbox style. This is inflated
      inside android.R.layout.preference. -->
-<!-- Layout used by DialogPreference in a PreferenceActivity. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
-    android:layout_width="match_parent"
+<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+android:id/checkbox" 
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
-    android:gravity="center_vertical"
-    android:paddingRight="?android:attr/scrollbarSize">
-
-    <!-- Preference should place its actual preference widget here. -->
-    <LinearLayout android:id="@+android:id/widget_frame"
-        android:layout_width="@dimen/preference_widget_width"
-        android:layout_height="match_parent"
-        android:gravity="center"
-        android:orientation="horizontal" >
-        <CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
-            android:id="@+android:id/checkbox" 
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:focusable="false"
-            android:clickable="false" />
-    </LinearLayout>
-
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginRight="6dip"
-        android:layout_marginTop="6dip"
-        android:layout_marginBottom="6dip"
-        android:layout_weight="1">
-
-        <TextView android:id="@+android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal" />
-            
-        <TextView android:id="@+android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_alignLeft="@android:id/title"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="4" />
-
-    </RelativeLayout>
-</LinearLayout>
-
+    android:layout_gravity="center"
+    android:focusable="false"
+    android:clickable="false" />
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index 115cb30..f52c627 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -31,7 +31,7 @@
     <dimen name="password_keyboard_key_height_numeric">0.47in</dimen>
 
     <!-- The width that is used when creating thumbnails of applications. -->
-    <dimen name="thumbnail_width">256dp</dimen>
+    <dimen name="thumbnail_width">160dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
-    <dimen name="thumbnail_height">255dp</dimen>
+    <dimen name="thumbnail_height">100dp</dimen>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1fd5146..3050345 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2305,11 +2305,9 @@
     <!-- See USB_STORAGE.  USB_STORAGE_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to mount.  This is the title. -->
     <string name="usb_storage_title">USB connected</string>
     <!-- See USB_STORAGE.    This is the message. [CHAR LIMIT=NONE] -->
-    <string name="usb_storage_message" product="tablet">You have connected your tablet to your computer via USB. Touch the button below if you want to copy files between your computer and your Android\u2018s USB storage.</string>
+    <string name="usb_storage_message" product="nosdcard">You have connected to your computer via USB. Touch the button below if you want to copy files between your computer and your Android\u2018s USB storage.</string>
     <!-- See USB_STORAGE.    This is the message. [CHAR LIMIT=NONE] -->
-    <string name="usb_storage_message" product="nosdcard">You have connected your phone to your computer via USB. Touch the button below if you want to copy files between your computer and your Android\u2018s USB storage.</string>
-    <!-- See USB_STORAGE.    This is the message. -->
-    <string name="usb_storage_message" product="default">You have connected your phone to your computer via USB. Touch the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
+    <string name="usb_storage_message" product="default">You have connected to your computer via USB. Touch the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
     <!-- See USB_STORAGE.    This is the button text to mount the phone on the computer. -->
     <string name="usb_storage_button_mount">Turn on USB storage</string>
     <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 1fd2565..f63805b 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -818,18 +818,18 @@
         <item name="android:shouldDisableView">false</item>
         <item name="android:selectable">false</item>
     </style>
-    
+
     <style name="Preference.CheckBoxPreference">
-        <item name="android:layout">@android:layout/preference_widget_checkbox</item>
+        <item name="android:widgetLayout">@android:layout/preference_widget_checkbox</item>
     </style>
-    
+
     <style name="Preference.PreferenceScreen">
     </style>
 
     <style name="Preference.DialogPreference">
         <item name="android:positiveButtonText">@android:string/ok</item>
         <item name="android:negativeButtonText">@android:string/cancel</item>
-        <item name="android:layout">@android:layout/preference_dialog</item>
+        <item name="android:widgetLayout">@android:layout/preference_dialog</item>
     </style>
     
     <style name="Preference.DialogPreference.YesNoPreference">
@@ -845,7 +845,7 @@
         <item name="android:ringtoneType">ringtone</item>
         <item name="android:showSilent">true</item>
         <item name="android:showDefault">true</item>
-        <item name="android:layout">@android:layout/preference_dialog</item>
+        <item name="android:widgetLayout">@android:layout/preference_dialog</item>
     </style>
 
     <!-- Other Misc Styles -->
diff --git a/core/tests/coretests/src/android/net/http/CookiesTest.java b/core/tests/coretests/src/android/net/http/CookiesTest.java
index c9eca03..e736bc9 100644
--- a/core/tests/coretests/src/android/net/http/CookiesTest.java
+++ b/core/tests/coretests/src/android/net/http/CookiesTest.java
@@ -19,15 +19,20 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.util.List;
 import java.util.logging.Logger;
 import java.util.logging.SimpleFormatter;
 import java.util.logging.StreamHandler;
 import junit.framework.TestCase;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.params.ConnRoutePNames;
 import org.apache.http.impl.client.DefaultHttpClient;
 import tests.http.MockResponse;
 import tests.http.MockWebServer;
+import tests.http.RecordedRequest;
 
 public final class CookiesTest extends TestCase {
 
@@ -66,4 +71,38 @@
             logger.removeHandler(handler);
         }
     }
+
+    /**
+     * Test that cookies aren't case-sensitive with respect to hostname.
+     * http://b/3167208
+     */
+    public void testCookiesWithNonMatchingCase() throws Exception {
+        // use a proxy so we can manipulate the origin server's host name
+        server = new MockWebServer();
+        server.enqueue(new MockResponse()
+                .addHeader("Set-Cookie: a=first; Domain=my.t-mobile.com")
+                .addHeader("Set-Cookie: b=second; Domain=.T-mobile.com")
+                .addHeader("Set-Cookie: c=third; Domain=.t-mobile.com")
+                .setBody("This response sets some cookies."));
+        server.enqueue(new MockResponse()
+                .setBody("This response gets those cookies back."));
+        server.play();
+
+        HttpClient client = new DefaultHttpClient();
+        client.getParams().setParameter(
+                ConnRoutePNames.DEFAULT_PROXY, new HttpHost("localhost", server.getPort()));
+
+        HttpResponse getCookies = client.execute(new HttpGet("http://my.t-mobile.com/"));
+        getCookies.getEntity().consumeContent();
+        server.takeRequest();
+
+        HttpResponse sendCookies = client.execute(new HttpGet("http://my.t-mobile.com/"));
+        sendCookies.getEntity().consumeContent();
+        RecordedRequest sendCookiesRequest = server.takeRequest();
+        assertContains(sendCookiesRequest.getHeaders(), "Cookie: a=first; b=second; c=third");
+    }
+
+    private void assertContains(List<String> headers, String header) {
+        assertTrue(headers.toString(), headers.contains(header));
+    }
 }
diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd
index e46dbb4..acb6f9f 100644
--- a/docs/html/guide/topics/graphics/2d-graphics.jd
+++ b/docs/html/guide/topics/graphics/2d-graphics.jd
@@ -257,9 +257,9 @@
 <p>
     The border is used to define the stretchable and static areas of 
     the image. You indicate a stretchable section by drawing one (or more) 1-pixel-wide 
-    black line(s) in the left and top part of the border. (You can have as 
-    many stretchable sections as you want.) The relative size of the stretchable 
-    sections stays the same, so the largest sections always remain the largest.
+    black line(s) in the left and top part of the border (the other border pixels should
+    be fully transparent or white). You can have as many stretchable sections as you want:
+    their relative size stays the same, so the largest sections always remain the largest.
 </p>
 <p>
     You can also define an optional drawable section of the image (effectively, 
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 632fa4b..350f2b0 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -103,12 +103,54 @@
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
         <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
-ADT 0.9.9</a> <em>(September 2010)</em>
+ADT 8.0</a> <em>(November 2010)</em>
   <div class="toggleme">
 
+<dl>
 
+<dt>Dependencies:</dt>
+
+<p><p>ADT 8.0 is designed for use with SDK Tools r8. If you haven't
+already installed SDK Tools r8 into your SDK, use the Android SDK and AVD Manager to do
+so.</p></dd>
+
+<dt>General notes:</dt>
+<dd>
+<ul>
+  <li>New version number scheme that follows the SDK Tools revision number. The major version
+number for your ADT plugin should now always match the revision number of your SDK Tools. For
+example, ADT 8.x is for SDK Tools r8.</li>
+  <li>Support for true debug build. You no longer need to change the value of the
+   <code>debuggable</code> attribute in the Android Manifest.
+  <p>Incremental builds automatically insert <code>debuggable="true"</code>, but if you perform
+  "export signed/unsigned application package", ADT does <em>not</em> insert it.
+  If you manually set <code>debuggable="true"</code> in the manifest file, then release builds will
+  actually create a debug build (it does not remove it if you placed it there).</p></li>
+  <li>Automatic <a href="{@docRoot}guide/developing/tools/proguard.html">Proguard</a> support in
+release builds. For it to work, you need to have a <code>proguard.config</code>
+  property in the <code>default.properties</code> file that points to a proguard config file.</li>
+  <li>Completely rewritten Visual Layout Editor. (This is still a work in progress.) Now includes:
+    <ul>
+      <li>Full drag and drop from palette to layout for all Layout classes.</li>
+      <li>Move widgets inside a Layout view, from one Layout view to another and from one layout file to another.</li>
+      <li>Contextual menu with enum/flag type properties.</li>
+      <li>New zoom controls.</li>
+    </ul></li>
+  <li>New HierarchyViewer plug-in integrated in Eclipse.</li>
+  <li>Android launch configurations don't recompile the whole workspace on launch anymore.</li>
+  <li><code>android.jar</code> source and javadoc location can now be configured.</li>
 </ul>
 </dd>
+</dl>
+ </div>
+</div>
+
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
+ADT 0.9.9</a> <em>(September 2010)</em>
+  <div class="toggleme">
 
 <dl>
 
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index 11e6642..0f36345 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -84,34 +84,56 @@
 
         <dd>
           <ul>
+          
+            <li>A new toolchain (based on GCC 4.4.3), which generates better code, and can also now
+be used as a standalone cross-compiler, for people who want to build their stuff with
+<code>./configure &amp;&amp; make</code>. See
+docs/STANDALONE-TOOLCHAIN.html for the details. The binaries for GCC 4.4.0 are still provided,
+but the 4.2.1 binaries were removed.</li>
+
+            <li>Support for prebuilt static and shared libraries (docs/PREBUILTS.html), module
+exports and imports to make sharing and reuse of third-party modules much easier
+(docs/IMPORT-MODULE.html explains why).</li>
+
+            <li>A C++ STL implementation (based on STLport) is now provided as a helper module. It
+can be used either as a static or shared library (details and usage exemple under
+sources/android/stlport/README). <strong>Note:</strong> For now, C++ Exceptions and RTTI are still
+not supported.</li>
+
+            <li>Improvements to the <code>cpufeatures</code> helper library to deal with buggy
+kernel that incorrectly report they run on an ARMv7 CPU (while the device really is an ARMv6). We
+recommend developers that use it to simply rebuild their applications to benefit from it, then
+upload to Market.</li>
+
             <li>Adds support for native activities, which allows you to write completely native
             applications.</li>
 
             <li>Adds an EGL library that lets you create and manage OpenGL ES textures and
             services.</li>
 
-            <li>Provides an interface that lets you write a native text-to-speech engine.</li>
-
             <li>Adds native support for the following:
 
               <ul>
-                <li>the input subsystem (such as the keyboard and touch screen)</li>
+                <li>Input subsystem (such as the keyboard and touch screen)</li>
 
-                <li>the window and surface subsystem.</li>
+                <li>Window and surface subsystem</li>
 
-                <li>audio APIs based on the OpenSL ES standard that support playback and recording
-                as well as control over platform audio effects.</li>
+                <li>Audio APIs based on the OpenSL ES standard that support playback and recording
+                as well as control over platform audio effects</li>
 
-                <li>event loop APIs to wait for things such as input and sensor events.</li>
+                <li>Event loop APIs to wait for things such as input and sensor events</li>
 
-                <li>accessing assets packaged in an <code>.apk</code> file.</li>
+                <li>Access to assets packaged in the <code>.apk</code></li>
 
-                <li>accessing sensor data (accelerometer, compass, gyroscope, etc).</li>
-
-                <li>provides sample applications, <code>native-plasma</code> and
-                <code>native-activity</code>, to demonstrate how to write a native activity.</li>
+                <li>Access to sensor data (accelerometer, compass, gyroscope, etc.)</li>
               </ul>
             </li>
+
+            <li>New sample applications, <code>native-plasma</code> and
+              <code>native-activity</code>, to demonstrate how to write a native activity.</li>
+            
+            <li>Plus many bugfixes and other small improvements; see docs/CHANGES.html for a more
+detailed list of changes.</li>
           </ul>
         </dd>
       </dl>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index ecc69c2..7ccb7a0 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -93,7 +93,7 @@
       <span style="display:none" class="zh-TW"></span>
       </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 0.9.9
+      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 8.0
       <span style="display:none" class="de"></span>
       <span style="display:none" class="es"></span>
       <span style="display:none" class="fr"></span>
@@ -115,7 +115,8 @@
       <span style="display:none" class="zh-TW"></span>
     </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Download the Android NDK, r5</a></li>
+      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Download the Android NDK, r5</a>
+        <span class="new">new!</span></li>
       <li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li>
     </ul>
   </li>
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index dfd6ac8..9dc291b 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -45,9 +45,8 @@
 
     @Override
     void updateFromNative() {
-        mRS.validate();
-        mName = mRS.nGetName(mID);
-        int typeID = mRS.nAllocationGetType(mID);
+        super.updateFromNative();
+        int typeID = mRS.nAllocationGetType(getID());
         if(typeID != 0) {
             mType = new Type(typeID, mRS);
             mType.updateFromNative();
@@ -60,17 +59,17 @@
 
     public void uploadToTexture(int baseMipLevel) {
         mRS.validate();
-        mRS.nAllocationUploadToTexture(mID, false, baseMipLevel);
+        mRS.nAllocationUploadToTexture(getID(), false, baseMipLevel);
     }
 
     public void uploadToTexture(boolean genMips, int baseMipLevel) {
         mRS.validate();
-        mRS.nAllocationUploadToTexture(mID, genMips, baseMipLevel);
+        mRS.nAllocationUploadToTexture(getID(), genMips, baseMipLevel);
     }
 
     public void uploadToBufferObject() {
         mRS.validate();
-        mRS.nAllocationUploadToBufferObject(mID);
+        mRS.nAllocationUploadToBufferObject(getID());
     }
 
     public void data(int[] d) {
@@ -90,16 +89,15 @@
         subData1D(0, mType.getElementCount(), d);
     }
 
-    public void updateFromBitmap(Bitmap b)
-        throws IllegalArgumentException {
+    public void updateFromBitmap(Bitmap b) {
 
         mRS.validate();
         if(mType.getX() != b.getWidth() ||
            mType.getY() != b.getHeight()) {
-            throw new IllegalArgumentException("Cannot update allocation from bitmap, sizes mismatch");
+            throw new RSIllegalArgumentException("Cannot update allocation from bitmap, sizes mismatch");
         }
 
-        mRS.nAllocationUpdateFromBitmap(mID, b);
+        mRS.nAllocationUpdateFromBitmap(getID(), b);
     }
 
     public void subData(int xoff, FieldPacker fp) {
@@ -108,100 +106,100 @@
 
         int count = data.length / eSize;
         if ((eSize * count) != data.length) {
-            throw new IllegalArgumentException("Field packer length " + data.length +
+            throw new RSIllegalArgumentException("Field packer length " + data.length +
                                                " not divisible by element size " + eSize + ".");
         }
         data1DChecks(xoff, count, data.length, data.length);
-        mRS.nAllocationSubData1D(mID, xoff, count, data, data.length);
+        mRS.nAllocationSubData1D(getID(), xoff, count, data, data.length);
     }
 
 
     public void subElementData(int xoff, int component_number, FieldPacker fp) {
         if (component_number >= mType.mElement.mElements.length) {
-            throw new IllegalArgumentException("Component_number " + component_number + " out of range.");
+            throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
         }
         if(xoff < 0) {
-            throw new IllegalArgumentException("Offset must be >= 0.");
+            throw new RSIllegalArgumentException("Offset must be >= 0.");
         }
 
         final byte[] data = fp.getData();
         int eSize = mType.mElement.mElements[component_number].getSizeBytes();
 
         if (data.length != eSize) {
-            throw new IllegalArgumentException("Field packer sizelength " + data.length +
+            throw new RSIllegalArgumentException("Field packer sizelength " + data.length +
                                                " does not match component size " + eSize + ".");
         }
 
-        mRS.nAllocationSubElementData1D(mID, xoff, component_number, data, data.length);
+        mRS.nAllocationSubElementData1D(getID(), xoff, component_number, data, data.length);
     }
 
     private void data1DChecks(int off, int count, int len, int dataSize) {
         mRS.validate();
         if(off < 0) {
-            throw new IllegalArgumentException("Offset must be >= 0.");
+            throw new RSIllegalArgumentException("Offset must be >= 0.");
         }
         if(count < 1) {
-            throw new IllegalArgumentException("Count must be >= 1.");
+            throw new RSIllegalArgumentException("Count must be >= 1.");
         }
         if((off + count) > mType.getElementCount()) {
-            throw new IllegalArgumentException("Overflow, Available count " + mType.getElementCount() +
+            throw new RSIllegalArgumentException("Overflow, Available count " + mType.getElementCount() +
                                                ", got " + count + " at offset " + off + ".");
         }
         if((len) < dataSize) {
-            throw new IllegalArgumentException("Array too small for allocation type.");
+            throw new RSIllegalArgumentException("Array too small for allocation type.");
         }
     }
 
     public void subData1D(int off, int count, int[] d) {
         int dataSize = mType.mElement.getSizeBytes() * count;
         data1DChecks(off, count, d.length * 4, dataSize);
-        mRS.nAllocationSubData1D(mID, off, count, d, dataSize);
+        mRS.nAllocationSubData1D(getID(), off, count, d, dataSize);
     }
     public void subData1D(int off, int count, short[] d) {
         int dataSize = mType.mElement.getSizeBytes() * count;
         data1DChecks(off, count, d.length * 2, dataSize);
-        mRS.nAllocationSubData1D(mID, off, count, d, dataSize);
+        mRS.nAllocationSubData1D(getID(), off, count, d, dataSize);
     }
     public void subData1D(int off, int count, byte[] d) {
         int dataSize = mType.mElement.getSizeBytes() * count;
         data1DChecks(off, count, d.length, dataSize);
-        mRS.nAllocationSubData1D(mID, off, count, d, dataSize);
+        mRS.nAllocationSubData1D(getID(), off, count, d, dataSize);
     }
     public void subData1D(int off, int count, float[] d) {
         int dataSize = mType.mElement.getSizeBytes() * count;
         data1DChecks(off, count, d.length * 4, dataSize);
-        mRS.nAllocationSubData1D(mID, off, count, d, dataSize);
+        mRS.nAllocationSubData1D(getID(), off, count, d, dataSize);
     }
 
 
     public void subData2D(int xoff, int yoff, int w, int h, int[] d) {
         mRS.validate();
-        mRS.nAllocationSubData2D(mID, xoff, yoff, w, h, d, d.length * 4);
+        mRS.nAllocationSubData2D(getID(), xoff, yoff, w, h, d, d.length * 4);
     }
 
     public void subData2D(int xoff, int yoff, int w, int h, float[] d) {
         mRS.validate();
-        mRS.nAllocationSubData2D(mID, xoff, yoff, w, h, d, d.length * 4);
+        mRS.nAllocationSubData2D(getID(), xoff, yoff, w, h, d, d.length * 4);
     }
 
     public void readData(int[] d) {
         mRS.validate();
-        mRS.nAllocationRead(mID, d);
+        mRS.nAllocationRead(getID(), d);
     }
 
     public void readData(float[] d) {
         mRS.validate();
-        mRS.nAllocationRead(mID, d);
+        mRS.nAllocationRead(getID(), d);
     }
 
     public synchronized void resize(int dimX) {
         if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.getFaces() || mType.getLOD()) {
-            throw new IllegalStateException("Resize only support for 1D allocations at this time.");
+            throw new RSInvalidStateException("Resize only support for 1D allocations at this time.");
         }
-        mRS.nAllocationResize1D(mID, dimX);
+        mRS.nAllocationResize1D(getID(), dimX);
         mRS.finish();  // Necessary because resize is fifoed and update is async.
 
-        int typeID = mRS.nAllocationGetType(mID);
+        int typeID = mRS.nAllocationGetType(getID());
         mType = new Type(typeID, mRS);
         mType.updateFromNative();
     }
@@ -209,12 +207,12 @@
     /*
     public void resize(int dimX, int dimY) {
         if ((mType.getZ() > 0) || mType.getFaces() || mType.getLOD()) {
-            throw new IllegalStateException("Resize only support for 2D allocations at this time.");
+            throw new RSIllegalStateException("Resize only support for 2D allocations at this time.");
         }
         if (mType.getY() == 0) {
-            throw new IllegalStateException("Resize only support for 2D allocations at this time.");
+            throw new RSIllegalStateException("Resize only support for 2D allocations at this time.");
         }
-        mRS.nAllocationResize2D(mID, dimX, dimY);
+        mRS.nAllocationResize2D(getID(), dimX, dimY);
     }
     */
 
@@ -225,27 +223,27 @@
 
         public void setConstraint(Dimension dim, int value) {
             mRS.validate();
-            mRS.nAdapter1DSetConstraint(mID, dim.mID, value);
+            mRS.nAdapter1DSetConstraint(getID(), dim.mID, value);
         }
 
         public void data(int[] d) {
             mRS.validate();
-            mRS.nAdapter1DData(mID, d);
+            mRS.nAdapter1DData(getID(), d);
         }
 
         public void data(float[] d) {
             mRS.validate();
-            mRS.nAdapter1DData(mID, d);
+            mRS.nAdapter1DData(getID(), d);
         }
 
         public void subData(int off, int count, int[] d) {
             mRS.validate();
-            mRS.nAdapter1DSubData(mID, off, count, d);
+            mRS.nAdapter1DSubData(getID(), off, count, d);
         }
 
         public void subData(int off, int count, float[] d) {
             mRS.validate();
-            mRS.nAdapter1DSubData(mID, off, count, d);
+            mRS.nAdapter1DSubData(getID(), off, count, d);
         }
     }
 
@@ -253,9 +251,9 @@
         mRS.validate();
         int id = mRS.nAdapter1DCreate();
         if(id == 0) {
-            throw new IllegalStateException("allocation failed.");
+            throw new RSRuntimeException("Adapter creation failed.");
         }
-        mRS.nAdapter1DBindAllocation(id, mID);
+        mRS.nAdapter1DBindAllocation(id, getID());
         return new Adapter1D(id, mRS);
     }
 
@@ -267,27 +265,27 @@
 
         public void setConstraint(Dimension dim, int value) {
             mRS.validate();
-            mRS.nAdapter2DSetConstraint(mID, dim.mID, value);
+            mRS.nAdapter2DSetConstraint(getID(), dim.mID, value);
         }
 
         public void data(int[] d) {
             mRS.validate();
-            mRS.nAdapter2DData(mID, d);
+            mRS.nAdapter2DData(getID(), d);
         }
 
         public void data(float[] d) {
             mRS.validate();
-            mRS.nAdapter2DData(mID, d);
+            mRS.nAdapter2DData(getID(), d);
         }
 
         public void subData(int xoff, int yoff, int w, int h, int[] d) {
             mRS.validate();
-            mRS.nAdapter2DSubData(mID, xoff, yoff, w, h, d);
+            mRS.nAdapter2DSubData(getID(), xoff, yoff, w, h, d);
         }
 
         public void subData(int xoff, int yoff, int w, int h, float[] d) {
             mRS.validate();
-            mRS.nAdapter2DSubData(mID, xoff, yoff, w, h, d);
+            mRS.nAdapter2DSubData(getID(), xoff, yoff, w, h, d);
         }
     }
 
@@ -295,9 +293,12 @@
         mRS.validate();
         int id = mRS.nAdapter2DCreate();
         if(id == 0) {
-            throw new IllegalStateException("allocation failed.");
+            throw new RSRuntimeException("allocation failed.");
         }
-        mRS.nAdapter2DBindAllocation(id, mID);
+        mRS.nAdapter2DBindAllocation(id, getID());
+        if(id == 0) {
+            throw new RSRuntimeException("Adapter creation failed.");
+        }
         return new Adapter2D(id, mRS);
     }
 
@@ -309,14 +310,16 @@
         mBitmapOptions.inScaled = false;
     }
 
-    static public Allocation createTyped(RenderScript rs, Type type)
-        throws IllegalArgumentException {
+    static public Allocation createTyped(RenderScript rs, Type type) {
 
         rs.validate();
-        if(type.mID == 0) {
-            throw new IllegalStateException("Bad Type");
+        if(type.getID() == 0) {
+            throw new RSInvalidStateException("Bad Type");
         }
-        int id = rs.nAllocationCreateTyped(type.mID);
+        int id = rs.nAllocationCreateTyped(type.getID());
+        if(id == 0) {
+            throw new RSRuntimeException("Allocation creation failed.");
+        }
         return new Allocation(id, rs, type);
     }
 
@@ -328,9 +331,9 @@
         b.add(Dimension.X, count);
         Type t = b.create();
 
-        int id = rs.nAllocationCreateTyped(t.mID);
+        int id = rs.nAllocationCreateTyped(t.getID());
         if(id == 0) {
-            throw new IllegalStateException("Bad element.");
+            throw new RSRuntimeException("Allocation creation failed.");
         }
         return new Allocation(id, rs, t);
     }
@@ -349,7 +352,7 @@
         if (bc == Bitmap.Config.RGB_565) {
             return Element.RGB_565(rs);
         }
-        throw new IllegalStateException("Bad bitmap type.");
+        throw new RSInvalidStateException("Bad bitmap type.");
     }
 
     static private Type typeFromBitmap(RenderScript rs, Bitmap b) {
@@ -360,28 +363,26 @@
         return tb.create();
     }
 
-    static public Allocation createFromBitmap(RenderScript rs, Bitmap b, Element dstFmt, boolean genMips)
-        throws IllegalArgumentException {
+    static public Allocation createFromBitmap(RenderScript rs, Bitmap b, Element dstFmt, boolean genMips) {
 
         rs.validate();
         Type t = typeFromBitmap(rs, b);
 
-        int id = rs.nAllocationCreateFromBitmap(dstFmt.mID, genMips, b);
+        int id = rs.nAllocationCreateFromBitmap(dstFmt.getID(), genMips, b);
         if(id == 0) {
-            throw new IllegalStateException("Load failed.");
+            throw new RSRuntimeException("Load failed.");
         }
         return new Allocation(id, rs, t);
     }
 
-    static public Allocation createBitmapRef(RenderScript rs, Bitmap b)
-        throws IllegalArgumentException {
+    static public Allocation createBitmapRef(RenderScript rs, Bitmap b) {
 
         rs.validate();
         Type t = typeFromBitmap(rs, b);
 
         int id = rs.nAllocationCreateBitmapRef(t.getID(), b);
         if(id == 0) {
-            throw new IllegalStateException("Load failed.");
+            throw new RSRuntimeException("Load failed.");
         }
 
         Allocation a = new Allocation(id, rs, t);
@@ -389,8 +390,7 @@
         return a;
     }
 
-    static public Allocation createFromBitmapResource(RenderScript rs, Resources res, int id, Element dstFmt, boolean genMips)
-        throws IllegalArgumentException {
+    static public Allocation createFromBitmapResource(RenderScript rs, Resources res, int id, Element dstFmt, boolean genMips) {
 
         rs.validate();
         InputStream is = null;
@@ -399,15 +399,12 @@
             is = res.openRawResource(id, value);
 
             int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
-            int allocationId = rs.nAllocationCreateFromAssetStream(dstFmt.mID, genMips,
-                    asset);
+            int aId = rs.nAllocationCreateFromAssetStream(dstFmt.getID(), genMips, asset);
 
-            if(allocationId == 0) {
-                throw new IllegalStateException("Load failed.");
+            if (aId == 0) {
+                throw new RSRuntimeException("Load failed.");
             }
-            return new Allocation(allocationId, rs, null);
-        } catch (Exception e) {
-            // Ignore
+            return new Allocation(aId, rs, null);
         } finally {
             if (is != null) {
                 try {
@@ -417,12 +414,9 @@
                 }
             }
         }
-
-        return null;
     }
 
-    static public Allocation createFromString(RenderScript rs, String str)
-        throws IllegalArgumentException {
+    static public Allocation createFromString(RenderScript rs, String str) {
         byte[] allocArray = null;
         try {
             allocArray = str.getBytes("UTF-8");
@@ -431,9 +425,8 @@
             return alloc;
         }
         catch (Exception e) {
-            Log.e("rs", "could not convert string to utf-8");
+            throw new RSRuntimeException("Could not convert string to utf-8.");
         }
-        return null;
     }
 }
 
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index 69907d9..026f7de 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -21,9 +21,12 @@
 /**
  * @hide
  *
+ * BaseObj is the base class for interfacing with native renderscript objects.
+ * It primarly contains code for tracking the native object ID and forcably
+ * disconecting the object from the native allocation for early cleanup.
+ *
  **/
 class BaseObj {
-
     BaseObj(int id, RenderScript rs) {
         rs.validate();
         mRS = rs;
@@ -31,6 +34,13 @@
         mDestroyed = false;
     }
 
+    void setID(int id) {
+        if (mID != 0) {
+            throw new RSRuntimeException("Internal Error, reset of object ID.");
+        }
+        mID = id;
+    }
+
     public int getID() {
         if (mDestroyed) {
             throw new RSInvalidStateException("using a destroyed object.");
@@ -38,9 +48,9 @@
         return mID;
     }
 
-    int mID;
-    boolean mDestroyed;
-    String mName;
+    private int mID;
+    private boolean mDestroyed;
+    private String mName;
     RenderScript mRS;
 
     public void setName(String s) {
@@ -74,7 +84,7 @@
         super.finalize();
     }
 
-    public void destroy() {
+    synchronized public void destroy() {
         if(mDestroyed) {
             throw new RSInvalidStateException("Object already destroyed.");
         }
@@ -85,6 +95,8 @@
     // If an object came from an a3d file, java fields need to be
     // created with objects from the native layer
     void updateFromNative() {
+        mRS.validate();
+        mName = mRS.nGetName(getID());
     }
 
 }
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index f844b7e..8907cd2 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -373,10 +373,11 @@
 
     @Override
     void updateFromNative() {
+        super.updateFromNative();
 
         // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
         int[] dataBuffer = new int[5];
-        mRS.nElementGetNativeData(mID, dataBuffer);
+        mRS.nElementGetNativeData(getID(), dataBuffer);
 
         mNormalized = dataBuffer[2] == 1 ? true : false;
         mVectorSize = dataBuffer[3];
@@ -399,7 +400,7 @@
             mElementNames = new String[numSubElements];
 
             int[] subElementIds = new int[numSubElements];
-            mRS.nElementGetSubElements(mID, subElementIds, mElementNames);
+            mRS.nElementGetSubElements(getID(), subElementIds, mElementNames);
             for(int i = 0; i < numSubElements; i ++) {
                 mElements[i] = new Element(subElementIds[i], mRS);
                 mElements[i].updateFromNative();
@@ -523,7 +524,7 @@
 
             int[] ids = new int[ein.length];
             for (int ct = 0; ct < ein.length; ct++ ) {
-                ids[ct] = ein[ct].mID;
+                ids[ct] = ein[ct].getID();
             }
             int id = mRS.nElementCreate2(ids, sin, asin);
             return new Element(id, mRS, ein, sin, asin);
diff --git a/graphics/java/android/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java
index fc74fc4..af85d8e 100644
--- a/graphics/java/android/renderscript/FileA3D.java
+++ b/graphics/java/android/renderscript/FileA3D.java
@@ -149,7 +149,7 @@
     }
 
     private void initEntries() {
-        int numFileEntries = mRS.nFileA3DGetNumIndexEntries(mID);
+        int numFileEntries = mRS.nFileA3DGetNumIndexEntries(getID());
         if(numFileEntries <= 0) {
             return;
         }
@@ -158,10 +158,10 @@
         int[] ids = new int[numFileEntries];
         String[] names = new String[numFileEntries];
 
-        mRS.nFileA3DGetIndexEntries(mID, numFileEntries, ids, names);
+        mRS.nFileA3DGetIndexEntries(getID(), numFileEntries, ids, names);
 
         for(int i = 0; i < numFileEntries; i ++) {
-            mFileEntries[i] = new IndexEntry(mRS, i, mID, names[i], ClassID.toClassID(ids[i]));
+            mFileEntries[i] = new IndexEntry(mRS, i, getID(), names[i], ClassID.toClassID(ids[i]));
         }
     }
 
diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java
index bb382f2..4187992 100644
--- a/graphics/java/android/renderscript/Mesh.java
+++ b/graphics/java/android/renderscript/Mesh.java
@@ -60,16 +60,16 @@
 
     @Override
     void updateFromNative() {
-        mName = mRS.nGetName(mID);
-        int vtxCount = mRS.nMeshGetVertexBufferCount(mID);
-        int idxCount = mRS.nMeshGetIndexCount(mID);
+        super.updateFromNative();
+        int vtxCount = mRS.nMeshGetVertexBufferCount(getID());
+        int idxCount = mRS.nMeshGetIndexCount(getID());
 
         int[] vtxIDs = new int[vtxCount];
         int[] idxIDs = new int[idxCount];
         int[] primitives = new int[idxCount];
 
-        mRS.nMeshGetVertices(mID, vtxIDs, vtxCount);
-        mRS.nMeshGetIndices(mID, idxIDs, primitives, idxCount);
+        mRS.nMeshGetVertices(getID(), vtxIDs, vtxCount);
+        mRS.nMeshGetIndices(getID(), idxIDs, primitives, idxCount);
 
         mVertexBuffers = new Allocation[vtxCount];
         mIndexBuffers = new Allocation[idxCount];
@@ -292,7 +292,7 @@
 
             for(int ct = 0; ct < b.mVertexTypeCount; ct ++) {
                 Entry entry = b.mVertexTypes[ct];
-                rs.nMeshBindVertex(id, entry.a.mID, ct);
+                rs.nMeshBindVertex(id, entry.a.getID(), ct);
                 newMesh.mVertexBuffers[ct] = entry.a;
             }
             rs.nMeshInitVertexAttribs(id);
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index 35236ca..83c3601 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -54,7 +54,7 @@
             a.getType().getID() != mConstants[slot].getID()) {
             throw new IllegalArgumentException("Allocation type does not match slot type.");
         }
-        mRS.nProgramBindConstants(mID, slot, a.mID);
+        mRS.nProgramBindConstants(getID(), slot, a.getID());
     }
 
     public void bindTexture(Allocation va, int slot)
@@ -64,7 +64,7 @@
             throw new IllegalArgumentException("Slot ID out of range.");
         }
 
-        mRS.nProgramBindTexture(mID, slot, va.mID);
+        mRS.nProgramBindTexture(getID(), slot, va.getID());
     }
 
     public void bindSampler(Sampler vs, int slot)
@@ -74,7 +74,7 @@
             throw new IllegalArgumentException("Slot ID out of range.");
         }
 
-        mRS.nProgramBindSampler(mID, slot, vs.mID);
+        mRS.nProgramBindSampler(getID(), slot, vs.getID());
     }
 
 
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index 8858b74..d30e483 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -42,15 +42,15 @@
 
             for (int i=0; i < mInputCount; i++) {
                 tmp[idx++] = 0;
-                tmp[idx++] = mInputs[i].mID;
+                tmp[idx++] = mInputs[i].getID();
             }
             for (int i=0; i < mOutputCount; i++) {
                 tmp[idx++] = 1;
-                tmp[idx++] = mOutputs[i].mID;
+                tmp[idx++] = mOutputs[i].getID();
             }
             for (int i=0; i < mConstantCount; i++) {
                 tmp[idx++] = 2;
-                tmp[idx++] = mConstants[i].mID;
+                tmp[idx++] = mConstants[i].getID();
             }
             tmp[idx++] = 3;
             tmp[idx++] = mTextureCount;
diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java
index 791dac8..5b55015 100644
--- a/graphics/java/android/renderscript/ProgramRaster.java
+++ b/graphics/java/android/renderscript/ProgramRaster.java
@@ -58,13 +58,13 @@
     void setLineWidth(float w) {
         mRS.validate();
         mLineWidth = w;
-        mRS.nProgramRasterSetLineWidth(mID, w);
+        mRS.nProgramRasterSetLineWidth(getID(), w);
     }
 
     void setCullMode(CullMode m) {
         mRS.validate();
         mCullMode = m;
-        mRS.nProgramRasterSetCullMode(mID, m.mID);
+        mRS.nProgramRasterSetCullMode(getID(), m.mID);
     }
 
     public static ProgramRaster CULL_BACK(RenderScript rs) {
diff --git a/graphics/java/android/renderscript/ProgramStore.java b/graphics/java/android/renderscript/ProgramStore.java
index 32c0d01..d191b06 100644
--- a/graphics/java/android/renderscript/ProgramStore.java
+++ b/graphics/java/android/renderscript/ProgramStore.java
@@ -288,15 +288,7 @@
         }
 
         static synchronized ProgramStore internalCreate(RenderScript rs, Builder b) {
-            int inID = 0;
-            int outID = 0;
-            if (b.mIn != null) {
-                inID = b.mIn.mID;
-            }
-            if (b.mOut != null) {
-                outID = b.mOut.mID;
-            }
-            rs.nProgramStoreBegin(inID, outID);
+            rs.nProgramStoreBegin(0, 0);
             rs.nProgramStoreDepthFunc(b.mDepthFunc.mID);
             rs.nProgramStoreDepthMask(b.mDepthMask);
             rs.nProgramStoreColorMask(b.mColorMaskR,
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 65a0af2..13f017a 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -51,15 +51,15 @@
 
             for (int i=0; i < mInputCount; i++) {
                 tmp[idx++] = 0;
-                tmp[idx++] = mInputs[i].mID;
+                tmp[idx++] = mInputs[i].getID();
             }
             for (int i=0; i < mOutputCount; i++) {
                 tmp[idx++] = 1;
-                tmp[idx++] = mOutputs[i].mID;
+                tmp[idx++] = mOutputs[i].getID();
             }
             for (int i=0; i < mConstantCount; i++) {
                 tmp[idx++] = 2;
-                tmp[idx++] = mConstants[i].mID;
+                tmp[idx++] = mConstants[i].getID();
             }
             tmp[idx++] = 3;
             tmp[idx++] = mTextureCount;
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index b3774d4..64afb6f 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -738,7 +738,7 @@
 
     protected int safeID(BaseObj o) {
         if(o != null) {
-            return o.mID;
+            return o.getID();
         }
         return 0;
     }
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index 430789a..7d7dd6d 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -38,19 +38,19 @@
         }
 
         public void execute() {
-            mRS.nScriptInvoke(mScript.mID, mSlot);
+            mRS.nScriptInvoke(mScript.getID(), mSlot);
         }
     }
 
     protected void invoke(int slot) {
-        mRS.nScriptInvoke(mID, slot);
+        mRS.nScriptInvoke(getID(), slot);
     }
 
     protected void invoke(int slot, FieldPacker v) {
         if (v != null) {
-            mRS.nScriptInvokeV(mID, slot, v.getData());
+            mRS.nScriptInvokeV(getID(), slot, v.getData());
         } else {
-            mRS.nScriptInvoke(mID, slot);
+            mRS.nScriptInvoke(getID(), slot);
         }
     }
 
@@ -62,40 +62,40 @@
     public void bindAllocation(Allocation va, int slot) {
         mRS.validate();
         if (va != null) {
-            mRS.nScriptBindAllocation(mID, va.mID, slot);
+            mRS.nScriptBindAllocation(getID(), va.getID(), slot);
         } else {
-            mRS.nScriptBindAllocation(mID, 0, slot);
+            mRS.nScriptBindAllocation(getID(), 0, slot);
         }
     }
 
     public void setVar(int index, float v) {
-        mRS.nScriptSetVarF(mID, index, v);
+        mRS.nScriptSetVarF(getID(), index, v);
     }
 
     public void setVar(int index, double v) {
-        mRS.nScriptSetVarD(mID, index, v);
+        mRS.nScriptSetVarD(getID(), index, v);
     }
 
     public void setVar(int index, int v) {
-        mRS.nScriptSetVarI(mID, index, v);
+        mRS.nScriptSetVarI(getID(), index, v);
     }
 
     public void setVar(int index, long v) {
-        mRS.nScriptSetVarJ(mID, index, v);
+        mRS.nScriptSetVarJ(getID(), index, v);
     }
 
     public void setVar(int index, boolean v) {
-        mRS.nScriptSetVarI(mID, index, v ? 1 : 0);
+        mRS.nScriptSetVarI(getID(), index, v ? 1 : 0);
     }
 
     public void setVar(int index, FieldPacker v) {
-        mRS.nScriptSetVarV(mID, index, v.getData());
+        mRS.nScriptSetVarV(getID(), index, v.getData());
     }
 
     public void setTimeZone(String timeZone) {
         mRS.validate();
         try {
-            mRS.nScriptSetTimeZone(mID, timeZone.getBytes("UTF-8"));
+            mRS.nScriptSetTimeZone(getID(), timeZone.getBytes("UTF-8"));
         } catch (java.io.UnsupportedEncodingException e) {
             throw new RuntimeException(e);
         }
diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java
index 5215795..44fc5fd 100644
--- a/graphics/java/android/renderscript/ScriptC.java
+++ b/graphics/java/android/renderscript/ScriptC.java
@@ -39,7 +39,8 @@
 
     protected ScriptC(RenderScript rs, Resources resources, int resourceID) {
         super(0, rs);
-        mID = internalCreate(rs, resources, resourceID);
+        int id = internalCreate(rs, resources, resourceID);
+        setID(id);
     }
 
 
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 0d65737..ad933b8 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -106,7 +106,7 @@
         // We have 6 integer to obtain mDimX; mDimY; mDimZ;
         // mDimLOD; mDimFaces; mElement;
         int[] dataBuffer = new int[6];
-        mRS.nTypeGetNativeData(mID, dataBuffer);
+        mRS.nTypeGetNativeData(getID(), dataBuffer);
 
         mDimX = dataBuffer[0];
         mDimY = dataBuffer[1];
@@ -135,7 +135,7 @@
         }
 
         public Builder(RenderScript rs, Element e) {
-            if(e.mID == 0) {
+            if(e.getID() == 0) {
                 throw new RSIllegalArgumentException("Invalid element.");
             }
 
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 32b6fa1..a710546 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -44,7 +44,8 @@
     AUDIO_SOURCE_VOICE_CALL = 4,
     AUDIO_SOURCE_CAMCORDER = 5,
     AUDIO_SOURCE_VOICE_RECOGNITION = 6,
-    AUDIO_SOURCE_MAX = AUDIO_SOURCE_VOICE_RECOGNITION,
+    AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
+    AUDIO_SOURCE_MAX = AUDIO_SOURCE_VOICE_COMMUNICATION,
 
     AUDIO_SOURCE_LIST_END  // must be last - used to validate audio source type
 };
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index bb469e5..7bf07eb 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -154,6 +154,7 @@
     bool exceedsFileDurationLimit();
     bool isFileStreamable() const;
     void trackProgressStatus(const Track* track, int64_t timeUs, status_t err = OK);
+    void writeCompositionMatrix(int32_t degrees);
 
     MPEG4Writer(const MPEG4Writer &);
     MPEG4Writer &operator=(const MPEG4Writer &);
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 770e596..d08df44 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -127,7 +127,9 @@
     }
 
     mPathHeap = recorder.mPathHeap;
-    mPathHeap->safeRef();
+    if (mPathHeap) {
+        mPathHeap->safeRef();
+    }
 }
 
 DisplayList::~DisplayList() {
@@ -155,7 +157,12 @@
     }
     mMatrices.clear();
 
-    mPathHeap->safeUnref();
+    if (mPathHeap) {
+        for (int i = 0; i < mPathHeap->count(); i++) {
+            caches.pathCache.remove(&(*mPathHeap)[i]);
+        }
+        mPathHeap->safeUnref();
+    }
 }
 
 void DisplayList::init() {
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index b58785a..04d07db 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -92,10 +92,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void PathCache::operator()(PathCacheEntry& path, PathTexture*& texture) {
-    const uint32_t size = texture->width * texture->height;
-    mSize -= size;
-
     if (texture) {
+        const uint32_t size = texture->width * texture->height;
+        mSize -= size;
+
+        PATH_LOGD("PathCache::callback: delete path: name, size, mSize = %d, %d, %d",
+                texture->id, size, mSize);
+
         glDeleteTextures(1, &texture->id);
         delete texture;
     }
@@ -107,12 +110,18 @@
 
 void PathCache::remove(SkPath* path) {
     Mutex::Autolock _l(mLock);
+
     // TODO: Linear search...
+    Vector<uint32_t> pathsToRemove;
     for (uint32_t i = 0; i < mCache.size(); i++) {
         if (mCache.getKeyAt(i).path == path) {
-            mCache.removeAt(i);
+            pathsToRemove.push(i);
         }
     }
+
+    for (size_t i = 0; i < pathsToRemove.size(); i++) {
+        mCache.removeAt(pathsToRemove.itemAt(i));
+    }
 }
 
 PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
@@ -188,6 +197,8 @@
     if (size < mMaxSize) {
         mLock.lock();
         mSize += size;
+        PATH_LOGD("PathCache::get: create path: name, size, mSize = %d, %d, %d",
+                texture->id, size, mSize);
         mCache.put(entry, texture);
         mLock.unlock();
     } else {
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index db5ce08..aea71cb 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -28,6 +28,24 @@
 namespace android {
 namespace uirenderer {
 
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_PATHS 0
+
+// Debug
+#if DEBUG_PATHS
+    #define PATH_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define PATH_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
 /**
  * Describe a path in the path cache.
  */
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 9d54277..2f7c7be 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -74,10 +74,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) {
-    const uint32_t size = texture->width * texture->height;
-    mSize -= size;
-
     if (texture) {
+        const uint32_t size = texture->width * texture->height;
+        mSize -= size;
+
         glDeleteTextures(1, &texture->id);
         delete texture;
     }
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 0c7948f..2497925 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -94,8 +94,8 @@
     // This will be called already locked
     if (texture) {
         mSize -= texture->bitmapSize;
-        TEXTURE_LOGD("TextureCache::callback: removed size, mSize = %d, %d",
-                texture->bitmapSize, mSize);
+        TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d",
+                texture->id, texture->bitmapSize, mSize);
         glDeleteTextures(1, &texture->id);
         delete texture;
     }
@@ -133,8 +133,8 @@
         if (size < mMaxSize) {
             mLock.lock();
             mSize += size;
-            TEXTURE_LOGD("TextureCache::get: create texture(0x%p): size, mSize = %d, %d",
-                     bitmap, size, mSize);
+            TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
+                     bitmap, texture->id, size, mSize);
             mCache.put(bitmap, texture);
             mLock.unlock();
         } else {
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index d9d2387..5c314fc 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -39,6 +39,10 @@
     #define TEXTURE_LOGD(...)
 #endif
 
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
 /**
  * A simple LRU texture cache. The cache has a maximum size expressed in bytes.
  * Any texture added to the cache causing the cache to grow beyond the maximum
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
index eb46e950..0afff34 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
@@ -53,7 +53,7 @@
 
     boolean holdingColor[] = new boolean[10];
     public void newTouchPosition(float x, float y, float pressure, int id) {
-        if (id > holdingColor.length) {
+        if (id >= holdingColor.length) {
             return;
         }
         int rate = (int)(pressure * pressure * 500.f);
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index b74fa8e..28078fc 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -869,6 +869,8 @@
         }
     } else {
         rsc->setError(RS_ERROR_BAD_VALUE, "Unsupported bitmap format");
+        delete texAlloc;
+        return NULL;
     }
 
     return texAlloc;
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 90febba..2bea84f 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -148,6 +148,15 @@
         /** Microphone audio source tuned for voice recognition if available, behaves like
          *  {@link #DEFAULT} otherwise. */
         public static final int VOICE_RECOGNITION = 6;
+
+        /**
+         * @hide
+         * Microphone audio source tuned for voice communications such as VoIP. It
+         * will for instance take advantage of echo cancellation or automatic gain control
+         * if available. It otherwise behaves like {@link #DEFAULT} if no voice processing
+         * is available.
+         */
+        public static final int VOICE_COMMUNICATION = 7;
     }
 
     /**
@@ -253,7 +262,7 @@
      * Gets the maximum value for audio sources.
      * @see android.media.MediaRecorder.AudioSource
      */
-    public static final int getAudioSourceMax() { return AudioSource.VOICE_RECOGNITION; }
+    public static final int getAudioSourceMax() { return AudioSource.VOICE_COMMUNICATION; }
 
     /**
      * Sets the video source to be used for recording. If this method is not
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 6a25dc5..9da5f01 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -345,6 +345,17 @@
     return OK;
 }
 
+// Always rotate clockwise, and only support 0, 90, 180 and 270 for now.
+status_t StagefrightRecorder::setParamVideoRotation(int32_t degrees) {
+    LOGV("setParamVideoRotation: %d", degrees);
+    if (degrees < 0 || degrees % 90 != 0) {
+        LOGE("Unsupported video rotation angle: %d", degrees);
+        return BAD_VALUE;
+    }
+    mRotationDegrees = degrees % 360;
+    return OK;
+}
+
 status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) {
     LOGV("setParamMaxFileDurationUs: %lld us", timeUs);
     if (timeUs <= 0) {
@@ -599,6 +610,11 @@
         if (safe_strtoi32(value.string(), &video_bitrate)) {
             return setParamVideoEncodingBitRate(video_bitrate);
         }
+    } else if (key == "video-param-rotation-angle-degrees") {
+        int32_t degrees;
+        if (safe_strtoi32(value.string(), &degrees)) {
+            return setParamVideoRotation(degrees);
+        }
     } else if (key == "video-param-i-frames-interval") {
         int32_t seconds;
         if (safe_strtoi32(value.string(), &seconds)) {
@@ -1255,6 +1271,9 @@
     if (mTrackEveryTimeDurationUs > 0) {
         (*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
     }
+    if (mRotationDegrees != 0) {
+        (*meta)->setInt32(kKeyRotation, mRotationDegrees);
+    }
 }
 
 status_t StagefrightRecorder::startMPEG4Recording() {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index d11d7e0..36a15a8 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -93,6 +93,7 @@
     int64_t mMaxFileSizeBytes;
     int64_t mMaxFileDurationUs;
     int64_t mTrackEveryTimeDurationUs;
+    int32_t mRotationDegrees;  // Clockwise
 
     bool mCaptureTimeLapse;
     int64_t mTimeBetweenTimeLapseFrameCaptureUs;
@@ -146,6 +147,7 @@
     status_t setParamVideoEncoderLevel(int32_t level);
     status_t setParamVideoCameraId(int32_t cameraId);
     status_t setParamVideoTimeScale(int32_t timeScale);
+    status_t setParamVideoRotation(int32_t degrees);
     status_t setParamTrackTimeStatus(int64_t timeDurationUs);
     status_t setParamInterleaveDuration(int32_t durationUs);
     status_t setParam64BitFileOffset(bool use64BitFileOffset);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 562eb60..1df0bed 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -44,8 +44,10 @@
         TimeSource.cpp                    \
         TimedEventQueue.cpp               \
         Utils.cpp                         \
+        VBRISeeker.cpp                    \
         WAVExtractor.cpp                  \
         WVMExtractor.cpp                  \
+        XINGSeeker.cpp                    \
         avc_utils.cpp                     \
         string.cpp
 
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 82c0426..84ced8f 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -21,6 +21,8 @@
 #include "include/MP3Extractor.h"
 
 #include "include/ID3.h"
+#include "include/VBRISeeker.h"
+#include "include/XINGSeeker.h"
 
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
@@ -42,10 +44,11 @@
 // Yes ... there are things that must indeed match...
 static const uint32_t kMask = 0xfffe0cc0;
 
-static bool get_mp3_frame_size(
+// static
+bool MP3Extractor::get_mp3_frame_size(
         uint32_t header, size_t *frame_size,
-        int *out_sampling_rate = NULL, int *out_channels = NULL,
-        int *out_bitrate = NULL) {
+        int *out_sampling_rate, int *out_channels,
+        int *out_bitrate) {
     *frame_size = 0;
 
     if (out_sampling_rate) {
@@ -178,136 +181,13 @@
     return true;
 }
 
-static bool parse_xing_header(
-        const sp<DataSource> &source, off_t first_frame_pos,
-        int32_t *frame_number = NULL, int32_t *byte_number = NULL,
-        char *table_of_contents = NULL, int32_t *quality_indicator = NULL,
-        int64_t *duration = NULL) {
-
-    if (frame_number) {
-        *frame_number = 0;
-    }
-    if (byte_number) {
-        *byte_number = 0;
-    }
-    if (table_of_contents) {
-        table_of_contents[0] = 0;
-    }
-    if (quality_indicator) {
-        *quality_indicator = 0;
-    }
-    if (duration) {
-        *duration = 0;
-    }
-
-    uint8_t buffer[4];
-    int offset = first_frame_pos;
-    if (source->readAt(offset, &buffer, 4) < 4) { // get header
-        return false;
-    }
-    offset += 4;
-
-    uint8_t id, layer, sr_index, mode;
-    layer = (buffer[1] >> 1) & 3;
-    id = (buffer[1] >> 3) & 3;
-    sr_index = (buffer[2] >> 2) & 3;
-    mode = (buffer[3] >> 6) & 3;
-    if (layer == 0) {
-        return false;
-    }
-    if (id == 1) {
-        return false;
-    }
-    if (sr_index == 3) {
-        return false;
-    }
-    // determine offset of XING header
-    if(id&1) { // mpeg1
-        if (mode != 3) offset += 32;
-        else offset += 17;
-    } else { // mpeg2
-        if (mode != 3) offset += 17;
-        else offset += 9;
-    }
-
-    if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
-        return false;
-    }
-    offset += 4;
-    // Check XING ID
-    if ((buffer[0] != 'X') || (buffer[1] != 'i')
-                || (buffer[2] != 'n') || (buffer[3] != 'g')) {
-        if ((buffer[0] != 'I') || (buffer[1] != 'n')
-                    || (buffer[2] != 'f') || (buffer[3] != 'o')) {
-            return false;
-        }
-    }
-
-    if (source->readAt(offset, &buffer, 4) < 4) { // flags
-        return false;
-    }
-    offset += 4;
-    uint32_t flags = U32_AT(buffer);
-
-    if (flags & 0x0001) {  // Frames field is present
-        if (source->readAt(offset, buffer, 4) < 4) {
-             return false;
-        }
-        if (frame_number) {
-           *frame_number = U32_AT(buffer);
-        }
-        int32_t frame = U32_AT(buffer);
-        // Samples per Frame: 1. index = MPEG Version ID, 2. index = Layer
-        const int samplesPerFrames[2][3] =
-        {
-            { 384, 1152, 576  }, // MPEG 2, 2.5: layer1, layer2, layer3
-            { 384, 1152, 1152 }, // MPEG 1: layer1, layer2, layer3
-        };
-        // sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
-        const int samplingRates[4][3] =
-        {
-            { 11025, 12000, 8000,  },    // MPEG 2.5
-            { 0,     0,     0,     },    // reserved
-            { 22050, 24000, 16000, },    // MPEG 2
-            { 44100, 48000, 32000, }     // MPEG 1
-        };
-        if (duration) {
-            *duration = (int64_t)frame * samplesPerFrames[id&1][3-layer] * 1000000LL
-                / samplingRates[id][sr_index];
-        }
-        offset += 4;
-    }
-    if (flags & 0x0002) {  // Bytes field is present
-        if (byte_number) {
-            if (source->readAt(offset, buffer, 4) < 4) {
-                return false;
-            }
-            *byte_number = U32_AT(buffer);
-        }
-        offset += 4;
-    }
-    if (flags & 0x0004) {  // TOC field is present
-       if (table_of_contents) {
-            if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
-                return false;
-            }
-        }
-        offset += 100;
-    }
-    if (flags & 0x0008) {  // Quality indicator field is present
-        if (quality_indicator) {
-            if (source->readAt(offset, buffer, 4) < 4) {
-                return false;
-            }
-            *quality_indicator = U32_AT(buffer);
-        }
-    }
-    return true;
-}
-
 static bool Resync(
         const sp<DataSource> &source, uint32_t match_header,
-        off_t *inout_pos, uint32_t *out_header) {
+        off_t *inout_pos, off_t *post_id3_pos, uint32_t *out_header) {
+    if (post_id3_pos != NULL) {
+        *post_id3_pos = 0;
+    }
+
     if (*inout_pos == 0) {
         // Skip an optional ID3 header if syncing at the very beginning
         // of the datasource.
@@ -340,6 +220,10 @@
             LOGV("skipped ID3 tag, new starting offset is %ld (0x%08lx)",
                  *inout_pos, *inout_pos);
         }
+
+        if (post_id3_pos != NULL) {
+            *post_id3_pos = *inout_pos;
+        }
     }
 
     off_t pos = *inout_pos;
@@ -365,8 +249,9 @@
 
         size_t frame_size;
         int sample_rate, num_channels, bitrate;
-        if (!get_mp3_frame_size(header, &frame_size,
-                               &sample_rate, &num_channels, &bitrate)) {
+        if (!MP3Extractor::get_mp3_frame_size(
+                    header, &frame_size,
+                    &sample_rate, &num_channels, &bitrate)) {
             ++pos;
             continue;
         }
@@ -396,7 +281,8 @@
             }
 
             size_t test_frame_size;
-            if (!get_mp3_frame_size(test_header, &test_frame_size)) {
+            if (!MP3Extractor::get_mp3_frame_size(
+                        test_header, &test_frame_size)) {
                 valid = false;
                 break;
             }
@@ -427,7 +313,7 @@
     MP3Source(
             const sp<MetaData> &meta, const sp<DataSource> &source,
             off_t first_frame_pos, uint32_t fixed_header,
-            int32_t byte_number, const char *table_of_contents);
+            const sp<MP3Seeker> &seeker);
 
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
@@ -448,9 +334,7 @@
     off_t mCurrentPos;
     int64_t mCurrentTimeUs;
     bool mStarted;
-    int32_t mByteNumber; // total number of bytes in this MP3
-    // TOC entries in XING header. Skip the first one since it's always 0.
-    char mTableOfContents[99];
+    sp<MP3Seeker> mSeeker;
     MediaBufferGroup *mGroup;
 
     MP3Source(const MP3Source &);
@@ -462,25 +346,28 @@
     : mInitCheck(NO_INIT),
       mDataSource(source),
       mFirstFramePos(-1),
-      mFixedHeader(0),
-      mByteNumber(0) {
+      mFixedHeader(0) {
     off_t pos = 0;
+    off_t post_id3_pos;
     uint32_t header;
     bool success;
 
     int64_t meta_offset;
     uint32_t meta_header;
+    int64_t meta_post_id3_offset;
     if (meta != NULL
             && meta->findInt64("offset", &meta_offset)
-            && meta->findInt32("header", (int32_t *)&meta_header)) {
+            && meta->findInt32("header", (int32_t *)&meta_header)
+            && meta->findInt64("post-id3-offset", &meta_post_id3_offset)) {
         // The sniffer has already done all the hard work for us, simply
         // accept its judgement.
         pos = (off_t)meta_offset;
         header = meta_header;
+        post_id3_pos = (off_t)meta_post_id3_offset;
 
         success = true;
     } else {
-        success = Resync(mDataSource, 0, &pos, &header);
+        success = Resync(mDataSource, 0, &pos, &post_id3_pos, &header);
     }
 
     if (!success) {
@@ -505,21 +392,27 @@
     mMeta->setInt32(kKeyBitRate, bitrate * 1000);
     mMeta->setInt32(kKeyChannelCount, num_channels);
 
-    int64_t duration;
-    parse_xing_header(
-            mDataSource, mFirstFramePos, NULL, &mByteNumber,
-            mTableOfContents, NULL, &duration);
-    if (duration > 0) {
-        mMeta->setInt64(kKeyDuration, duration);
-    } else {
+    mSeeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
+
+    if (mSeeker == NULL) {
+        mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
+    }
+
+    int64_t durationUs;
+
+    if (mSeeker == NULL || !mSeeker->getDuration(&durationUs)) {
         off_t fileSize;
         if (mDataSource->getSize(&fileSize) == OK) {
-            mMeta->setInt64(
-                    kKeyDuration,
-                    8000LL * (fileSize - mFirstFramePos) / bitrate);
+            durationUs = 8000LL * (fileSize - mFirstFramePos) / bitrate;
+        } else {
+            durationUs = -1;
         }
     }
 
+    if (durationUs >= 0) {
+        mMeta->setInt64(kKeyDuration, durationUs);
+    }
+
     mInitCheck = OK;
 }
 
@@ -534,7 +427,7 @@
 
     return new MP3Source(
             mMeta, mDataSource, mFirstFramePos, mFixedHeader,
-            mByteNumber, mTableOfContents);
+            mSeeker);
 }
 
 sp<MetaData> MP3Extractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -550,7 +443,7 @@
 MP3Source::MP3Source(
         const sp<MetaData> &meta, const sp<DataSource> &source,
         off_t first_frame_pos, uint32_t fixed_header,
-        int32_t byte_number, const char *table_of_contents)
+        const sp<MP3Seeker> &seeker)
     : mMeta(meta),
       mDataSource(source),
       mFirstFramePos(first_frame_pos),
@@ -558,9 +451,8 @@
       mCurrentPos(0),
       mCurrentTimeUs(0),
       mStarted(false),
-      mByteNumber(byte_number),
+      mSeeker(seeker),
       mGroup(NULL) {
-    memcpy (mTableOfContents, table_of_contents, sizeof(mTableOfContents));
 }
 
 MP3Source::~MP3Source() {
@@ -607,43 +499,21 @@
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
-        int32_t bitrate;
-        if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
-            // bitrate is in bits/sec.
-            LOGI("no bitrate");
+        int64_t actualSeekTimeUs = seekTimeUs;
+        if (mSeeker == NULL
+                || !mSeeker->getOffsetForTime(&actualSeekTimeUs, &mCurrentPos)) {
+            int32_t bitrate;
+            if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
+                // bitrate is in bits/sec.
+                LOGI("no bitrate");
 
-            return ERROR_UNSUPPORTED;
-        }
-
-        mCurrentTimeUs = seekTimeUs;
-        // interpolate in TOC to get file seek point in bytes
-        int64_t duration;
-        if ((mByteNumber > 0) && (mTableOfContents[0] > 0)
-            && mMeta->findInt64(kKeyDuration, &duration)) {
-            float percent = (float)seekTimeUs * 100 / duration;
-            float fx;
-            if( percent <= 0.0f ) {
-                fx = 0.0f;
-            } else if( percent >= 100.0f ) {
-                fx = 256.0f;
-            } else {
-                int a = (int)percent;
-                float fa, fb;
-                if ( a == 0 ) {
-                    fa = 0.0f;
-                } else {
-                    fa = (float)mTableOfContents[a-1];
-                }
-                if ( a < 99 ) {
-                    fb = (float)mTableOfContents[a];
-                } else {
-                    fb = 256.0f;
-                }
-                fx = fa + (fb-fa)*(percent-a);
+                return ERROR_UNSUPPORTED;
             }
-            mCurrentPos = mFirstFramePos + (int)((1.0f/256.0f)*fx*mByteNumber);
-        } else {
+
+            mCurrentTimeUs = seekTimeUs;
             mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000;
+        } else {
+            mCurrentTimeUs = actualSeekTimeUs;
         }
     }
 
@@ -667,7 +537,8 @@
         uint32_t header = U32_AT((const uint8_t *)buffer->data());
 
         if ((header & kMask) == (mFixedHeader & kMask)
-            && get_mp3_frame_size(header, &frame_size, NULL, NULL, &bitrate)) {
+            && MP3Extractor::get_mp3_frame_size(
+                header, &frame_size, NULL, NULL, &bitrate)) {
             break;
         }
 
@@ -675,7 +546,7 @@
         LOGV("lost sync! header = 0x%08x, old header = 0x%08x\n", header, mFixedHeader);
 
         off_t pos = mCurrentPos;
-        if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
+        if (!Resync(mDataSource, mFixedHeader, &pos, NULL, NULL)) {
             LOGE("Unable to resync. Signalling end of stream.");
 
             buffer->release();
@@ -781,14 +652,16 @@
         const sp<DataSource> &source, String8 *mimeType,
         float *confidence, sp<AMessage> *meta) {
     off_t pos = 0;
+    off_t post_id3_pos;
     uint32_t header;
-    if (!Resync(source, 0, &pos, &header)) {
+    if (!Resync(source, 0, &pos, &post_id3_pos, &header)) {
         return false;
     }
 
     *meta = new AMessage;
     (*meta)->setInt64("offset", pos);
     (*meta)->setInt32("header", header);
+    (*meta)->setInt64("post-id3-offset", post_id3_pos);
 
     *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
     *confidence = 0.2f;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index b3ed845..17a40b00 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -202,6 +202,7 @@
 
     // Simple validation on the codec specific data
     status_t checkCodecSpecificData() const;
+    int32_t mRotation;
 
     void updateTrackSizeEstimate();
     void addOneStscTableEntry(size_t chunkId, size_t sampleId);
@@ -520,6 +521,59 @@
     LOGD("Writer thread stopped");
 }
 
+/*
+ * MP4 file standard defines a composition matrix:
+ * | a  b  u |
+ * | c  d  v |
+ * | x  y  w |
+ *
+ * the element in the matrix is stored in the following
+ * order: {a, b, u, c, d, v, x, y, w},
+ * where a, b, c, d, x, and y is in 16.16 format, while
+ * u, v and w is in 2.30 format.
+ */
+void MPEG4Writer::writeCompositionMatrix(int degrees) {
+    LOGV("writeCompositionMatrix");
+    uint32_t a = 0x00010000;
+    uint32_t b = 0;
+    uint32_t c = 0;
+    uint32_t d = 0x00010000;
+    switch (degrees) {
+        case 0:
+            break;
+        case 90:
+            a = 0;
+            b = 0x00010000;
+            c = 0xFFFF0000;
+            d = 0;
+            break;
+        case 180:
+            a = 0xFFFF0000;
+            d = 0xFFFF0000;
+            break;
+        case 270:
+            a = 0;
+            b = 0xFFFF0000;
+            c = 0x00010000;
+            d = 0;
+            break;
+        default:
+            CHECK(!"Should never reach this unknown rotation");
+            break;
+    }
+
+    writeInt32(a);           // a
+    writeInt32(b);           // b
+    writeInt32(0);           // u
+    writeInt32(c);           // c
+    writeInt32(d);           // d
+    writeInt32(0);           // v
+    writeInt32(0);           // x
+    writeInt32(0);           // y
+    writeInt32(0x40000000);  // w
+}
+
+
 status_t MPEG4Writer::stop() {
     if (mFile == NULL) {
         return OK;
@@ -585,15 +639,7 @@
         writeInt16(0);             // reserved
         writeInt32(0);             // reserved
         writeInt32(0);             // reserved
-        writeInt32(0x10000);       // matrix
-        writeInt32(0);
-        writeInt32(0);
-        writeInt32(0);
-        writeInt32(0x10000);
-        writeInt32(0);
-        writeInt32(0);
-        writeInt32(0);
-        writeInt32(0x40000000);
+        writeCompositionMatrix(0); // matrix
         writeInt32(0);             // predefined
         writeInt32(0);             // predefined
         writeInt32(0);             // predefined
@@ -886,7 +932,8 @@
       mCodecSpecificData(NULL),
       mCodecSpecificDataSize(0),
       mGotAllCodecSpecificData(false),
-      mReachedEOS(false) {
+      mReachedEOS(false),
+      mRotation(0) {
     getCodecSpecificDataFromInputFormatIfPossible();
 
     const char *mime;
@@ -1179,6 +1226,11 @@
         startTimeUs = 0;
     }
 
+    int32_t rotationDegrees;
+    if (!mIsAudio && params && params->findInt32(kKeyRotation, &rotationDegrees)) {
+        mRotation = rotationDegrees;
+    }
+
     mIsRealTimeRecording = true;
     {
         int32_t isNotRealTime;
@@ -2075,15 +2127,7 @@
         mOwner->writeInt16(mIsAudio ? 0x100 : 0);  // volume
         mOwner->writeInt16(0);             // reserved
 
-        mOwner->writeInt32(0x10000);       // matrix
-        mOwner->writeInt32(0);
-        mOwner->writeInt32(0);
-        mOwner->writeInt32(0);
-        mOwner->writeInt32(0x10000);
-        mOwner->writeInt32(0);
-        mOwner->writeInt32(0);
-        mOwner->writeInt32(0);
-        mOwner->writeInt32(0x40000000);
+        mOwner->writeCompositionMatrix(mRotation);       // matrix
 
         if (mIsAudio) {
             mOwner->writeInt32(0);
diff --git a/media/libstagefright/VBRISeeker.cpp b/media/libstagefright/VBRISeeker.cpp
new file mode 100644
index 0000000..6608644
--- /dev/null
+++ b/media/libstagefright/VBRISeeker.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VBRISeeker"
+#include <utils/Log.h>
+
+#include "include/VBRISeeker.h"
+
+#include "include/MP3Extractor.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static uint32_t U24_AT(const uint8_t *ptr) {
+    return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
+}
+
+// static
+sp<VBRISeeker> VBRISeeker::CreateFromSource(
+        const sp<DataSource> &source, off_t post_id3_pos) {
+    off_t pos = post_id3_pos;
+
+    uint8_t header[4];
+    ssize_t n = source->readAt(pos, header, sizeof(header));
+    if (n < (ssize_t)sizeof(header)) {
+        return NULL;
+    }
+
+    uint32_t tmp = U32_AT(&header[0]);
+    size_t frameSize;
+    int sampleRate;
+    if (!MP3Extractor::get_mp3_frame_size(tmp, &frameSize, &sampleRate)) {
+        return NULL;
+    }
+
+    // VBRI header follows 32 bytes after the header _ends_.
+    pos += sizeof(header) + 32;
+
+    uint8_t vbriHeader[26];
+    n = source->readAt(pos, vbriHeader, sizeof(vbriHeader));
+    if (n < (ssize_t)sizeof(vbriHeader)) {
+        return NULL;
+    }
+
+    if (memcmp(vbriHeader, "VBRI", 4)) {
+        return NULL;
+    }
+
+    size_t numFrames = U32_AT(&vbriHeader[14]);
+
+    int64_t durationUs =
+        numFrames * 1000000ll * (sampleRate >= 32000 ? 1152 : 576) / sampleRate;
+
+    LOGV("duration = %.2f secs", durationUs / 1E6);
+
+    size_t numEntries = U16_AT(&vbriHeader[18]);
+    size_t entrySize = U16_AT(&vbriHeader[22]);
+    size_t scale = U16_AT(&vbriHeader[20]);
+
+    LOGV("%d entries, scale=%d, size_per_entry=%d",
+         numEntries,
+         scale,
+         entrySize);
+
+    size_t totalEntrySize = numEntries * entrySize;
+    uint8_t *buffer = new uint8_t[totalEntrySize];
+
+    n = source->readAt(pos + sizeof(vbriHeader), buffer, totalEntrySize);
+    if (n < (ssize_t)totalEntrySize) {
+        delete[] buffer;
+        buffer = NULL;
+
+        return NULL;
+    }
+
+    sp<VBRISeeker> seeker = new VBRISeeker;
+    seeker->mBasePos = post_id3_pos;
+    seeker->mDurationUs = durationUs;
+
+    off_t offset = post_id3_pos;
+    for (size_t i = 0; i < numEntries; ++i) {
+        uint32_t numBytes;
+        switch (entrySize) {
+            case 1: numBytes = buffer[i]; break;
+            case 2: numBytes = U16_AT(buffer + 2 * i); break;
+            case 3: numBytes = U24_AT(buffer + 3 * i); break;
+            default:
+            {
+                CHECK_EQ(entrySize, 4u);
+                numBytes = U32_AT(buffer + 4 * i); break;
+            }
+        }
+
+        numBytes *= scale;
+
+        seeker->mSegments.push(numBytes);
+
+        LOGV("entry #%d: %d offset 0x%08lx", i, numBytes, offset);
+        offset += numBytes;
+    }
+
+    delete[] buffer;
+    buffer = NULL;
+
+    LOGI("Found VBRI header.");
+
+    return seeker;
+}
+
+VBRISeeker::VBRISeeker()
+    : mDurationUs(-1) {
+}
+
+bool VBRISeeker::getDuration(int64_t *durationUs) {
+    if (mDurationUs < 0) {
+        return false;
+    }
+
+    *durationUs = mDurationUs;
+
+    return true;
+}
+
+bool VBRISeeker::getOffsetForTime(int64_t *timeUs, off_t *pos) {
+    if (mDurationUs < 0) {
+        return false;
+    }
+
+    int64_t segmentDurationUs = mDurationUs / mSegments.size();
+
+    int64_t nowUs = 0;
+    *pos = mBasePos;
+    size_t segmentIndex = 0;
+    while (segmentIndex < mSegments.size() && nowUs < *timeUs) {
+        nowUs += segmentDurationUs;
+        *pos += mSegments.itemAt(segmentIndex++);
+    }
+
+    LOGV("getOffsetForTime %lld us => 0x%08lx", *timeUs, *pos);
+
+    *timeUs = nowUs;
+
+    return true;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp
new file mode 100644
index 0000000..72f260e
--- /dev/null
+++ b/media/libstagefright/XINGSeeker.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/XINGSeeker.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static bool parse_xing_header(
+        const sp<DataSource> &source, off_t first_frame_pos,
+        int32_t *frame_number = NULL, int32_t *byte_number = NULL,
+        char *table_of_contents = NULL, int32_t *quality_indicator = NULL,
+        int64_t *duration = NULL);
+
+// static
+sp<XINGSeeker> XINGSeeker::CreateFromSource(
+        const sp<DataSource> &source, off_t first_frame_pos) {
+    sp<XINGSeeker> seeker = new XINGSeeker;
+
+    seeker->mFirstFramePos = first_frame_pos;
+
+    if (!parse_xing_header(
+                source, first_frame_pos,
+                NULL, &seeker->mSizeBytes, seeker->mTableOfContents,
+                NULL, &seeker->mDurationUs)) {
+        return NULL;
+    }
+
+    LOGI("Found XING header.");
+
+    return seeker;
+}
+
+XINGSeeker::XINGSeeker()
+    : mDurationUs(-1),
+      mSizeBytes(0) {
+}
+
+bool XINGSeeker::getDuration(int64_t *durationUs) {
+    if (mDurationUs < 0) {
+        return false;
+    }
+
+    *durationUs = mDurationUs;
+
+    return true;
+}
+
+bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off_t *pos) {
+    if (mSizeBytes == 0 || mTableOfContents[0] <= 0 || mDurationUs < 0) {
+        return false;
+    }
+
+    float percent = (float)(*timeUs) * 100 / mDurationUs;
+    float fx;
+    if( percent <= 0.0f ) {
+        fx = 0.0f;
+    } else if( percent >= 100.0f ) {
+        fx = 256.0f;
+    } else {
+        int a = (int)percent;
+        float fa, fb;
+        if ( a == 0 ) {
+            fa = 0.0f;
+        } else {
+            fa = (float)mTableOfContents[a-1];
+        }
+        if ( a < 99 ) {
+            fb = (float)mTableOfContents[a];
+        } else {
+            fb = 256.0f;
+        }
+        fx = fa + (fb-fa)*(percent-a);
+    }
+
+    *pos = (int)((1.0f/256.0f)*fx*mSizeBytes) + mFirstFramePos;
+
+    return true;
+}
+
+static bool parse_xing_header(
+        const sp<DataSource> &source, off_t first_frame_pos,
+        int32_t *frame_number, int32_t *byte_number,
+        char *table_of_contents, int32_t *quality_indicator,
+        int64_t *duration) {
+    if (frame_number) {
+        *frame_number = 0;
+    }
+    if (byte_number) {
+        *byte_number = 0;
+    }
+    if (table_of_contents) {
+        table_of_contents[0] = 0;
+    }
+    if (quality_indicator) {
+        *quality_indicator = 0;
+    }
+    if (duration) {
+        *duration = 0;
+    }
+
+    uint8_t buffer[4];
+    int offset = first_frame_pos;
+    if (source->readAt(offset, &buffer, 4) < 4) { // get header
+        return false;
+    }
+    offset += 4;
+
+    uint8_t id, layer, sr_index, mode;
+    layer = (buffer[1] >> 1) & 3;
+    id = (buffer[1] >> 3) & 3;
+    sr_index = (buffer[2] >> 2) & 3;
+    mode = (buffer[3] >> 6) & 3;
+    if (layer == 0) {
+        return false;
+    }
+    if (id == 1) {
+        return false;
+    }
+    if (sr_index == 3) {
+        return false;
+    }
+    // determine offset of XING header
+    if(id&1) { // mpeg1
+        if (mode != 3) offset += 32;
+        else offset += 17;
+    } else { // mpeg2
+        if (mode != 3) offset += 17;
+        else offset += 9;
+    }
+
+    if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
+        return false;
+    }
+    offset += 4;
+    // Check XING ID
+    if ((buffer[0] != 'X') || (buffer[1] != 'i')
+                || (buffer[2] != 'n') || (buffer[3] != 'g')) {
+        if ((buffer[0] != 'I') || (buffer[1] != 'n')
+                    || (buffer[2] != 'f') || (buffer[3] != 'o')) {
+            return false;
+        }
+    }
+
+    if (source->readAt(offset, &buffer, 4) < 4) { // flags
+        return false;
+    }
+    offset += 4;
+    uint32_t flags = U32_AT(buffer);
+
+    if (flags & 0x0001) {  // Frames field is present
+        if (source->readAt(offset, buffer, 4) < 4) {
+             return false;
+        }
+        if (frame_number) {
+           *frame_number = U32_AT(buffer);
+        }
+        int32_t frame = U32_AT(buffer);
+        // Samples per Frame: 1. index = MPEG Version ID, 2. index = Layer
+        const int samplesPerFrames[2][3] =
+        {
+            { 384, 1152, 576  }, // MPEG 2, 2.5: layer1, layer2, layer3
+            { 384, 1152, 1152 }, // MPEG 1: layer1, layer2, layer3
+        };
+        // sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
+        const int samplingRates[4][3] =
+        {
+            { 11025, 12000, 8000,  },    // MPEG 2.5
+            { 0,     0,     0,     },    // reserved
+            { 22050, 24000, 16000, },    // MPEG 2
+            { 44100, 48000, 32000, }     // MPEG 1
+        };
+        if (duration) {
+            *duration = (int64_t)frame * samplesPerFrames[id&1][3-layer] * 1000000LL
+                / samplingRates[id][sr_index];
+        }
+        offset += 4;
+    }
+    if (flags & 0x0002) {  // Bytes field is present
+        if (byte_number) {
+            if (source->readAt(offset, buffer, 4) < 4) {
+                return false;
+            }
+            *byte_number = U32_AT(buffer);
+        }
+        offset += 4;
+    }
+    if (flags & 0x0004) {  // TOC field is present
+       if (table_of_contents) {
+            if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
+                return false;
+            }
+        }
+        offset += 100;
+    }
+    if (flags & 0x0008) {  // Quality indicator field is present
+        if (quality_indicator) {
+            if (source->readAt(offset, buffer, 4) < 4) {
+                return false;
+            }
+            *quality_indicator = U32_AT(buffer);
+        }
+    }
+    return true;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index d5a5313..72611cf 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -50,6 +50,7 @@
                     profileLevel = CORE_PROFILE_LEVEL2;
                     break;
                 }
+                break;
             default:
                 LOGE("Unsupported profile (%d) for H263", omxProfile);
                 return BAD_VALUE;
@@ -74,7 +75,8 @@
                         LOGE("Unsupported level (%d) for MPEG4 simple profile",
                             omxLevel);
                         return BAD_VALUE;
-            }
+                }
+                break;
             case OMX_VIDEO_MPEG4ProfileSimpleScalable:
                 switch (omxLevel) {
                     case OMX_VIDEO_MPEG4Level0b:
@@ -91,6 +93,7 @@
                              "scalable profile", omxLevel);
                         return BAD_VALUE;
                 }
+                break;
             case OMX_VIDEO_MPEG4ProfileCore:
                 switch (omxLevel) {
                     case OMX_VIDEO_MPEG4Level1:
@@ -104,6 +107,7 @@
                              "profile", omxLevel);
                         return BAD_VALUE;
                 }
+                break;
             case OMX_VIDEO_MPEG4ProfileCoreScalable:
                 switch (omxLevel) {
                     case OMX_VIDEO_MPEG4Level1:
@@ -120,6 +124,7 @@
                              "scalable profile", omxLevel);
                         return BAD_VALUE;
                 }
+                break;
             default:
                 LOGE("Unsupported MPEG4 profile (%d)", omxProfile);
                 return BAD_VALUE;
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index 30136e7d..11ca243 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -24,6 +24,7 @@
 
 struct AMessage;
 class DataSource;
+struct MP3Seeker;
 class String8;
 
 class MP3Extractor : public MediaExtractor {
@@ -37,6 +38,11 @@
 
     virtual sp<MetaData> getMetaData();
 
+    static bool get_mp3_frame_size(
+            uint32_t header, size_t *frame_size,
+            int *out_sampling_rate = NULL, int *out_channels = NULL,
+            int *out_bitrate = NULL);
+
 private:
     status_t mInitCheck;
 
@@ -44,8 +50,7 @@
     off_t mFirstFramePos;
     sp<MetaData> mMeta;
     uint32_t mFixedHeader;
-    int32_t mByteNumber; // total number of bytes in this MP3
-    char mTableOfContents[99]; // TOC entries in XING header
+    sp<MP3Seeker> mSeeker;
 
     MP3Extractor(const MP3Extractor &);
     MP3Extractor &operator=(const MP3Extractor &);
diff --git a/media/libstagefright/include/MP3Seeker.h b/media/libstagefright/include/MP3Seeker.h
new file mode 100644
index 0000000..190eaed
--- /dev/null
+++ b/media/libstagefright/include/MP3Seeker.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MP3_SEEKER_H_
+
+#define MP3_SEEKER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct MP3Seeker : public RefBase {
+    MP3Seeker() {}
+
+    virtual bool getDuration(int64_t *durationUs) = 0;
+
+    // Given a request seek time in "*timeUs", find the byte offset closest
+    // to that position and return it in "*pos". Update "*timeUs" to reflect
+    // the actual time that seekpoint represents.
+    virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos) = 0;
+
+protected:
+    virtual ~MP3Seeker() {}
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(MP3Seeker);
+};
+
+}  // namespace android
+
+#endif  // MP3_SEEKER_H_
+
diff --git a/media/libstagefright/include/VBRISeeker.h b/media/libstagefright/include/VBRISeeker.h
new file mode 100644
index 0000000..d6bd19d
--- /dev/null
+++ b/media/libstagefright/include/VBRISeeker.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VBRI_SEEKER_H_
+
+#define VBRI_SEEKER_H_
+
+#include "include/MP3Seeker.h"
+
+#include <utils/Vector.h>
+
+namespace android {
+
+struct DataSource;
+
+struct VBRISeeker : public MP3Seeker {
+    static sp<VBRISeeker> CreateFromSource(
+            const sp<DataSource> &source, off_t post_id3_pos);
+
+    virtual bool getDuration(int64_t *durationUs);
+    virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos);
+
+private:
+    off_t mBasePos;
+    int64_t mDurationUs;
+    Vector<uint32_t> mSegments;
+
+    VBRISeeker();
+
+    DISALLOW_EVIL_CONSTRUCTORS(VBRISeeker);
+};
+
+}  // namespace android
+
+#endif  // VBRI_SEEKER_H_
+
+
diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h
new file mode 100644
index 0000000..d4ff4e1
--- /dev/null
+++ b/media/libstagefright/include/XINGSeeker.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef XING_SEEKER_H_
+
+#define XING_SEEKER_H_
+
+#include "include/MP3Seeker.h"
+
+namespace android {
+
+struct DataSource;
+
+struct XINGSeeker : public MP3Seeker {
+    static sp<XINGSeeker> CreateFromSource(
+            const sp<DataSource> &source, off_t first_frame_pos);
+
+    virtual bool getDuration(int64_t *durationUs);
+    virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos);
+
+private:
+    int64_t mFirstFramePos;
+    int64_t mDurationUs;
+    int32_t mSizeBytes;
+
+    // TOC entries in XING header. Skip the first one since it's always 0.
+    char mTableOfContents[99];
+
+    XINGSeeker();
+
+    DISALLOW_EVIL_CONSTRUCTORS(XINGSeeker);
+};
+
+}  // namespace android
+
+#endif  // XING_SEEKER_H_
+
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index 25d7697..bfac71b 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -78,18 +78,7 @@
 typedef void *EGLNativeWindowType;
 typedef void *EGLNativePixmapType;
 
-#elif defined(__unix__) && !defined(ANDROID)
-
-/* X11 (tentative)  */
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-
-typedef Display *EGLNativeDisplayType;
-typedef Pixmap   EGLNativePixmapType;
-typedef Window   EGLNativeWindowType;
-
-
-#elif defined(ANDROID)
+#elif defined(__ANDROID__) || defined(ANDROID)
 
 #include <android/native_window.h>
 
@@ -99,6 +88,16 @@
 typedef struct egl_native_pixmap_t*     EGLNativePixmapType;
 typedef void*                           EGLNativeDisplayType;
 
+#elif defined(__unix__)
+
+/* X11 (tentative)  */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef Display *EGLNativeDisplayType;
+typedef Pixmap   EGLNativePixmapType;
+typedef Window   EGLNativeWindowType;
+
 #else
 #error "Platform not recognized"
 #endif
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/app_icon.png b/packages/SystemUI/res/drawable-xlarge-mdpi/app_icon.png
new file mode 100644
index 0000000..50a8ac8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xlarge-mdpi/app_icon.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent.xml
index 2f9e0d7..ac038a7 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent.xml
@@ -25,25 +25,29 @@
     android:background="@drawable/sysbar_panel_recents_bg"
     android:orientation="vertical">
 
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/recent_tasks_app_label"
+        android:textSize="22dip"
+        android:drawableLeft="@drawable/app_icon"
+        android:layout_margin="10dip"
+    />
+
     <TextView android:id="@+id/recents_no_recents"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:text="@string/recent_tasks_empty"
+        android:textSize="22dip"
         android:gravity="center_horizontal|center_vertical"
-        android:visibility="gone">
+        android:visibility="gone"
+        android:layout_margin="10dip">
     </TextView>
 
-    <HorizontalScrollView android:id="@+id/scroll_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <LinearLayout android:id="@+id/recents_container"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:gravity="right"
-            android:orientation="horizontal"
-        />
-
-    </HorizontalScrollView>
+    <LinearLayout android:id="@+id/recents_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+    />
 
 </com.android.systemui.statusbar.tablet.RecentAppsPanel>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent_item.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent_item.xml
new file mode 100644
index 0000000..b1997b8
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent_item.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!--    android:background="@drawable/status_bar_closed_default_background" -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:orientation="horizontal"
+    android:layout_margin="10dip">
+
+    <ImageView android:id="@+id/app_thumbnail"
+        android:layout_width="88dip"
+        android:layout_height="56dip"
+        android:layout_margin="10dip"
+        android:background="#80808080">
+    </ImageView>
+
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:orientation="horizontal"
+        android:layout_marginTop="10dip"
+        android:layout_marginBottom="10dip"
+        android:layout_marginRight="10dip">
+
+        <ImageView android:id="@+id/app_icon"
+            android:layout_width="23dip"
+            android:layout_height="23dip"
+            android:gravity="bottom"
+            android:layout_margin="5dip"
+            />
+
+        <TextView android:id="@+id/app_label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="22dip"
+        />
+
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6384db5..a65de37 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -80,6 +80,9 @@
     <!-- Recent Tasks dialog: message when there are no recent applications [CHAR LIMIT=NONE]-->
     <string name="recent_tasks_empty">No recent applications.</string>
 
+    <!-- Recent apps label. Shown as title on recent apps panel -->
+    <string name="recent_tasks_app_label">Apps</string>
+
     <!-- Rotation lock toast text: shown when rotation lock is turned off (and the screen will
          auto-rotate based on the accelerometer).  [CHAR LIMIT=NONE]-->
     <string name="toast_rotation_free">Screen will rotate automatically.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index 6797958..1831eda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -20,45 +20,75 @@
 import java.util.List;
 
 import android.app.ActivityManager;
-import android.bluetooth.BluetoothAdapter;
+import android.app.IThumbnailReceiver;
+import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Matrix;
 import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.net.wifi.WifiManager;
+import android.os.RemoteException;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnClickListener;
-import android.widget.BaseAdapter;
-import android.widget.Gallery;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.systemui.R;
 
 public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnClickListener {
     private static final String TAG = "RecentAppsPanel";
     private static final boolean DEBUG = TabletStatusBarService.DEBUG;
-    private static final int MAX_RECENT_TASKS = 20;
-    private static final float ITEM_WIDTH = 75;
-    private static final float ITEM_HEIGHT = 75;
+    private static final int DISPLAY_TASKS = 4; // number of recent tasks to display
+    private static final int MAX_TASKS = 2 * DISPLAY_TASKS; // give some slack for non-apps
+    private static final boolean DBG = true;
     private TabletStatusBarService mBar;
     private TextView mNoRecents;
     private LinearLayout mRecentsContainer;
-    private float mDensity;
-    private HorizontalScrollView mScrollView;
+    private ArrayList<ActivityDescription> mActivityDescriptions;
+
+    static class ActivityDescription {
+        int id;
+        Bitmap thumbnail; // generated by Activity.onCreateThumbnail()
+        Drawable icon; // application package icon
+        String label; // application package label
+        CharSequence description; // generated by Activity.onCreateDescription()
+        Intent intent; // launch intent for application
+        Matrix matrix; // arbitrary rotation matrix to correct orientation
+        int position; // position in list
+
+        public ActivityDescription(Bitmap _thumbnail,
+                Drawable _icon, String _label, String _desc, Intent _intent, int _id, int _pos)
+        {
+            thumbnail = _thumbnail;
+            icon = _icon;
+            label = _label;
+            description = _desc;
+            intent = _intent;
+            id = _id;
+            position = _pos;
+        }
+    };
+
+    private final IThumbnailReceiver mThumbnailReceiver = new IThumbnailReceiver.Stub() {
+
+        public void finished() throws RemoteException {
+        }
+
+        public void newThumbnail(final int id, final Bitmap bitmap, CharSequence description)
+                throws RemoteException {
+            ActivityDescription info = findActivityDescription(id);
+            if (info != null) {
+                info.thumbnail = bitmap;
+                info.description = description;
+            }
+        }
+    };
 
     public boolean isInContentArea(int x, int y) {
         final int l = getPaddingLeft();
@@ -78,7 +108,6 @@
 
     public RecentAppsPanel(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mDensity = getResources().getDisplayMetrics().density;
     }
 
     @Override
@@ -86,8 +115,6 @@
         super.onFinishInflate();
         mNoRecents = (TextView) findViewById(R.id.recents_no_recents);
         mRecentsContainer = (LinearLayout) findViewById(R.id.recents_container);
-        mScrollView = (HorizontalScrollView) findViewById(R.id.scroll_view);
-        mScrollView.setHorizontalFadingEdgeEnabled(true);
     }
 
     @Override
@@ -95,63 +122,124 @@
         super.onVisibilityChanged(changedView, visibility);
         Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")");
         if (visibility == View.VISIBLE && changedView == this) {
-            refreshIcons();
+            refreshApplicationList();
             mRecentsContainer.setScrollbarFadingEnabled(true);
             mRecentsContainer.scrollTo(0, 0);
         }
     }
 
-    private void refreshIcons() {
-        mRecentsContainer.removeAllViews();
-        final Context context = getContext();
-        final PackageManager pm = context.getPackageManager();
+    private ArrayList<ActivityDescription> getRecentTasks() {
+        ArrayList<ActivityDescription> activityDescriptions = new ArrayList<ActivityDescription>();
+        final PackageManager pm = mContext.getPackageManager();
         final ActivityManager am = (ActivityManager)
-                context.getSystemService(Context.ACTIVITY_SERVICE);
-        final List<ActivityManager.RecentTaskInfo> recentTasks =
-                am.getRecentTasks(MAX_RECENT_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+                mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
-        ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .resolveActivityInfo(pm, 0);
+        final List<ActivityManager.RecentTaskInfo> recentTasks =
+                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+
+        ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+                    .resolveActivityInfo(pm, 0);
 
         int numTasks = recentTasks.size();
-        final int width = (int) (mDensity * ITEM_WIDTH + 0.5f);
-        final int height = (int) (mDensity * ITEM_HEIGHT + 0.5f);
-        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(width, height);
-        for (int i = 0; i < numTasks; ++i) {
-            final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
+        for (int i = 0, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+            final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
 
-            Intent intent = new Intent(info.baseIntent);
-            if (info.origActivity != null) {
-                intent.setComponent(info.origActivity);
+            Intent intent = new Intent(recentInfo.baseIntent);
+            if (recentInfo.origActivity != null) {
+                intent.setComponent(recentInfo.origActivity);
             }
 
-            // Exclude home activity.
+            // Skip the current home activity.
             if (homeInfo != null
                     && homeInfo.packageName.equals(intent.getComponent().getPackageName())
                     && homeInfo.name.equals(intent.getComponent().getClassName())) {
-                    continue;
+                continue;
             }
 
             intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                     | Intent.FLAG_ACTIVITY_NEW_TASK);
             final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
             if (resolveInfo != null) {
-                final ActivityInfo activityInfo = resolveInfo.activityInfo;
-                final String title = activityInfo.loadLabel(pm).toString();
-                Drawable icon = activityInfo.loadIcon(pm);
-
+                final ActivityInfo info = resolveInfo.activityInfo;
+                final String title = info.loadLabel(pm).toString();
+                Drawable icon = info.loadIcon(pm);
+                int id = recentTasks.get(i).id;
                 if (title != null && title.length() > 0 && icon != null) {
-                    ImageView imageView = new ImageView(mContext);
-                    imageView.setScaleType(ImageView.ScaleType.FIT_XY);
-                    imageView.setLayoutParams(layoutParams);
-                    imageView.setOnClickListener(this);
-                    imageView.setTag(intent);
-                    imageView.setImageDrawable(icon);
-                    mRecentsContainer.addView(imageView);
+                    Log.v(TAG, "creating activity desc for id=" + id + ", label=" + title);
+                    ActivityDescription item = new ActivityDescription(
+                            null, icon, title, null, intent, id, index);
+                    activityDescriptions.add(item);
+                    ++index;
+                } else {
+                    if (DBG) Log.v(TAG, "SKIPPING item " + id);
                 }
             }
         }
+        return activityDescriptions;
+    }
+
+    ActivityDescription findActivityDescription(int id)
+    {
+        ActivityDescription desc = null;
+        for (int i = 0; i < mActivityDescriptions.size(); i++) {
+            ActivityDescription item = mActivityDescriptions.get(i);
+            if (item != null && item.id == id) {
+                desc = item;
+                break;
+            }
+        }
+        return desc;
+    }
+
+    private void getThumbnails(ArrayList<ActivityDescription> tasks) {
+        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        List<RunningTaskInfo> runningTasks = am.getRunningTasks(MAX_TASKS, 0, mThumbnailReceiver);
+        for (RunningTaskInfo r : runningTasks) {
+            // Find the activity description associted with the given id
+            ActivityDescription desc = findActivityDescription(r.id);
+            if (desc != null) {
+                if (r.thumbnail != null) {
+                    desc.thumbnail = r.thumbnail;
+                    desc.description = r.description;
+                } else {
+                    if (DBG) Log.v(TAG, "*** RUNNING THUMBNAIL WAS NULL ***");
+                }
+            } else {
+                if (DBG) Log.v(TAG, "Couldn't find ActivityDesc for id=" + r.id);
+            }
+        }
+    }
+
+    private void refreshApplicationList() {
+        mActivityDescriptions = getRecentTasks();
+        getThumbnails(mActivityDescriptions);
+        updateUiElements();
+    }
+
+    private void updateUiElements() {
+        mRecentsContainer.removeAllViews();
+        final int first = 0;
+        final int last = Math.min(mActivityDescriptions.size(), DISPLAY_TASKS) - 1;
+        for (int i = last; i >= first; i--) {
+            ActivityDescription activityDescription = mActivityDescriptions.get(i);
+            View view = View.inflate(mContext, R.layout.sysbar_panel_recent_item, null);
+            ImageView appThumbnail = (ImageView) view.findViewById(R.id.app_thumbnail);
+            ImageView appIcon = (ImageView) view.findViewById(R.id.app_icon);
+            TextView appDescription = (TextView) view.findViewById(R.id.app_label);
+            if (activityDescription.thumbnail != null) {
+                Log.v(TAG, "thumbnail res = " + activityDescription.thumbnail.getWidth()
+                        + "x" + activityDescription.thumbnail.getHeight());
+            } else {
+                Log.v(TAG, "thumbnail for " + activityDescription.label + " was null");
+            }
+            appThumbnail.setImageBitmap(activityDescription.thumbnail);
+            appIcon.setImageDrawable(activityDescription.icon);
+            appDescription.setText(activityDescription.label);
+            view.setOnClickListener(this);
+            view.setTag(activityDescription.intent);
+            Log.v(TAG, "Adding task: " + activityDescription.label);
+            mRecentsContainer.addView(view);
+        }
 
         int views = mRecentsContainer.getChildCount();
         mNoRecents.setVisibility(views == 0 ? View.VISIBLE : View.GONE);
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index 86d4c9f..edea2c5 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -1749,6 +1749,7 @@
     case AUDIO_SOURCE_DEFAULT:
     case AUDIO_SOURCE_MIC:
     case AUDIO_SOURCE_VOICE_RECOGNITION:
+    case AUDIO_SOURCE_VOICE_COMMUNICATION:
         if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
             mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
             device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index da5f204..d035eb5 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -116,7 +116,6 @@
     static final long TIME_TO_RECONNECT = 10*1000;
 
     private static final int NOT_A_SUBTYPE_ID = -1;
-    private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
     // If IME doesn't support the system locale, the default subtype will be the first defined one.
     private static final int DEFAULT_SUBTYPE_ID = 0;
 
@@ -367,7 +366,9 @@
                                     if (!doit) {
                                         return true;
                                     }
-                                    resetSelectedInputMethodAndSubtypeLocked("");
+                                    Settings.Secure.putString(mContext.getContentResolver(),
+                                            Settings.Secure.DEFAULT_INPUT_METHOD, "");
+                                    resetSelectedInputMethodSubtype();
                                     chooseNewDefaultIMELocked();
                                     return true;
                                 }
@@ -424,7 +425,9 @@
                                 changed = true;
                                 curIm = null;
                                 Slog.i(TAG, "Unsetting current input method");
-                                resetSelectedInputMethodAndSubtypeLocked("");
+                                Settings.Secure.putString(mContext.getContentResolver(),
+                                        Settings.Secure.DEFAULT_INPUT_METHOD, "");
+                                resetSelectedInputMethodSubtype();
                             }
                         }
                     }
@@ -507,7 +510,9 @@
                 Slog.i(TAG, "No default found, using " + defIm.getId());
             }
             if (defIm != null) {
-                setSelectSubtypeLocked(defIm, NOT_A_SUBTYPE_ID);
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
+                putSelectedInputMethodSubtype(defIm, NOT_A_SUBTYPE_ID);
             }
         }
 
@@ -989,7 +994,7 @@
                     synchronized (mMethodMap) {
                         if (mCurMethod != null) {
                             try {
-                                setSelectSubtypeLocked(info, subtypeId);
+                                putSelectedInputMethodSubtype(info, subtypeId);
                                 if (mInputShown) {
                                     // If mInputShown is false, there is no IME button on the
                                     // system bar.
@@ -1009,13 +1014,13 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
+            mCurMethodId = id;
             // Set a subtype to this input method.
             // subtypeId the name of a subtype which will be set.
-            setSelectedInputMethodAndSubtypeLocked(info, subtypeId);
-            // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
-            // because mCurMethodId is stored as a history in
-            // setSelectedInputMethodAndSubtypeLocked().
-            mCurMethodId = id;
+            putSelectedInputMethodSubtype(info, subtypeId);
+
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.DEFAULT_INPUT_METHOD, id);
 
             if (ActivityManagerNative.isSystemReady()) {
                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -1482,7 +1487,9 @@
                 }
             }
             InputMethodInfo imi = enabled.get(i);
-            resetSelectedInputMethodAndSubtypeLocked(imi.getId());
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.DEFAULT_INPUT_METHOD, imi.getId());
+            putSelectedInputMethodSubtype(imi, NOT_A_SUBTYPE_ID);
             return true;
         }
 
@@ -1786,8 +1793,11 @@
                 String selId = Settings.Secure.getString(mContext.getContentResolver(),
                         Settings.Secure.DEFAULT_INPUT_METHOD);
                 if (id.equals(selId)) {
-                    resetSelectedInputMethodAndSubtypeLocked(enabledInputMethodsList.size() > 0
-                            ? enabledInputMethodsList.get(0).first : "");
+                    Settings.Secure.putString(
+                            mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD,
+                            enabledInputMethodsList.size() > 0
+                                    ? enabledInputMethodsList.get(0).first : "");
+                    resetSelectedInputMethodSubtype();
                 }
                 // Previous state was enabled.
                 return true;
@@ -1799,54 +1809,22 @@
         }
     }
 
-    private void saveCurrentInputMethodAndSubtypeToHistory() {
-        String subtypeId = NOT_A_SUBTYPE_ID_STR;
-        if (mCurrentSubtype != null) {
-            subtypeId = String.valueOf(mCurrentSubtype.hashCode());
-        }
-        mSettings.addSubtypeToHistory(mCurMethodId, subtypeId);
+    private void resetSelectedInputMethodSubtype() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID);
     }
 
-    private void setSelectSubtypeLocked(InputMethodInfo imi, int subtypeId) {
-        saveCurrentInputMethodAndSubtypeToHistory();
-        if (imi == null || subtypeId < 0) {
-            mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
-            mCurrentSubtype = null;
+    private void putSelectedInputMethodSubtype(InputMethodInfo imi, int subtypeId) {
+        final ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+        if (subtypeId >= 0 && subtypeId < subtypes.size()) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                    subtypes.get(subtypeId).hashCode());
+            mCurrentSubtype = subtypes.get(subtypeId);
         } else {
-            final ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
-            if (subtypeId < subtypes.size()) {
-                mSettings.putSelectedSubtype(subtypes.get(subtypeId).hashCode());
-                mCurrentSubtype = subtypes.get(subtypeId);
-            } else {
-                mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
-                mCurrentSubtype = null;
-            }
+            resetSelectedInputMethodSubtype();
+            mCurrentSubtype = null;
         }
-        
-    }
-
-    private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId) {
-        setSelectSubtypeLocked(imi, subtypeId);
-        mSettings.putSelectedInputMethod(imi.getId());
-    }
-
-    private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
-        InputMethodInfo imi = mMethodMap.get(newDefaultIme);
-        int lastSubtypeId = NOT_A_SUBTYPE_ID;
-        // newDefaultIme is empty when there is no candidate for the selected IME.
-        if (imi != null && !TextUtils.isEmpty(newDefaultIme)) {
-            String subtypeHashCode = mSettings.getLastSubtypeForInputMethodLocked(newDefaultIme);
-            if (subtypeHashCode != null) {
-                try {
-                    lastSubtypeId = getSubtypeIdFromHashCode(
-                            imi, Integer.valueOf(subtypeHashCode));
-                } catch (NumberFormatException e) {
-                    Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
-                }
-            }
-        }
-        setSelectSubtypeLocked(imi, lastSubtypeId);
-        mSettings.putSelectedInputMethod(newDefaultIme);
     }
 
     private int getSelectedInputMethodSubtypeId(String id) {
@@ -1854,6 +1832,7 @@
         if (imi == null) {
             return NOT_A_SUBTYPE_ID;
         }
+        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
         int subtypeId;
         try {
             subtypeId = Settings.Secure.getInt(mContext.getContentResolver(),
@@ -1861,14 +1840,9 @@
         } catch (SettingNotFoundException e) {
             return NOT_A_SUBTYPE_ID;
         }
-        return getSubtypeIdFromHashCode(imi, subtypeId);
-    }
-
-    private int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) {
-        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
         for (int i = 0; i < subtypes.size(); ++i) {
             InputMethodSubtype ims = subtypes.get(i);
-            if (subtypeHashCode == ims.hashCode()) {
+            if (subtypeId == ims.hashCode()) {
                 return i;
             }
         }
@@ -1948,10 +1922,10 @@
         // example: ("ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0")
         private static final char INPUT_METHOD_SEPARATER = ':';
         private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
-        private final TextUtils.SimpleStringSplitter mInputMethodSplitter =
+        private final TextUtils.SimpleStringSplitter mStringColonSplitter =
                 new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
 
-        private final TextUtils.SimpleStringSplitter mSubtypeSplitter =
+        private final TextUtils.SimpleStringSplitter mStringSemiColonSplitter =
                 new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
 
         private final ContentResolver mResolver;
@@ -2014,16 +1988,16 @@
             if (TextUtils.isEmpty(enabledInputMethodsStr)) {
                 return imsList;
             }
-            mInputMethodSplitter.setString(enabledInputMethodsStr);
-            while (mInputMethodSplitter.hasNext()) {
-                String nextImsStr = mInputMethodSplitter.next();
-                mSubtypeSplitter.setString(nextImsStr);
-                if (mSubtypeSplitter.hasNext()) {
+            mStringColonSplitter.setString(enabledInputMethodsStr);
+            while (mStringColonSplitter.hasNext()) {
+                String nextImsStr = mStringColonSplitter.next();
+                mStringSemiColonSplitter.setString(nextImsStr);
+                if (mStringSemiColonSplitter.hasNext()) {
                     ArrayList<String> subtypeHashes = new ArrayList<String>();
                     // The first element is ime id.
-                    String imeId = mSubtypeSplitter.next();
-                    while (mSubtypeSplitter.hasNext()) {
-                        subtypeHashes.add(mSubtypeSplitter.next());
+                    String imeId = mStringSemiColonSplitter.next();
+                    while (mStringSemiColonSplitter.hasNext()) {
+                        subtypeHashes.add(mStringSemiColonSplitter.next());
                     }
                     imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
                 }
@@ -2109,161 +2083,8 @@
         private String getEnabledInputMethodsStr() {
             mEnabledInputMethodsStrCache = Settings.Secure.getString(
                     mResolver, Settings.Secure.ENABLED_INPUT_METHODS);
-            if (DEBUG) {
-                Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache);
-            }
             return mEnabledInputMethodsStrCache;
         }
-
-        private void saveSubtypeHistory(
-                List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
-            StringBuilder builder = new StringBuilder();
-            boolean isImeAdded = false;
-            if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
-                builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
-                        newSubtypeId);
-                isImeAdded = true;
-            }
-            for (Pair<String, String> ime: savedImes) {
-                String imeId = ime.first;
-                String subtypeId = ime.second;
-                if (TextUtils.isEmpty(subtypeId)) {
-                    subtypeId = NOT_A_SUBTYPE_ID_STR;
-                }
-                if (isImeAdded) {
-                    builder.append(INPUT_METHOD_SEPARATER);
-                } else {
-                    isImeAdded = true;
-                }
-                builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
-                        subtypeId);
-            }
-            // Remove the last INPUT_METHOD_SEPARATER
-            putSubtypeHistoryStr(builder.toString());
-        }
-
-        public void addSubtypeToHistory(String imeId, String subtypeId) {
-            List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
-            for (Pair<String, String> ime: subtypeHistory) {
-                if (ime.first.equals(imeId)) {
-                    if (DEBUG) {
-                        Slog.v(TAG, "Subtype found in the history: " + imeId
-                                + ime.second);
-                    }
-                    // We should break here
-                    subtypeHistory.remove(ime);
-                    break;
-                }
-            }
-            saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
-        }
-
-        private void putSubtypeHistoryStr(String str) {
-            if (DEBUG) {
-                Slog.d(TAG, "putSubtypeHistoryStr: " + str);
-            }
-            Settings.Secure.putString(
-                    mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str);
-        }
-
-        public Pair<String, String> getLastInputMethodAndSubtypeLocked() {
-            // Gets the first one from the history
-            return getLastSubtypeForInputMethodLockedInternal(null);
-        }
-
-        public String getLastSubtypeForInputMethodLocked(String imeId) {
-            Pair<String, String> ime = getLastSubtypeForInputMethodLockedInternal(imeId);
-            if (ime != null) {
-                return ime.second;
-            } else {
-                return null;
-            }
-        }
-
-        private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) {
-            List<Pair<String, ArrayList<String>>> enabledImes =
-                    getEnabledInputMethodsAndSubtypeListLocked();
-            List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
-            for (Pair<String, String> imeAndSubtype: subtypeHistory) {
-                final String imeInTheHistory = imeAndSubtype.first;
-                // If imeId is empty, returns the first IME and subtype in the history
-                if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) {
-                    final String subtypeInTheHistory = imeAndSubtype.second;
-                    final String subtypeHashCode = getEnabledSubtypeForInputMethodAndSubtypeLocked(
-                            enabledImes, imeInTheHistory, subtypeInTheHistory);
-                    if (!TextUtils.isEmpty(subtypeHashCode)) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "Enabled subtype found in the history:" + subtypeHashCode);
-                        }
-                        return new Pair<String, String>(imeInTheHistory, subtypeHashCode);
-                    }
-                }
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "No enabled IME found in the history");
-            }
-            return null;
-        }
-
-        private String getEnabledSubtypeForInputMethodAndSubtypeLocked(List<Pair<String,
-                ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
-            for (Pair<String, ArrayList<String>> enabledIme: enabledImes) {
-                if (enabledIme.first.equals(imeId)) {
-                    for (String s: enabledIme.second) {
-                        if (s.equals(subtypeHashCode)) {
-                            // If both imeId and subtypeId are enabled, return subtypeId.
-                            return s;
-                        }
-                    }
-                    // If imeId was enabled but subtypeId was disabled.
-                    return NOT_A_SUBTYPE_ID_STR;
-                }
-            }
-            // If both imeId and subtypeId are disabled, return null
-            return null;
-        }
-
-        private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() {
-            ArrayList<Pair<String, String>> imsList = new ArrayList<Pair<String, String>>();
-            final String subtypeHistoryStr = getSubtypeHistoryStr();
-            if (TextUtils.isEmpty(subtypeHistoryStr)) {
-                return imsList;
-            }
-            mInputMethodSplitter.setString(subtypeHistoryStr);
-            while (mInputMethodSplitter.hasNext()) {
-                String nextImsStr = mInputMethodSplitter.next();
-                mSubtypeSplitter.setString(nextImsStr);
-                if (mSubtypeSplitter.hasNext()) {
-                    String subtypeId = NOT_A_SUBTYPE_ID_STR;
-                    // The first element is ime id.
-                    String imeId = mSubtypeSplitter.next();
-                    while (mSubtypeSplitter.hasNext()) {
-                        subtypeId = mSubtypeSplitter.next();
-                        break;
-                    }
-                    imsList.add(new Pair<String, String>(imeId, subtypeId));
-                }
-            }
-            return imsList;
-        }
-
-        private String getSubtypeHistoryStr() {
-            if (DEBUG) {
-                Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getString(
-                        mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY));
-            }
-            return Settings.Secure.getString(
-                    mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY);
-        }
-
-        public void putSelectedInputMethod(String imeId) {
-            Settings.Secure.putString(mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, imeId);
-        }
-
-        public void putSelectedSubtype(int subtypeId) {
-            Settings.Secure.putInt(
-                    mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, subtypeId);
-        }
     }
 
     // ----------------------------------------------------------------------
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 1c92da9..fb87d69 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -23,8 +23,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -100,7 +98,6 @@
 import android.view.Display;
 import android.view.DragEvent;
 import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
@@ -128,7 +125,6 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Transformation;
 
-import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
 import java.io.File;
@@ -464,6 +460,11 @@
     // If non-null, we are in the middle of animating from one wallpaper target
     // to another, and this is the higher one in Z-order.
     WindowState mUpperWallpaperTarget = null;
+    // Window currently running an animation that has requested it be detached
+    // from the wallpaper.  This means we need to ensure the wallpaper is
+    // visible behind it in case it animates in a way that would allow it to be
+    // seen.
+    WindowState mWindowDetachedWallpaper = null;
     int mWallpaperAnimLayerAdjustment;
     float mLastWallpaperX = -1;
     float mLastWallpaperY = -1;
@@ -1709,6 +1710,7 @@
         int foundI = 0;
         WindowState topCurW = null;
         int topCurI = 0;
+        int windowDetachedI = -1;
         int i = N;
         while (i > 0) {
             i--;
@@ -1721,13 +1723,12 @@
                 continue;
             }
             topCurW = null;
-            if (w.mAppToken != null) {
+            if (w != mWindowDetachedWallpaper && w.mAppToken != null) {
                 // If this window's app token is hidden and not animating,
                 // it is of no interest to us.
                 if (w.mAppToken.hidden && w.mAppToken.animation == null) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
-                            "Skipping hidden or animating token: " + w);
-                    topCurW = null;
+                            "Skipping not hidden or animating token: " + w);
                     continue;
                 }
             }
@@ -1752,9 +1753,18 @@
                     continue;
                 }
                 break;
+            } else if (w == mWindowDetachedWallpaper) {
+                windowDetachedI = i;
             }
         }
 
+        if (foundW == null && windowDetachedI >= 0) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Found animating detached wallpaper activity: #" + i + "=" + w);
+            foundW = w;
+            foundI = windowDetachedI;
+        }
+
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
             // If we are currently waiting for an app transition, and either
             // the current target or the next target are involved with it,
@@ -8961,6 +8971,12 @@
         int curLayer = 0;
         int i;
 
+        if (DEBUG_LAYERS) {
+            RuntimeException here = new RuntimeException("here");
+            here.fillInStackTrace();
+            Log.v(TAG, "Assigning layers", here);
+        }
+
         for (i=0; i<N; i++) {
             WindowState w = mWindows.get(i);
             if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
@@ -9297,6 +9313,7 @@
                 boolean tokenMayBeDrawn = false;
                 boolean wallpaperMayChange = false;
                 boolean forceHiding = false;
+                WindowState windowDetachedWallpaper = null;
 
                 mPolicy.beginAnimationLw(dw, dh);
 
@@ -9318,19 +9335,35 @@
                             }
                         }
 
-                        boolean wasAnimating = w.mAnimating;
-                        if (w.stepAnimationLocked(currentTime, dw, dh)) {
+                        final boolean wasAnimating = w.mAnimating;
+                        final boolean nowAnimating = w.stepAnimationLocked(currentTime, dw, dh);
+
+                        // If this window is animating, make a note that we have
+                        // an animating window and take care of a request to run
+                        // a detached wallpaper animation.
+                        if (nowAnimating) {
+                            if (w.mAnimation != null && w.mAnimation.getDetachWallpaper()) {
+                                windowDetachedWallpaper = w;
+                            }
                             animating = true;
-                            //w.dump("  ");
                         }
+
+                        // If this window's app token is running a detached wallpaper
+                        // animation, make a note so we can ensure the wallpaper is
+                        // displayed behind it.
+                        if (w.mAppToken != null && w.mAppToken.animation != null
+                                && w.mAppToken.animation.getDetachWallpaper()) {
+                            windowDetachedWallpaper = w;
+                        }
+
                         if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) {
                             wallpaperMayChange = true;
                         }
 
                         if (mPolicy.doesForceHide(w, attrs)) {
-                            if (!wasAnimating && animating) {
+                            if (!wasAnimating && nowAnimating) {
                                 if (DEBUG_VISIBILITY) Slog.v(TAG,
-                                        "Animation done that could impact force hide: "
+                                        "Animation started that could impact force hide: "
                                         + w);
                                 wallpaperForceHidingChanged = true;
                                 mFocusMayChange = true;
@@ -9754,6 +9787,14 @@
                     }
                 }
 
+                if (mWindowDetachedWallpaper != windowDetachedWallpaper) {
+                    if (DEBUG_WALLPAPER) Slog.v(TAG,
+                            "Detached wallpaper changed from " + mWindowDetachedWallpaper
+                            + windowDetachedWallpaper);
+                    mWindowDetachedWallpaper = windowDetachedWallpaper;
+                    wallpaperMayChange = true;
+                }
+
                 if (wallpaperMayChange) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
                             "Wallpaper may change!  Adjusting");
@@ -11085,6 +11126,9 @@
                 pw.print("  mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
                 pw.print("  mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
             }
+            if (mWindowDetachedWallpaper != null) {
+                pw.print("  mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
+            }
             pw.print("  mCurConfiguration="); pw.println(this.mCurConfiguration);
             pw.print("  mInTouchMode="); pw.print(mInTouchMode);
                     pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
index ebdf9c2..6c33a21 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
@@ -240,13 +240,18 @@
 
     /**
      * Checks if the file is a test.
-     * Currently we run .html and .xhtml tests.
+     * Currently we run .html, .xhtml and .php tests.
+     *
+     * @warning You MUST also call isTestDir() on the parent directory before
+     * assuming that a file is a test.
      *
      * @param testName
      * @return if the file is a test
      */
     public static boolean isTestFile(String testName) {
-        return testName.endsWith(".html") || testName.endsWith(".xhtml");
+        return testName.endsWith(".html")
+            || testName.endsWith(".xhtml")
+            || testName.endsWith(".php");
     }
 
     /**