Merge change I53e3a20e into eclair-mr2

* changes:
  move SubscribedFeeds to GSF
diff --git a/Android.mk b/Android.mk
index 732b486..127693f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -442,6 +442,7 @@
 LOCAL_DROIDDOC_OPTIONS:=\
 		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
                 $(web_docs_sample_code_flags) \
+                -offlinemode \
 		-title "Android SDK" \
 		-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
 		-todo $(OUT_DOCS)/$(LOCAL_MODULE)-docs-todo.html \
diff --git a/api/current.xml b/api/current.xml
index fed1e45..12c2eee 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -167061,6 +167061,39 @@
  visibility="public"
 >
 </field>
+<field name="BRIGHTNESS_OVERRIDE_FULL"
+ type="float"
+ transient="false"
+ volatile="false"
+ value="1.0f"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BRIGHTNESS_OVERRIDE_NONE"
+ type="float"
+ transient="false"
+ volatile="false"
+ value="-1.0f"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BRIGHTNESS_OVERRIDE_OFF"
+ type="float"
+ transient="false"
+ volatile="false"
+ value="0.0f"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CREATOR"
  type="android.os.Parcelable.Creator"
  transient="false"
@@ -167917,6 +167950,16 @@
  visibility="public"
 >
 </field>
+<field name="buttonBrightness"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="dimAmount"
  type="float"
  transient="false"
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 8c25d85..a31a5c0 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -19,13 +19,13 @@
 #include <binder/ProcessState.h>
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/CameraSource.h>
+#include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MPEG4Writer.h>
-#include <media/stagefright/MmapSource.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
 #include <media/MediaPlayerInterface.h>
@@ -105,7 +105,7 @@
     sp<MediaSource> source;
 
     sp<MediaExtractor> extractor =
-        MediaExtractor::Create(new MmapSource(filename));
+        MediaExtractor::Create(new FileSource(filename));
 
     size_t num_tracks = extractor->countTracks();
 
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index d26e558..76ec54b 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -24,15 +24,14 @@
 #include <binder/ProcessState.h>
 #include <media/IMediaPlayerService.h>
 #include <media/stagefright/CachingDataSource.h>
+#include <media/stagefright/FileSource.h>
 #include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/JPEGSource.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaPlayerImpl.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
-#include <media/stagefright/MmapSource.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
 
@@ -52,9 +51,6 @@
 static void playSource(OMXClient *client, const sp<MediaSource> &source) {
     sp<MetaData> meta = source->getFormat();
 
-    int64_t durationUs;
-    CHECK(meta->findInt64(kKeyDuration, &durationUs));
-
     const char *mime;
     CHECK(meta->findCString(kKeyMIMEType, &mime));
 
@@ -74,6 +70,9 @@
     rawSource->start();
 
     if (gReproduceBug >= 3 && gReproduceBug <= 5) {
+        int64_t durationUs;
+        CHECK(meta->findInt64(kKeyDuration, &durationUs));
+
         status_t err;
         MediaBuffer *buffer;
         MediaSource::ReadOptions options;
@@ -368,7 +367,7 @@
             dataSource = new HTTPDataSource(filename);
             dataSource = new CachingDataSource(dataSource, 64 * 1024, 10);
         } else {
-            dataSource = new MmapSource(filename);
+            dataSource = new FileSource(filename);
         }
 
         bool isJPEG = false;
diff --git a/common/Android.mk b/common/Android.mk
index 349bb86..76091eb 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -24,3 +24,6 @@
 
 # Include this library in the build server's output directory
 $(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):android-common.jar)
+
+# Build the test package
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/common/java/com/android/common/OperationScheduler.java b/common/java/com/android/common/OperationScheduler.java
new file mode 100644
index 0000000..71b22ce
--- /dev/null
+++ b/common/java/com/android/common/OperationScheduler.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common;
+
+import android.content.SharedPreferences;
+import android.text.format.Time;
+
+import java.util.Map;
+import java.util.TreeSet;
+
+/**
+ * Tracks the success/failure history of a particular network operation in
+ * persistent storage and computes retry strategy accordingly.  Handles
+ * exponential backoff, periodic rescheduling, event-driven triggering,
+ * retry-after moratorium intervals, etc. based on caller-specified parameters.
+ *
+ * <p>This class does not directly perform or invoke any operations,
+ * it only keeps track of the schedule.  Somebody else needs to call
+ * {@link #getNextTimeMillis()} as appropriate and do the actual work.
+ */
+public class OperationScheduler {
+    /** Tunable parameter options for {@link #getNextTimeMillis}. */
+    public static class Options {
+        /** Wait this long after every error before retrying. */
+        public long backoffFixedMillis = 0;
+
+        /** Wait this long times the number of consecutive errors so far before retrying. */
+        public long backoffIncrementalMillis = 5000;
+
+        /** Maximum duration of moratorium to honor.  Mostly an issue for clock rollbacks. */
+        public long maxMoratoriumMillis = 24 * 3600 * 1000;
+
+        /** Minimum duration after success to wait before allowing another trigger. */
+        public long minTriggerMillis = 0;
+
+        /** Automatically trigger this long after the last success. */
+        public long periodicIntervalMillis = 0;
+
+        @Override
+        public String toString() {
+            return String.format(
+                    "OperationScheduler.Options[backoff=%.1f+%.1f max=%.1f min=%.1f period=%.1f]",
+                    backoffFixedMillis / 1000.0, backoffIncrementalMillis / 1000.0,
+                    maxMoratoriumMillis / 1000.0, minTriggerMillis / 1000.0,
+                    periodicIntervalMillis / 1000.0);
+        }
+    }
+
+    private static final String PREFIX = "OperationScheduler_";
+    private final SharedPreferences mStorage;
+
+    /**
+     * Initialize the scheduler state.
+     * @param storage to use for recording the state of operations across restarts/reboots
+     */
+    public OperationScheduler(SharedPreferences storage) {
+        mStorage = storage;
+    }
+
+    /**
+     * Parse scheduler options supplied in this string form:
+     *
+     * <pre>
+     * backoff=(fixed)+(incremental) max=(maxmoratorium) min=(mintrigger) [period=](interval)
+     * </pre>
+     *
+     * All values are times in (possibly fractional) <em>seconds</em> (not milliseconds).
+     * Omitted settings are left at whatever existing default value was passed in.
+     *
+     * <p>
+     * The default options: <code>backoff=0+5 max=86400 min=0 period=0</code><br>
+     * Fractions are OK: <code>backoff=+2.5 period=10.0</code><br>
+     * The "period=" can be omitted: <code>3600</code><br>
+     *
+     * @param spec describing some or all scheduler options.
+     * @param options to update with parsed values.
+     * @return the options passed in (for convenience)
+     * @throws IllegalArgumentException if the syntax is invalid
+     */
+    public static Options parseOptions(String spec, Options options)
+            throws IllegalArgumentException {
+        for (String param : spec.split(" +")) {
+            if (param.length() == 0) continue;
+            if (param.startsWith("backoff=")) {
+                int plus = param.indexOf('+', 8);
+                if (plus < 0) {
+                    options.backoffFixedMillis = parseSeconds(param.substring(8));
+                } else {
+                    if (plus > 8) {
+                        options.backoffFixedMillis = parseSeconds(param.substring(8, plus));
+                    }
+                    options.backoffIncrementalMillis = parseSeconds(param.substring(plus + 1));
+                }
+            } else if (param.startsWith("max=")) {
+                options.maxMoratoriumMillis = parseSeconds(param.substring(4));
+            } else if (param.startsWith("min=")) {
+                options.minTriggerMillis = parseSeconds(param.substring(4));
+            } else if (param.startsWith("period=")) {
+                options.periodicIntervalMillis = parseSeconds(param.substring(7));
+            } else {
+                options.periodicIntervalMillis = parseSeconds(param);
+            }
+        }
+        return options;
+    }
+
+    private static long parseSeconds(String param) throws NumberFormatException {
+        return (long) (Float.parseFloat(param) * 1000);
+    }
+
+    /**
+     * Compute the time of the next operation.  Does not modify any state.
+     *
+     * @param options to use for this computation.
+     * @return the wall clock time ({@link System#currentTimeMillis()}) when the
+     * next operation should be attempted -- immediately, if the return value is
+     * before the current time.
+     */
+    public long getNextTimeMillis(Options options) {
+        boolean enabledState = mStorage.getBoolean(PREFIX + "enabledState", true);
+        if (!enabledState) return Long.MAX_VALUE;
+
+        boolean permanentError = mStorage.getBoolean(PREFIX + "permanentError", false);
+        if (permanentError) return Long.MAX_VALUE;
+
+        // We do quite a bit of limiting to prevent a clock rollback from totally
+        // hosing the scheduler.  Times which are supposed to be in the past are
+        // clipped to the current time so we don't languish forever.
+
+        int errorCount = mStorage.getInt(PREFIX + "errorCount", 0);
+        long now = System.currentTimeMillis();
+        long lastSuccessTimeMillis = getTimeBefore(PREFIX + "lastSuccessTimeMillis", now);
+        long lastErrorTimeMillis = getTimeBefore(PREFIX + "lastErrorTimeMillis", now);
+        long triggerTimeMillis = mStorage.getLong(PREFIX + "triggerTimeMillis", Long.MAX_VALUE);
+        long moratoriumSetMillis = mStorage.getLong(PREFIX + "moratoriumSetTimeMillis", 0);
+        long moratoriumTimeMillis = getTimeBefore(PREFIX + "moratoriumTimeMillis",
+                moratoriumSetMillis + options.maxMoratoriumMillis);
+
+        long time = triggerTimeMillis;
+        if (options.periodicIntervalMillis > 0) {
+            time = Math.min(time, lastSuccessTimeMillis + options.periodicIntervalMillis);
+        }
+        if (time >= moratoriumTimeMillis - options.maxMoratoriumMillis) {
+            time = Math.max(time, moratoriumTimeMillis);
+        }
+        time = Math.max(time, lastSuccessTimeMillis + options.minTriggerMillis);
+        time = Math.max(time, lastErrorTimeMillis + options.backoffFixedMillis +
+                options.backoffIncrementalMillis * errorCount);
+        return time;
+    }
+
+    /**
+     * Fetch a {@link SharedPreferences} property, but force it to be before
+     * a certain time, updating the value if necessary.  This is to recover
+     * gracefully from clock rollbacks which could otherwise strand our timers.
+     *
+     * @param name of SharedPreferences key
+     * @param max time to allow in result
+     * @return current value attached to key (default 0), limited by max
+     */
+    private long getTimeBefore(String name, long max) {
+        long time = mStorage.getLong(name, 0);
+        if (time > max) mStorage.edit().putLong(name, (time = max)).commit();
+        return time;
+    }
+
+    /**
+     * Request an operation to be performed at a certain time.  The actual
+     * scheduled time may be affected by error backoff logic and defined
+     * minimum intervals.
+     *
+     * @param millis wall clock time ({@link System#currentTimeMillis()}) to
+     * trigger another operation; 0 to trigger immediately
+     */
+    public void setTriggerTimeMillis(long millis) {
+        mStorage.edit().putLong(PREFIX + "triggerTimeMillis", millis).commit();
+    }
+
+    /**
+     * Forbid any operations until after a certain (absolute) time.
+     * Commonly used when a server returns a "Retry-After:" type directive.
+     * Limited by {@link #Options.maxMoratoriumMillis}.
+     *
+     * @param millis wall clock time ({@link System#currentTimeMillis()}) to
+     * wait before attempting any more operations; 0 to remove moratorium
+     */
+    public void setMoratoriumTimeMillis(long millis) {
+        mStorage.edit()
+                .putLong(PREFIX + "moratoriumTimeMillis", millis)
+                .putLong(PREFIX + "moratoriumSetTimeMillis", System.currentTimeMillis())
+                .commit();
+    }
+
+    /**
+     * Enable or disable all operations.  When disabled, all calls to
+     * {@link #getNextTimeMillis()} return {@link Long#MAX_VALUE}.
+     * Commonly used when data network availability goes up and down.
+     *
+     * @param enabled if operations can be performed
+     */
+    public void setEnabledState(boolean enabled) {
+        mStorage.edit().putBoolean(PREFIX + "enabledState", enabled).commit();
+    }
+
+    /**
+     * Report successful completion of an operation.  Resets all error
+     * counters, clears any trigger directives, and records the success.
+     */
+    public void onSuccess() {
+        resetTransientError();
+        resetPermanentError();
+        long now = System.currentTimeMillis();
+        mStorage.edit()
+                .remove(PREFIX + "errorCount")
+                .remove(PREFIX + "lastErrorTimeMillis")
+                .remove(PREFIX + "permanentError")
+                .remove(PREFIX + "triggerTimeMillis")
+                .putLong(PREFIX + "lastSuccessTimeMillis", now).commit();
+    }
+
+    /**
+     * Report a transient error (usually a network failure).  Increments
+     * the error count and records the time of the latest error for backoff
+     * purposes.
+     */
+    public void onTransientError() {
+        long now = System.currentTimeMillis();
+        mStorage.edit().putLong(PREFIX + "lastErrorTimeMillis", now).commit();
+        mStorage.edit().putInt(PREFIX + "errorCount",
+                mStorage.getInt(PREFIX + "errorCount", 0) + 1).commit();
+    }
+
+    /**
+     * Reset all transient error counts, allowing the next operation to proceed
+     * immediately without backoff.  Commonly used on network state changes, when
+     * partial progress occurs (some data received), and in other circumstances
+     * where there is reason to hope things might start working better.
+     */
+    public void resetTransientError() {
+        mStorage.edit()
+                .remove(PREFIX + "lastErrorTimeMillis")
+                .remove(PREFIX + "errorCount").commit();
+    }
+
+    /**
+     * Report a permanent error that will not go away until further notice.
+     * No operation will be scheduled until {@link #resetPermanentError()}
+     * is called.  Commonly used for authentication failures (which are reset
+     * when the accounts database is updated).
+     */
+    public void onPermanentError() {
+        mStorage.edit().putBoolean(PREFIX + "permanentError", true).commit();
+    }
+
+    /**
+     * Reset any permanent error status set by {@link #onPermanentError},
+     * allowing operations to be scheduled as normal.
+     */
+    public void resetPermanentError() {
+        mStorage.edit().remove(PREFIX + "permanentError").commit();
+    }
+
+    /**
+     * Return a string description of the scheduler state for debugging.
+     */
+    public String toString() {
+        StringBuilder out = new StringBuilder("[OperationScheduler:");
+        for (String key : new TreeSet<String>(mStorage.getAll().keySet())) {  // Sort keys
+            if (key.startsWith(PREFIX)) {
+                if (key.endsWith("TimeMillis")) {
+                    Time time = new Time();
+                    time.set(mStorage.getLong(key, 0));
+                    out.append(" ").append(key.substring(PREFIX.length(), key.length() - 10));
+                    out.append("=").append(time.format("%Y-%m-%d/%H:%M:%S"));
+                } else {
+                    out.append(" ").append(key.substring(PREFIX.length()));
+                    out.append("=").append(mStorage.getAll().get(key).toString());
+                }
+            }
+        }
+        return out.append("]").toString();
+    }
+}
diff --git a/common/tests/src/com/android/common/OperationSchedulerTest.java b/common/tests/src/com/android/common/OperationSchedulerTest.java
new file mode 100644
index 0000000..13f710d
--- /dev/null
+++ b/common/tests/src/com/android/common/OperationSchedulerTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common;
+
+import android.content.SharedPreferences;
+import android.test.AndroidTestCase;
+
+public class OperationSchedulerTest extends AndroidTestCase {
+    public void testScheduler() throws Exception {
+        String name = "OperationSchedulerTest.testScheduler";
+        SharedPreferences storage = getContext().getSharedPreferences(name, 0);
+        storage.edit().clear().commit();
+
+        OperationScheduler scheduler = new OperationScheduler(storage);
+        OperationScheduler.Options options = new OperationScheduler.Options();
+        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+
+        long beforeTrigger = System.currentTimeMillis();
+        scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
+        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
+
+        // It will schedule for the later of the trigger and the moratorium...
+        scheduler.setMoratoriumTimeMillis(beforeTrigger + 500000);
+        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
+        scheduler.setMoratoriumTimeMillis(beforeTrigger + 1500000);
+        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+
+        // Test enable/disable toggle
+        scheduler.setEnabledState(false);
+        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+        scheduler.setEnabledState(true);
+        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+
+        // Backoff interval after an error
+        long beforeError = System.currentTimeMillis();
+        scheduler.onTransientError();
+        long afterError = System.currentTimeMillis();
+        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+        options.backoffFixedMillis = 1000000;
+        options.backoffIncrementalMillis = 500000;
+        assertTrue(beforeError + 1500000 <= scheduler.getNextTimeMillis(options));
+        assertTrue(afterError + 1500000 >= scheduler.getNextTimeMillis(options));
+
+        // Two errors: backoff interval increases
+        beforeError = System.currentTimeMillis();
+        scheduler.onTransientError();
+        afterError = System.currentTimeMillis();
+        assertTrue(beforeError + 2000000 <= scheduler.getNextTimeMillis(options));
+        assertTrue(afterError + 2000000 >= scheduler.getNextTimeMillis(options));
+
+        // Permanent error holds true even if transient errors are reset
+        // However, we remember that the transient error was reset...
+        scheduler.onPermanentError();
+        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+        scheduler.resetTransientError();
+        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+        scheduler.resetPermanentError();
+        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+
+        // Success resets the trigger
+        long beforeSuccess = System.currentTimeMillis();
+        scheduler.onSuccess();
+        long afterSuccess = System.currentTimeMillis();
+        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+
+        // The moratorium is not reset by success!
+        scheduler.setTriggerTimeMillis(beforeSuccess + 500000);
+        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+        scheduler.setMoratoriumTimeMillis(0);
+        assertEquals(beforeSuccess + 500000, scheduler.getNextTimeMillis(options));
+
+        // Periodic interval after success
+        options.periodicIntervalMillis = 250000;
+        assertTrue(beforeSuccess + 250000 <= scheduler.getNextTimeMillis(options));
+        assertTrue(afterSuccess + 250000 >= scheduler.getNextTimeMillis(options));
+
+        // Trigger minimum is also since the last success
+        options.minTriggerMillis = 1000000;
+        assertTrue(beforeSuccess + 1000000 <= scheduler.getNextTimeMillis(options));
+        assertTrue(afterSuccess + 1000000 >= scheduler.getNextTimeMillis(options));
+    }
+
+    public void testParseOptions() throws Exception {
+         OperationScheduler.Options options = new OperationScheduler.Options();
+         assertEquals(
+                 "OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]",
+                 OperationScheduler.parseOptions("3600", options).toString());
+
+         assertEquals(
+                 "OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]",
+                 OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString());
+
+         assertEquals(
+                 "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
+                 OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800",
+                         options).toString());
+
+         assertEquals(
+                "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
+                 OperationScheduler.parseOptions("", options).toString());
+    }
+}
diff --git a/common/tests/src/com/android/common/PatternsTest.java b/common/tests/src/com/android/common/PatternsTest.java
index a89ad62..7fabe5e 100644
--- a/common/tests/src/com/android/common/PatternsTest.java
+++ b/common/tests/src/com/android/common/PatternsTest.java
@@ -28,10 +28,10 @@
     public void testTldPattern() throws Exception {
         boolean t;
 
-        t = Patterns.TOP_LEVEL_DOMAIN_PATTERN.matcher("com").matches();
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("com").matches();
         assertTrue("Missed valid TLD", t);
 
-        t = Patterns.TOP_LEVEL_DOMAIN_PATTERN.matcher("xer").matches();
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("xer").matches();
         assertFalse("Matched invalid TLD!", t);
     }
 
@@ -39,19 +39,19 @@
     public void testUrlPattern() throws Exception {
         boolean t;
 
-        t = Patterns.WEB_URL_PATTERN.matcher("http://www.google.com").matches();
+        t = Patterns.WEB_URL.matcher("http://www.google.com").matches();
         assertTrue("Valid URL", t);
 
-        t = Patterns.WEB_URL_PATTERN.matcher("ftp://www.example.com").matches();
+        t = Patterns.WEB_URL.matcher("ftp://www.example.com").matches();
         assertFalse("Matched invalid protocol", t);
 
-        t = Patterns.WEB_URL_PATTERN.matcher("http://www.example.com:8080").matches();
+        t = Patterns.WEB_URL.matcher("http://www.example.com:8080").matches();
         assertTrue("Didn't match valid URL with port", t);
 
-        t = Patterns.WEB_URL_PATTERN.matcher("http://www.example.com:8080/?foo=bar").matches();
+        t = Patterns.WEB_URL.matcher("http://www.example.com:8080/?foo=bar").matches();
         assertTrue("Didn't match valid URL with port and query args", t);
 
-        t = Patterns.WEB_URL_PATTERN.matcher("http://www.example.com:8080/~user/?foo=bar").matches();
+        t = Patterns.WEB_URL.matcher("http://www.example.com:8080/~user/?foo=bar").matches();
         assertTrue("Didn't match valid URL with ~", t);
     }
 
@@ -59,10 +59,10 @@
     public void testIpPattern() throws Exception {
         boolean t;
 
-        t = Patterns.IP_ADDRESS_PATTERN.matcher("172.29.86.3").matches();
+        t = Patterns.IP_ADDRESS.matcher("172.29.86.3").matches();
         assertTrue("Valid IP", t);
 
-        t = Patterns.IP_ADDRESS_PATTERN.matcher("1234.4321.9.9").matches();
+        t = Patterns.IP_ADDRESS.matcher("1234.4321.9.9").matches();
         assertFalse("Invalid IP", t);
     }
 
@@ -70,10 +70,10 @@
     public void testDomainPattern() throws Exception {
         boolean t;
 
-        t = Patterns.DOMAIN_NAME_PATTERN.matcher("mail.example.com").matches();
+        t = Patterns.DOMAIN_NAME.matcher("mail.example.com").matches();
         assertTrue("Valid domain", t);
 
-        t = Patterns.DOMAIN_NAME_PATTERN.matcher("__+&42.xer").matches();
+        t = Patterns.DOMAIN_NAME.matcher("__+&42.xer").matches();
         assertFalse("Invalid domain", t);
     }
 
@@ -81,10 +81,10 @@
     public void testPhonePattern() throws Exception {
         boolean t;
 
-        t = Patterns.PHONE_PATTERN.matcher("(919) 555-1212").matches();
+        t = Patterns.PHONE.matcher("(919) 555-1212").matches();
         assertTrue("Valid phone", t);
 
-        t = Patterns.PHONE_PATTERN.matcher("2334 9323/54321").matches();
+        t = Patterns.PHONE.matcher("2334 9323/54321").matches();
         assertFalse("Invalid phone", t);
 
         String[] tests = {
@@ -115,7 +115,7 @@
         };
 
         for (String test : tests) {
-            Matcher m = Patterns.PHONE_PATTERN.matcher(test);
+            Matcher m = Patterns.PHONE.matcher(test);
 
             assertTrue("Valid phone " + test, m.find());
         }
diff --git a/core/java/android/os/LocalPowerManager.java b/core/java/android/os/LocalPowerManager.java
index 03331aa..d348f07 100644
--- a/core/java/android/os/LocalPowerManager.java
+++ b/core/java/android/os/LocalPowerManager.java
@@ -49,4 +49,5 @@
     boolean isScreenOn();
 
     void setScreenBrightnessOverride(int brightness);
+    void setButtonBrightnessOverride(int brightness);
 }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6696533..fe329f2 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -649,7 +649,28 @@
          * be cleared automatically after the window is displayed.
          */
         public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100;
-        
+
+        /**
+         * Default value for {@link #screenBrightness} and {@link #buttonBrightness}
+         * indicating that the brightness value is not overridden for this window
+         * and normal brightness policy should be used.
+         */
+        public static final float BRIGHTNESS_OVERRIDE_NONE = -1.0f;
+
+        /**
+         * Value for {@link #screenBrightness} and {@link #buttonBrightness}
+         * indicating that the screen or button backlight brightness should be set
+         * to the lowest value when this window is in front.
+         */
+        public static final float BRIGHTNESS_OVERRIDE_OFF = 0.0f;
+
+        /**
+         * Value for {@link #screenBrightness} and {@link #buttonBrightness}
+         * indicating that the screen or button backlight brightness should be set
+         * to the hightest value when this window is in front.
+         */
+        public static final float BRIGHTNESS_OVERRIDE_FULL = 1.0f;
+
         /**
          * Desired operating mode for any soft input area.  May any combination
          * of:
@@ -717,9 +738,17 @@
          * preferred screen brightness.  0 to 1 adjusts the brightness from
          * dark to full bright.
          */
-        public float screenBrightness = -1.0f;
+        public float screenBrightness = BRIGHTNESS_OVERRIDE_NONE;
         
         /**
+         * This can be used to override the standard behavior of the button and
+         * keyboard backlights.  A value of less than 0, the default, means to
+         * use the standard backlight behavior.  0 to 1 adjusts the brightness
+         * from dark to full bright.
+         */
+        public float buttonBrightness = BRIGHTNESS_OVERRIDE_NONE;
+
+        /**
          * Identifier for this window.  This will usually be filled in for
          * you.
          */
@@ -816,6 +845,7 @@
             out.writeFloat(alpha);
             out.writeFloat(dimAmount);
             out.writeFloat(screenBrightness);
+            out.writeFloat(buttonBrightness);
             out.writeStrongBinder(token);
             out.writeString(packageName);
             TextUtils.writeToParcel(mTitle, out, parcelableFlags);
@@ -851,6 +881,7 @@
             alpha = in.readFloat();
             dimAmount = in.readFloat();
             screenBrightness = in.readFloat();
+            buttonBrightness = in.readFloat();
             token = in.readStrongBinder();
             packageName = in.readString();
             mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
@@ -870,6 +901,8 @@
         public static final int SOFT_INPUT_MODE_CHANGED = 1<<9;
         public static final int SCREEN_ORIENTATION_CHANGED = 1<<10;
         public static final int SCREEN_BRIGHTNESS_CHANGED = 1<<11;
+        /** {@hide} */
+        public static final int BUTTON_BRIGHTNESS_CHANGED = 1<<12;
     
         // internal buffer to backup/restore parameters under compatibility mode.
         private int[] mCompatibilityParamsBackup = null;
@@ -971,6 +1004,10 @@
                 screenBrightness = o.screenBrightness;
                 changes |= SCREEN_BRIGHTNESS_CHANGED;
             }
+            if (buttonBrightness != o.buttonBrightness) {
+                buttonBrightness = o.buttonBrightness;
+                changes |= BUTTON_BRIGHTNESS_CHANGED;
+            }
     
             if (screenOrientation != o.screenOrientation) {
                 screenOrientation = o.screenOrientation;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1899a6e..8dc2ce6 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3291,6 +3291,35 @@
         }
     }
 
+    /**
+     * Dump the display tree to "/sdcard/displayTree.txt"
+     *
+     * @hide debug only
+     */
+    public void dumpDisplayTree() {
+        nativeDumpDisplayTree(getUrl());
+    }
+
+    /**
+     * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
+     * "/sdcard/domTree.txt"
+     *
+     * @hide debug only
+     */
+    public void dumpDomTree(boolean toFile) {
+        mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
+    }
+
+    /**
+     * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
+     * to "/sdcard/renderTree.txt"
+     *
+     * @hide debug only
+     */
+    public void dumpRenderTree(boolean toFile) {
+        mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
+    }
+
     // This is used to determine long press with the center key.  Does not
     // affect long press with the trackball/touch.
     private boolean mGotCenterDown = false;
@@ -3380,24 +3409,15 @@
         if (getSettings().getNavDump()) {
             switch (keyCode) {
                 case KeyEvent.KEYCODE_4:
-                    // "/data/data/com.android.browser/displayTree.txt"
-                    nativeDumpDisplayTree(getUrl());
+                    dumpDisplayTree();
                     break;
                 case KeyEvent.KEYCODE_5:
                 case KeyEvent.KEYCODE_6:
-                    // 5: dump the dom tree to the file
-                    // "/data/data/com.android.browser/domTree.txt"
-                    // 6: dump the dom tree to the adb log
-                    mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE,
-                            (keyCode == KeyEvent.KEYCODE_5) ? 1 : 0, 0);
+                    dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
                     break;
                 case KeyEvent.KEYCODE_7:
                 case KeyEvent.KEYCODE_8:
-                    // 7: dump the render tree to the file
-                    // "/data/data/com.android.browser/renderTree.txt"
-                    // 8: dump the render tree to the adb log
-                    mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE,
-                            (keyCode == KeyEvent.KEYCODE_7) ? 1 : 0, 0);
+                    dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
                     break;
                 case KeyEvent.KEYCODE_9:
                     nativeInstrumentReport();
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index f723cfd..6575da6 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -133,9 +133,9 @@
         return INVALID_OPERATION;
     };
 
-protected:
     virtual void        sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }
 
+protected:
     void*               mCookie;
     notify_callback_f   mNotify;
 };
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 960eda3..71344e6 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -30,12 +30,20 @@
 
 class AudioPlayer : public TimeSource {
 public:
+    enum {
+        REACHED_EOS,
+        SEEK_COMPLETE
+    };
+
     AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
     virtual ~AudioPlayer();
 
     // Caller retains ownership of "source".
     void setSource(const sp<MediaSource> &source);
 
+    void setListenerCallback(
+            void (*notify)(void *cookie, int what), void *cookie);
+
     // Return time in us.
     virtual int64_t getRealTimeUs();
 
@@ -76,6 +84,9 @@
 
     bool mStarted;
 
+    void (*mListenerCallback)(void *cookie, int what);
+    void *mListenerCookie;
+
     sp<MediaPlayerBase::AudioSink> mAudioSink;
 
     static void AudioCallback(int event, void *user, void *info);
diff --git a/include/media/stagefright/MediaPlayerImpl.h b/include/media/stagefright/MediaPlayerImpl.h
deleted file mode 100644
index 53a2088..0000000
--- a/include/media/stagefright/MediaPlayerImpl.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2009 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 MEDIA_PLAYER_IMPL_H_
-
-#define MEDIA_PLAYER_IMPL_H_
-
-#include <pthread.h>
-
-#include <media/MediaPlayerInterface.h>
-#include <media/stagefright/OMXClient.h>
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class AudioPlayer;
-class IOMXRenderer;
-class ISurface;
-class MediaExtractor;
-class MediaBuffer;
-class MediaSource;
-class MemoryHeapPmem;
-class MetaData;
-class Surface;
-class TimeSource;
-
-class MediaPlayerImpl {
-public:
-    MediaPlayerImpl(const char *uri);
-
-    status_t initCheck() const;
-
-    // Assumes ownership of "fd".
-    MediaPlayerImpl(int fd, int64_t offset, int64_t length);
-
-    ~MediaPlayerImpl();
-
-    void play();
-    void pause();
-    bool isPlaying() const;
-
-    void setSurface(const sp<Surface> &surface);
-    void setISurface(const sp<ISurface> &isurface);
-
-    void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
-
-    int32_t getWidth() const { return mVideoWidth; }
-    int32_t getHeight() const { return mVideoHeight; }
-
-    int64_t getDuration();
-    int64_t getPosition();
-    status_t seekTo(int64_t time);
-
-private:
-    status_t mInitCheck;
-
-    OMXClient mClient;
-
-    sp<MediaExtractor> mExtractor;
-
-    TimeSource *mTimeSource;
-
-    sp<MediaSource> mAudioSource;
-    sp<MediaSource> mAudioDecoder;
-    AudioPlayer *mAudioPlayer;
-
-    sp<MediaSource> mVideoSource;
-    sp<MediaSource> mVideoDecoder;
-    int32_t mVideoWidth, mVideoHeight;
-    int64_t mVideoPosition;
-
-    int64_t mDuration;
-
-    bool mPlaying;
-    bool mPaused;
-
-    int64_t mTimeSourceDeltaUs;
-
-    sp<Surface> mSurface;
-    sp<ISurface> mISurface;
-    sp<IOMXRenderer> mVideoRenderer;
-
-    sp<MediaPlayerBase::AudioSink> mAudioSink;
-
-    Mutex mLock;
-    pthread_t mVideoThread;
-
-    bool mSeeking;
-    int64_t mSeekTimeUs;
-
-    void init();
-
-    static void *VideoWrapper(void *me);
-    void videoEntry();
-
-    void setAudioSource(const sp<MediaSource> &source);
-    void setVideoSource(const sp<MediaSource> &source);
-
-    MediaSource *makeShoutcastSource(const char *path);
-
-    void displayOrDiscardFrame(MediaBuffer *buffer, int64_t pts_us);
-    void populateISurface();
-    void depopulateISurface();
-    void sendFrameToISurface(MediaBuffer *buffer);
-
-    void stop();
-
-    MediaPlayerImpl(const MediaPlayerImpl &);
-    MediaPlayerImpl &operator=(const MediaPlayerImpl &);
-};
-
-}  // namespace android
-
-#endif  // MEDIA_PLAYER_IMPL_H_
diff --git a/include/media/stagefright/MmapSource.h b/include/media/stagefright/MmapSource.h
deleted file mode 100644
index 1b39d53..0000000
--- a/include/media/stagefright/MmapSource.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2009 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 MMAP_SOURCE_H_
-
-#define MMAP_SOURCE_H_
-
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaErrors.h>
-
-namespace android {
-
-class MmapSource : public DataSource {
-public:
-    MmapSource(const char *filename);
-
-    // Assumes ownership of "fd".
-    MmapSource(int fd, int64_t offset, int64_t length);
-
-    virtual status_t initCheck() const;
-
-    virtual ssize_t readAt(off_t offset, void *data, size_t size);
-    virtual status_t getSize(off_t *size);
-
-protected:
-    virtual ~MmapSource();
-
-private:
-    int mFd;
-    void *mBase;
-    size_t mSize;
-
-    MmapSource(const MmapSource &);
-    MmapSource &operator=(const MmapSource &);
-};
-
-}  // namespace android
-
-#endif  // MMAP_SOURCE_H_
-
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
index 7a3aee86..42c1877 100644
--- a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
@@ -24,11 +24,11 @@
 #include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/ColorConverter.h>
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
 #include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MetaData.h>
-#include <media/stagefright/MmapSource.h>
 #include <media/stagefright/OMXCodec.h>
 
 namespace android {
@@ -58,7 +58,7 @@
     LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
 
     mExtractor = MediaExtractor::Create(
-            new MmapSource(fd, offset, length));
+            new FileSource(fd, offset, length));
 
     return OK;
 }
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index dbee451..5915105 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -3,19 +3,24 @@
 #include <utils/Log.h>
 
 #include "StagefrightPlayer.h"
-#include <media/stagefright/MediaPlayerImpl.h>
+
+#include "AwesomePlayer.h"
 
 namespace android {
 
 StagefrightPlayer::StagefrightPlayer()
-    : mPlayer(NULL) {
+    : mPlayer(new AwesomePlayer) {
     LOGV("StagefrightPlayer");
+
+    mPlayer->setListener(this);
 }
 
 StagefrightPlayer::~StagefrightPlayer() {
     LOGV("~StagefrightPlayer");
     reset();
-    LOGV("~StagefrightPlayer done.");
+
+    delete mPlayer;
+    mPlayer = NULL;
 }
 
 status_t StagefrightPlayer::initCheck() {
@@ -25,62 +30,32 @@
 
 status_t StagefrightPlayer::setDataSource(const char *url) {
     LOGV("setDataSource('%s')", url);
-
-    reset();
-    mPlayer = new MediaPlayerImpl(url);
-
-    status_t err = mPlayer->initCheck();
-    if (err != OK) {
-        delete mPlayer;
-        mPlayer = NULL;
-    } else {
-        mPlayer->setAudioSink(mAudioSink);
-    }
-
-    return err;
+    return mPlayer->setDataSource(url);
 }
 
 // Warning: The filedescriptor passed into this method will only be valid until
 // the method returns, if you want to keep it, dup it!
 status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
     LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
-
-    reset();
-    mPlayer = new MediaPlayerImpl(dup(fd), offset, length);
-
-    status_t err = mPlayer->initCheck();
-    if (err != OK) {
-        delete mPlayer;
-        mPlayer = NULL;
-    } else {
-        mPlayer->setAudioSink(mAudioSink);
-    }
-
-    return err;
+    return mPlayer->setDataSource(dup(fd), offset, length);
 }
 
 status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
     LOGV("setVideoSurface");
 
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
     mPlayer->setISurface(surface);
-
     return OK;
 }
 
 status_t StagefrightPlayer::prepare() {
     LOGV("prepare");
 
-    if (mPlayer == NULL) {
-        return NO_INIT;
+    int32_t width, height;
+    if (mPlayer->getVideoDimensions(&width, &height) != OK) {
+        width = height = 0;
     }
 
-    sendEvent(
-            MEDIA_SET_VIDEO_SIZE,
-            mPlayer->getWidth(), mPlayer->getHeight());
+    sendEvent(MEDIA_SET_VIDEO_SIZE, width, height);
 
     return OK;
 }
@@ -102,92 +77,76 @@
 status_t StagefrightPlayer::start() {
     LOGV("start");
 
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
-    mPlayer->play();
-
-    return OK;
+    return mPlayer->play();
 }
 
 status_t StagefrightPlayer::stop() {
     LOGV("stop");
 
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
-    reset();
-
-    return OK;
+    return pause();  // what's the difference?
 }
 
 status_t StagefrightPlayer::pause() {
     LOGV("pause");
 
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
-    mPlayer->pause();
-
-    return OK;
+    return mPlayer->pause();
 }
 
 bool StagefrightPlayer::isPlaying() {
     LOGV("isPlaying");
-    return mPlayer != NULL && mPlayer->isPlaying();
+    return mPlayer->isPlaying();
 }
 
 status_t StagefrightPlayer::seekTo(int msec) {
     LOGV("seekTo");
 
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
     status_t err = mPlayer->seekTo((int64_t)msec * 1000);
 
-    sendEvent(MEDIA_SEEK_COMPLETE);
-
     return err;
 }
 
 status_t StagefrightPlayer::getCurrentPosition(int *msec) {
     LOGV("getCurrentPosition");
 
-    if (mPlayer == NULL) {
-        return NO_INIT;
+    int64_t positionUs;
+    status_t err = mPlayer->getPosition(&positionUs);
+
+    if (err != OK) {
+        return err;
     }
 
-    *msec = mPlayer->getPosition() / 1000;
+    *msec = (positionUs + 500) / 1000;
+
     return OK;
 }
 
 status_t StagefrightPlayer::getDuration(int *msec) {
     LOGV("getDuration");
 
-    if (mPlayer == NULL) {
-        return NO_INIT;
+    int64_t durationUs;
+    status_t err = mPlayer->getDuration(&durationUs);
+
+    if (err != OK) {
+        return err;
     }
 
-    *msec = mPlayer->getDuration() / 1000;
+    *msec = (durationUs + 500) / 1000;
+
     return OK;
 }
 
 status_t StagefrightPlayer::reset() {
     LOGV("reset");
 
-    delete mPlayer;
-    mPlayer = NULL;
+    mPlayer->reset();
 
     return OK;
 }
 
 status_t StagefrightPlayer::setLooping(int loop) {
     LOGV("setLooping");
-    return UNKNOWN_ERROR;
+
+    return mPlayer->setLooping(loop);
 }
 
 player_type StagefrightPlayer::playerType() {
@@ -202,9 +161,7 @@
 void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
     MediaPlayerInterface::setAudioSink(audioSink);
 
-    if (mPlayer != NULL) {
-        mPlayer->setAudioSink(audioSink);
-    }
+    mPlayer->setAudioSink(audioSink);
 }
 
 }  // namespace android
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index f214872c..9d005cb 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -22,7 +22,7 @@
 
 namespace android {
 
-class MediaPlayerImpl;
+struct AwesomePlayer;
 
 class StagefrightPlayer : public MediaPlayerInterface {
 public:
@@ -49,7 +49,7 @@
     virtual void setAudioSink(const sp<AudioSink> &audioSink);
 
 private:
-    MediaPlayerImpl *mPlayer;
+    AwesomePlayer *mPlayer;
 
     StagefrightPlayer(const StagefrightPlayer &);
     StagefrightPlayer &operator=(const StagefrightPlayer &);
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 1e3c5a4..bdd7550 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -33,7 +33,10 @@
 
 class AMRSource : public MediaSource {
 public:
-    AMRSource(const sp<DataSource> &source, bool isWide);
+    AMRSource(const sp<DataSource> &source,
+              const sp<MetaData> &meta,
+              size_t frameSize,
+              bool isWide);
 
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
@@ -48,6 +51,8 @@
 
 private:
     sp<DataSource> mDataSource;
+    sp<MetaData> mMeta;
+    size_t mFrameSize;
     bool mIsWide;
 
     off_t mOffset;
@@ -61,15 +66,63 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+static size_t getFrameSize(bool isWide, unsigned FT) {
+    static const size_t kFrameSizeNB[8] = {
+        95, 103, 118, 134, 148, 159, 204, 244
+    };
+    static const size_t kFrameSizeWB[9] = {
+        132, 177, 253, 285, 317, 365, 397, 461, 477
+    };
+
+    size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
+
+    // Round up bits to bytes and add 1 for the header byte.
+    frameSize = (frameSize + 7) / 8 + 1;
+
+    return frameSize;
+}
+
 AMRExtractor::AMRExtractor(const sp<DataSource> &source)
     : mDataSource(source),
       mInitCheck(NO_INIT) {
     String8 mimeType;
     float confidence;
-    if (SniffAMR(mDataSource, &mimeType, &confidence)) {
-        mInitCheck = OK;
-        mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
+    if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
+        return;
     }
+
+    mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
+
+    mMeta = new MetaData;
+    mMeta->setCString(
+            kKeyMIMEType, mIsWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
+                                  : MEDIA_MIMETYPE_AUDIO_AMR_NB);
+
+    mMeta->setInt32(kKeyChannelCount, 1);
+    mMeta->setInt32(kKeySampleRate, mIsWide ? 16000 : 8000);
+
+    size_t offset = mIsWide ? 9 : 6;
+    uint8_t header;
+    if (mDataSource->readAt(offset, &header, 1) != 1) {
+        return;
+    }
+
+    unsigned FT = (header >> 3) & 0x0f;
+
+    if (FT > 8 || (!mIsWide && FT > 7)) {
+        return;
+    }
+
+    mFrameSize = getFrameSize(mIsWide, FT);
+
+    off_t streamSize;
+    if (mDataSource->getSize(&streamSize) == OK) {
+        off_t numFrames = streamSize / mFrameSize;
+
+        mMeta->setInt64(kKeyDuration, 20000ll * numFrames);
+    }
+
+    mInitCheck = OK;
 }
 
 AMRExtractor::~AMRExtractor() {
@@ -84,7 +137,7 @@
         return NULL;
     }
 
-    return new AMRSource(mDataSource, mIsWide);
+    return new AMRSource(mDataSource, mMeta, mFrameSize, mIsWide);
 }
 
 sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -92,26 +145,17 @@
         return NULL;
     }
 
-    return makeAMRFormat(mIsWide);
-}
-
-// static
-sp<MetaData> AMRExtractor::makeAMRFormat(bool isWide) {
-    sp<MetaData> meta = new MetaData;
-    meta->setCString(
-            kKeyMIMEType, isWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
-                                 : MEDIA_MIMETYPE_AUDIO_AMR_NB);
-
-    meta->setInt32(kKeyChannelCount, 1);
-    meta->setInt32(kKeySampleRate, isWide ? 16000 : 8000);
-
-    return meta;
+    return mMeta;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-AMRSource::AMRSource(const sp<DataSource> &source, bool isWide)
+AMRSource::AMRSource(
+        const sp<DataSource> &source, const sp<MetaData> &meta,
+        size_t frameSize, bool isWide)
     : mDataSource(source),
+      mMeta(meta),
+      mFrameSize(frameSize),
       mIsWide(isWide),
       mOffset(mIsWide ? 9 : 6),
       mCurrentTimeUs(0),
@@ -148,13 +192,20 @@
 }
 
 sp<MetaData> AMRSource::getFormat() {
-    return AMRExtractor::makeAMRFormat(mIsWide);
+    return mMeta;
 }
 
 status_t AMRSource::read(
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
 
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        int64_t seekFrame = seekTimeUs / 20000ll;  // 20ms per frame.
+        mCurrentTimeUs = seekFrame * 20000ll;
+        mOffset = seekFrame * mFrameSize + (mIsWide ? 9 : 6);
+    }
+
     uint8_t header;
     ssize_t n = mDataSource->readAt(mOffset, &header, 1);
 
@@ -180,17 +231,8 @@
         return ERROR_MALFORMED;
     }
 
-    static const size_t kFrameSizeNB[8] = {
-        95, 103, 118, 134, 148, 159, 204, 244
-    };
-    static const size_t kFrameSizeWB[9] = {
-        132, 177, 253, 285, 317, 365, 397, 461, 477
-    };
-
-    size_t frameSize = mIsWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
-
-    // Round up bits to bytes and add 1 for the header byte.
-    frameSize = (frameSize + 7) / 8 + 1;
+    size_t frameSize = getFrameSize(mIsWide, FT);
+    CHECK_EQ(frameSize, mFrameSize);
 
     n = mDataSource->readAt(mOffset, buffer->data(), frameSize);
 
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index c36e769..460c496 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -17,6 +17,7 @@
 LOCAL_SRC_FILES +=                \
         AMRExtractor.cpp          \
         AudioPlayer.cpp           \
+        AwesomePlayer.cpp         \
         CachingDataSource.cpp     \
         CameraSource.cpp          \
         DataSource.cpp            \
@@ -28,8 +29,6 @@
         MPEG4Extractor.cpp        \
         MPEG4Writer.cpp           \
         MediaExtractor.cpp        \
-        MediaPlayerImpl.cpp       \
-        MmapSource.cpp            \
         SampleTable.cpp           \
         ShoutcastSource.cpp       \
         TimeSource.cpp            \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index d7e3f66..4280683 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -47,6 +47,12 @@
     }
 }
 
+void AudioPlayer::setListenerCallback(
+        void (*notify)(void *cookie, int what), void *cookie) {
+    mListenerCallback = notify;
+    mListenerCookie = cookie;
+}
+
 void AudioPlayer::setSource(const sp<MediaSource> &source) {
     CHECK_EQ(mSource, NULL);
     mSource = source;
@@ -195,7 +201,6 @@
                     mInputBuffer->release();
                     mInputBuffer = NULL;
                 }
-                mSeeking = false;
             }
         }
 
@@ -205,7 +210,19 @@
             CHECK((err == OK && mInputBuffer != NULL)
                    || (err != OK && mInputBuffer == NULL));
 
+            if (mSeeking) {
+                mSeeking = false;
+
+                if (mListenerCallback) {
+                    (*mListenerCallback)(mListenerCookie, SEEK_COMPLETE);
+                }
+            }
+
             if (err != OK) {
+                if (mListenerCallback) {
+                    (*mListenerCallback)(mListenerCookie, REACHED_EOS);
+                }
+
                 memset((char *)data + size_done, 0, size_remaining);
                 break;
             }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
new file mode 100644
index 0000000..ed46cea
--- /dev/null
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2009 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 "AwesomePlayer"
+#include <utils/Log.h>
+
+#include "include/AwesomePlayer.h"
+
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXCodec.h>
+
+namespace android {
+
+struct AwesomeEvent : public TimedEventQueue::Event {
+    AwesomeEvent(AwesomePlayer *player, int32_t code)
+        : mPlayer(player),
+          mCode(code) {
+    }
+
+protected:
+    virtual ~AwesomeEvent() {}
+
+    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+        mPlayer->onEvent(mCode);
+    }
+
+private:
+    AwesomePlayer *mPlayer;
+    int32_t mCode;
+
+    AwesomeEvent(const AwesomeEvent &);
+    AwesomeEvent &operator=(const AwesomeEvent &);
+};
+
+AwesomePlayer::AwesomePlayer()
+    : mTimeSource(NULL),
+      mAudioPlayer(NULL),
+      mLastVideoBuffer(NULL),
+      mVideoBuffer(NULL) {
+    CHECK_EQ(mClient.connect(), OK);
+
+    DataSource::RegisterDefaultSniffers();
+
+    mVideoEvent = new AwesomeEvent(this, 0);
+    mVideoEventPending = false;
+    mStreamDoneEvent = new AwesomeEvent(this, 1);
+    mStreamDoneEventPending = false;
+
+    mQueue.start();
+
+    reset();
+}
+
+AwesomePlayer::~AwesomePlayer() {
+    mQueue.stop();
+
+    reset();
+
+    mClient.disconnect();
+}
+
+void AwesomePlayer::cancelPlayerEvents() {
+    mQueue.cancelEvent(mVideoEvent->eventID());
+    mVideoEventPending = false;
+    mQueue.cancelEvent(mStreamDoneEvent->eventID());
+    mStreamDoneEventPending = false;
+}
+
+void AwesomePlayer::setListener(const sp<MediaPlayerBase> &listener) {
+    Mutex::Autolock autoLock(mLock);
+    mListener = listener;
+}
+
+status_t AwesomePlayer::setDataSource(const char *uri) {
+    Mutex::Autolock autoLock(mLock);
+
+    reset_l();
+
+    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri);
+
+    if (extractor == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    return setDataSource_l(extractor);
+}
+
+status_t AwesomePlayer::setDataSource(
+        int fd, int64_t offset, int64_t length) {
+    Mutex::Autolock autoLock(mLock);
+
+    reset_l();
+
+    sp<DataSource> source = new FileSource(fd, offset, length);
+
+    status_t err = source->initCheck();
+
+    if (err != OK) {
+        return err;
+    }
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(source);
+
+    if (extractor == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    return setDataSource_l(extractor);
+}
+
+status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
+    reset_l();
+
+    bool haveAudio = false;
+    bool haveVideo = false;
+    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+        const char *mime;
+        CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+        if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
+            if (setVideoSource(extractor->getTrack(i)) == OK) {
+                haveVideo = true;
+            }
+        } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
+            if (setAudioSource(extractor->getTrack(i)) == OK) {
+                haveAudio = true;
+            }
+        }
+
+        if (haveAudio && haveVideo) {
+            break;
+        }
+    }
+
+    return !haveAudio && !haveVideo ? UNKNOWN_ERROR : OK;
+}
+
+void AwesomePlayer::reset() {
+    Mutex::Autolock autoLock(mLock);
+    reset_l();
+}
+
+void AwesomePlayer::reset_l() {
+    cancelPlayerEvents();
+
+    if (mLastVideoBuffer) {
+        mLastVideoBuffer->release();
+        mLastVideoBuffer = NULL;
+    }
+
+    if (mVideoBuffer) {
+        mVideoBuffer->release();
+        mVideoBuffer = NULL;
+    }
+
+    if (mVideoSource != NULL) {
+        mVideoSource->stop();
+        mVideoSource.clear();
+    }
+
+    mAudioSource.clear();
+
+    if (mTimeSource != mAudioPlayer) {
+        delete mTimeSource;
+    }
+    mTimeSource = NULL;
+
+    delete mAudioPlayer;
+    mAudioPlayer = NULL;
+
+    mVideoRenderer.clear();
+
+    mDurationUs = -1;
+    mFlags = 0;
+    mVideoWidth = mVideoHeight = -1;
+    mTimeSourceDeltaUs = 0;
+    mVideoTimeUs = 0;
+
+    mSeeking = false;
+    mSeekTimeUs = 0;
+}
+
+// static
+void AwesomePlayer::AudioNotify(void *_me, int what) {
+    AwesomePlayer *me = (AwesomePlayer *)_me;
+
+    Mutex::Autolock autoLock(me->mLock);
+
+    switch (what) {
+        case AudioPlayer::REACHED_EOS:
+            me->postStreamDoneEvent_l();
+            break;
+
+        case AudioPlayer::SEEK_COMPLETE:
+        {
+            if (me->mListener != NULL) {
+                me->mListener->sendEvent(MEDIA_SEEK_COMPLETE);
+            }
+
+            break;
+        }
+
+        default:
+            CHECK(!"should not be here.");
+            break;
+    }
+}
+
+void AwesomePlayer::onStreamDone() {
+    // Posted whenever any stream finishes playing.
+
+    Mutex::Autolock autoLock(mLock);
+    mStreamDoneEventPending = false;
+
+    if (mFlags & LOOPING) {
+        seekTo_l(0);
+
+        if (mVideoRenderer != NULL) {
+            postVideoEvent_l();
+        }
+    } else {
+        if (mListener != NULL) {
+            mListener->sendEvent(MEDIA_PLAYBACK_COMPLETE);
+        }
+
+        pause_l();
+    }
+}
+
+status_t AwesomePlayer::play() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mFlags & PLAYING) {
+        return OK;
+    }
+
+    mFlags |= PLAYING;
+    mFlags |= FIRST_FRAME;
+
+    if (mAudioSource != NULL) {
+        if (mAudioPlayer == NULL) {
+            if (mAudioSink != NULL) {
+                mAudioPlayer = new AudioPlayer(mAudioSink);
+
+                mAudioPlayer->setListenerCallback(
+                        &AwesomePlayer::AudioNotify, this);
+
+                mAudioPlayer->setSource(mAudioSource);
+                mAudioPlayer->start();
+
+                delete mTimeSource;
+                mTimeSource = mAudioPlayer;
+
+                // If there was a seek request while we were paused
+                // and we're just starting up again, honor the request now.
+                seekAudioIfNecessary_l();
+            }
+        } else {
+            mAudioPlayer->resume();
+        }
+    }
+
+    if (mTimeSource == NULL && mAudioPlayer == NULL) {
+        mTimeSource = new SystemTimeSource;
+    }
+
+    if (mVideoSource != NULL) {
+        if (mVideoRenderer == NULL) {
+            initRenderer_l();
+        }
+
+        if (mVideoRenderer != NULL) {
+            // Kick off video playback
+            postVideoEvent_l();
+        }
+    }
+
+    return OK;
+}
+
+void AwesomePlayer::initRenderer_l() {
+    if (mISurface != NULL) {
+        sp<MetaData> meta = mVideoSource->getFormat();
+
+        int32_t format;
+        const char *component;
+        int32_t decodedWidth, decodedHeight;
+        CHECK(meta->findInt32(kKeyColorFormat, &format));
+        CHECK(meta->findCString(kKeyDecoderComponent, &component));
+        CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
+        CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
+
+        mVideoRenderer =
+            mClient.interface()->createRenderer(
+                    mISurface, component,
+                    (OMX_COLOR_FORMATTYPE)format,
+                    decodedWidth, decodedHeight,
+                    mVideoWidth, mVideoHeight);
+    }
+}
+
+status_t AwesomePlayer::pause() {
+    Mutex::Autolock autoLock(mLock);
+    return pause_l();
+}
+
+status_t AwesomePlayer::pause_l() {
+    if (!(mFlags & PLAYING)) {
+        return OK;
+    }
+
+    cancelPlayerEvents();
+
+    if (mAudioPlayer != NULL) {
+        mAudioPlayer->pause();
+    }
+
+    mFlags &= ~PLAYING;
+
+    return OK;
+}
+
+bool AwesomePlayer::isPlaying() const {
+    Mutex::Autolock autoLock(mLock);
+
+    return mFlags & PLAYING;
+}
+
+void AwesomePlayer::setISurface(const sp<ISurface> &isurface) {
+    Mutex::Autolock autoLock(mLock);
+
+    mISurface = isurface;
+}
+
+void AwesomePlayer::setAudioSink(
+        const sp<MediaPlayerBase::AudioSink> &audioSink) {
+    Mutex::Autolock autoLock(mLock);
+
+    mAudioSink = audioSink;
+}
+
+status_t AwesomePlayer::setLooping(bool shouldLoop) {
+    Mutex::Autolock autoLock(mLock);
+
+    mFlags = mFlags & ~LOOPING;
+
+    if (shouldLoop) {
+        mFlags |= LOOPING;
+    }
+
+    return OK;
+}
+
+status_t AwesomePlayer::getDuration(int64_t *durationUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mDurationUs < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    *durationUs = mDurationUs;
+
+    return OK;
+}
+
+status_t AwesomePlayer::getPosition(int64_t *positionUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mVideoRenderer != NULL) {
+        *positionUs = mVideoTimeUs;
+    } else if (mAudioPlayer != NULL) {
+        *positionUs = mAudioPlayer->getMediaTimeUs();
+    } else {
+        *positionUs = 0;
+    }
+
+    return OK;
+}
+
+status_t AwesomePlayer::seekTo(int64_t timeUs) {
+    Mutex::Autolock autoLock(mLock);
+    return seekTo_l(timeUs);
+}
+
+status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
+    mSeeking = true;
+    mSeekTimeUs = timeUs;
+
+    seekAudioIfNecessary_l();
+
+    return OK;
+}
+
+void AwesomePlayer::seekAudioIfNecessary_l() {
+    if (mSeeking && mVideoRenderer == NULL && mAudioPlayer != NULL) {
+        mAudioPlayer->seekTo(mSeekTimeUs);
+
+        mSeeking = false;
+    }
+}
+
+status_t AwesomePlayer::getVideoDimensions(
+        int32_t *width, int32_t *height) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mVideoWidth < 0 || mVideoHeight < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    *width = mVideoWidth;
+    *height = mVideoHeight;
+
+    return OK;
+}
+
+status_t AwesomePlayer::setAudioSource(const sp<MediaSource> &source) {
+    if (source == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    mAudioSource = OMXCodec::Create(
+            mClient.interface(), source->getFormat(),
+            false, // createEncoder
+            source);
+
+    if (mAudioSource != NULL) {
+        int64_t durationUs;
+        if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+            if (mDurationUs < 0 || durationUs > mDurationUs) {
+                mDurationUs = durationUs;
+            }
+        }
+    }
+
+    return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
+}
+
+status_t AwesomePlayer::setVideoSource(const sp<MediaSource> &source) {
+    if (source == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    mVideoSource = OMXCodec::Create(
+            mClient.interface(), source->getFormat(),
+            false, // createEncoder
+            source);
+
+    if (mVideoSource != NULL) {
+        int64_t durationUs;
+        if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+            if (mDurationUs < 0 || durationUs > mDurationUs) {
+                mDurationUs = durationUs;
+            }
+        }
+
+        CHECK(source->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
+        CHECK(source->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
+
+        mVideoSource->start();
+    }
+
+    return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
+}
+
+void AwesomePlayer::onEvent(int32_t code) {
+    if (code == 1) {
+        onStreamDone();
+        return;
+    }
+
+    Mutex::Autolock autoLock(mLock);
+    mVideoEventPending = false;
+
+    if (mSeeking) {
+        if (mLastVideoBuffer) {
+            mLastVideoBuffer->release();
+            mLastVideoBuffer = NULL;
+        }
+
+        if (mVideoBuffer) {
+            mVideoBuffer->release();
+            mVideoBuffer = NULL;
+        }
+    }
+
+    if (!mVideoBuffer) {
+        MediaSource::ReadOptions options;
+        if (mSeeking) {
+            LOGV("seeking to %lld us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
+
+            options.setSeekTo(mSeekTimeUs);
+        }
+        for (;;) {
+            status_t err = mVideoSource->read(&mVideoBuffer, &options);
+
+            if (err != OK) {
+                CHECK_EQ(mVideoBuffer, NULL);
+
+                if (err == INFO_FORMAT_CHANGED) {
+                    LOGV("VideoSource signalled format change.");
+
+                    initRenderer_l();
+                    continue;
+                }
+
+                postStreamDoneEvent_l();
+                return;
+            }
+
+            break;
+        }
+    }
+
+    int64_t timeUs;
+    CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+    mVideoTimeUs = timeUs;
+
+    if (mSeeking) {
+        if (mAudioPlayer != NULL) {
+            LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
+
+            mAudioPlayer->seekTo(timeUs);
+        } else {
+            // If we're playing video only, report seek complete now,
+            // otherwise audio player will notify us later.
+            if (mListener != NULL) {
+                mListener->sendEvent(MEDIA_SEEK_COMPLETE);
+            }
+        }
+
+        mFlags |= FIRST_FRAME;
+        mSeeking = false;
+    }
+
+    if (mFlags & FIRST_FRAME) {
+        mFlags &= ~FIRST_FRAME;
+
+        mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - timeUs;
+    }
+
+    int64_t realTimeUs, mediaTimeUs;
+    if (mAudioPlayer != NULL
+        && mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
+        mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
+    }
+
+    int64_t nowUs = mTimeSource->getRealTimeUs() - mTimeSourceDeltaUs;
+
+    int64_t latenessUs = nowUs - timeUs;
+
+    if (latenessUs > 20000) {
+        // We're more than 20ms late.
+        LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
+
+        mVideoBuffer->release();
+        mVideoBuffer = NULL;
+
+        postVideoEvent_l();
+        return;
+    }
+
+    if (latenessUs < -10000) {
+        // We're more than 10ms early.
+
+        postVideoEvent_l(10000);
+        return;
+    }
+
+    void *id;
+    if (mVideoBuffer->meta_data()->findPointer(kKeyBufferID, &id)) {
+        mVideoRenderer->render((IOMX::buffer_id)id);
+    }
+
+    if (mLastVideoBuffer) {
+        mLastVideoBuffer->release();
+        mLastVideoBuffer = NULL;
+    }
+    mLastVideoBuffer = mVideoBuffer;
+    mVideoBuffer = NULL;
+
+    postVideoEvent_l();
+}
+
+void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {
+    if (mVideoEventPending) {
+        return;
+    }
+
+    mVideoEventPending = true;
+    mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void AwesomePlayer::postStreamDoneEvent_l() {
+    if (mStreamDoneEventPending) {
+        return;
+    }
+    mStreamDoneEventPending = true;
+    mQueue.postEvent(mStreamDoneEvent);
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 19a1f85..9d3deb7 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -25,10 +25,10 @@
 
 #include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
 #include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
-#include <media/stagefright/MmapSource.h>
 #include <utils/String8.h>
 
 namespace android {
@@ -70,13 +70,13 @@
         const char *uri, const char *mime) {
     sp<DataSource> source;
     if (!strncasecmp("file://", uri, 7)) {
-        source = new MmapSource(uri + 7);
+        source = new FileSource(uri + 7);
     } else if (!strncasecmp("http://", uri, 7)) {
         source = new HTTPDataSource(uri);
         source = new CachingDataSource(source, 64 * 1024, 10);
     } else {
         // Assume it's a filename.
-        source = new MmapSource(uri);
+        source = new FileSource(uri);
     }
 
     if (source == NULL || source->initCheck() != OK) {
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
deleted file mode 100644
index c1044a3..0000000
--- a/media/libstagefright/MediaPlayerImpl.cpp
+++ /dev/null
@@ -1,658 +0,0 @@
-/*
- * Copyright (C) 2009 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 "MediaPlayerImpl"
-#include "utils/Log.h"
-
-#include "include/stagefright_string.h"
-#include "include/HTTPStream.h"
-
-#include <OMX_Component.h>
-
-#include <unistd.h>
-
-#include <media/stagefright/AudioPlayer.h>
-// #include <media/stagefright/CameraSource.h>
-#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaExtractor.h>
-#include <media/stagefright/MediaPlayerImpl.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MmapSource.h>
-#include <media/stagefright/OMXCodec.h>
-#include <media/stagefright/ShoutcastSource.h>
-#include <media/stagefright/TimeSource.h>
-#include <ui/PixelFormat.h>
-#include <ui/Surface.h>
-
-namespace android {
-
-MediaPlayerImpl::MediaPlayerImpl(const char *uri)
-    : mInitCheck(NO_INIT),
-      mTimeSource(NULL),
-      mAudioPlayer(NULL),
-      mVideoWidth(0),
-      mVideoHeight(0),
-      mVideoPosition(0),
-      mDuration(0),
-      mPlaying(false),
-      mPaused(false),
-      mSeeking(false) {
-    LOGV("MediaPlayerImpl(%s)", uri);
-    DataSource::RegisterDefaultSniffers();
-
-    status_t err = mClient.connect();
-    if (err != OK) {
-        LOGE("Failed to connect to OMXClient.");
-        return;
-    }
-
-    if (!strncasecmp("shoutcast://", uri, 12)) {
-        setAudioSource(makeShoutcastSource(uri));
-#if 0
-    } else if (!strncasecmp("camera:", uri, 7)) {
-        mVideoWidth = 480;
-        mVideoHeight = 320;
-        mVideoDecoder = CameraSource::Create();
-#endif
-    } else {
-        mExtractor = MediaExtractor::CreateFromURI(uri);
-
-        if (mExtractor == NULL) {
-            return;
-        }
-    }
-
-    init();
-
-    mInitCheck = OK;
-}
-
-MediaPlayerImpl::MediaPlayerImpl(int fd, int64_t offset, int64_t length)
-    : mInitCheck(NO_INIT),
-      mTimeSource(NULL),
-      mAudioPlayer(NULL),
-      mVideoWidth(0),
-      mVideoHeight(0),
-      mVideoPosition(0),
-      mDuration(0),
-      mPlaying(false),
-      mPaused(false),
-      mSeeking(false) {
-    LOGV("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length);
-    DataSource::RegisterDefaultSniffers();
-
-    status_t err = mClient.connect();
-    if (err != OK) {
-        LOGE("Failed to connect to OMXClient.");
-        return;
-    }
-
-    mExtractor = MediaExtractor::Create(
-            new MmapSource(fd, offset, length));
-
-    if (mExtractor == NULL) {
-        return;
-    }
-
-    init();
-
-    mInitCheck = OK;
-}
-
-status_t MediaPlayerImpl::initCheck() const {
-    return mInitCheck;
-}
-
-MediaPlayerImpl::~MediaPlayerImpl() {
-    stop();
-    setSurface(NULL);
-
-    if (mInitCheck == OK) {
-        mClient.disconnect();
-    }
-
-    LOGV("~MediaPlayerImpl done.");
-}
-
-void MediaPlayerImpl::play() {
-    LOGV("play");
-
-    if (mPlaying) {
-        if (mPaused) {
-            if (mAudioSource != NULL) {
-                mAudioPlayer->resume();
-            }
-            mPaused = false;
-        }
-        return;
-    }
-
-    mPlaying = true;
-
-    if (mAudioSource != NULL) {
-        mAudioPlayer = new AudioPlayer(mAudioSink);
-        mAudioPlayer->setSource(mAudioDecoder);
-
-        if (mVideoDecoder == NULL) {
-            // If there is no video, start playing right away,
-            // otherwise we'll start the audio player after we decode
-            // the first video frame, this way we won't be behind right
-            // away.
-            mAudioPlayer->start();
-        }
-
-        mTimeSource = mAudioPlayer;
-    } else {
-        mTimeSource = new SystemTimeSource;
-    }
-
-    if (mVideoDecoder != NULL) {
-        pthread_attr_t attr;
-        pthread_attr_init(&attr);
-        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
-        pthread_create(&mVideoThread, &attr, VideoWrapper, this);
-
-        pthread_attr_destroy(&attr);
-    }
-}
-
-void MediaPlayerImpl::pause() {
-    if (!mPlaying || mPaused) {
-        return;
-    }
-
-    if (mAudioSource != NULL) {
-        mAudioPlayer->pause();
-    }
-
-    mPaused = true;
-}
-
-void MediaPlayerImpl::stop() {
-    if (!mPlaying) {
-        return;
-    }
-
-    mPlaying = false;
-
-    if (mVideoDecoder != NULL) {
-        void *dummy;
-        pthread_join(mVideoThread, &dummy);
-    }
-
-    if (mAudioSource != NULL) {
-        mAudioPlayer->stop();
-
-        delete mAudioPlayer;
-        mAudioPlayer = NULL;
-    } else {
-        delete mTimeSource;
-    }
-
-    mTimeSource = NULL;
-}
-
-// static
-void *MediaPlayerImpl::VideoWrapper(void *me) {
-    ((MediaPlayerImpl *)me)->videoEntry();
-
-    return NULL;
-}
-
-void MediaPlayerImpl::videoEntry() {
-    bool firstFrame = true;
-    bool eof = false;
-
-    status_t err = mVideoDecoder->start();
-    CHECK_EQ(err, OK);
-
-    while (mPlaying) {
-        MediaBuffer *buffer;
-
-        MediaSource::ReadOptions options;
-        bool seeking = false;
-
-        {
-            Mutex::Autolock autoLock(mLock);
-            if (mSeeking) {
-                LOGV("seek-options to %lld", mSeekTimeUs);
-                options.setSeekTo(mSeekTimeUs);
-
-                mSeeking = false;
-                seeking = true;
-                eof = false;
-            }
-        }
-
-        if (eof || mPaused) {
-            usleep(100000);
-            continue;
-        }
-
-        status_t err = mVideoDecoder->read(&buffer, &options);
-        CHECK((err == OK && buffer != NULL) || (err != OK && buffer == NULL));
-
-        if (err == INFO_FORMAT_CHANGED) {
-            LOGV("format changed.");
-            depopulateISurface();
-            populateISurface();
-            continue;
-        }
-
-        if (err == ERROR_END_OF_STREAM || err != OK) {
-            eof = true;
-            continue;
-        }
-
-        if (buffer->range_length() == 0) {
-            // The final buffer is empty.
-            buffer->release();
-            continue;
-        }
-
-        int64_t pts_us;
-        CHECK(buffer->meta_data()->findInt64(kKeyTime, &pts_us));
-
-        {
-            Mutex::Autolock autoLock(mLock);
-            mVideoPosition = pts_us;
-
-            LOGV("now_video = %.2f secs (%lld ms)",
-                 pts_us / 1E6, (pts_us + 500) / 1000);
-        }
-
-        if (seeking && mAudioPlayer != NULL) {
-            // Now that we know where exactly video seeked (taking sync-samples
-            // into account), we will seek the audio track to the same time.
-            mAudioPlayer->seekTo(pts_us);
-        }
-
-        if (firstFrame || seeking) {
-            if (firstFrame && mAudioPlayer != NULL) {
-                // We've deferred starting the audio player until now.
-                mAudioPlayer->start();
-            }
-            mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - pts_us;
-            firstFrame = false;
-        }
-
-        displayOrDiscardFrame(buffer, pts_us);
-    }
-
-    mVideoDecoder->stop();
-}
-
-void MediaPlayerImpl::displayOrDiscardFrame(
-        MediaBuffer *buffer, int64_t pts_us) {
-    for (;;) {
-        if (!mPlaying || mPaused) {
-            buffer->release();
-            buffer = NULL;
-
-            return;
-        }
-
-        int64_t realtime_us, mediatime_us;
-        if (mAudioPlayer != NULL
-            && mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) {
-            mTimeSourceDeltaUs = realtime_us - mediatime_us;
-            LOGV("mTimeSourceDeltaUs = %.2f secs", mTimeSourceDeltaUs / 1E6);
-        }
-
-        int64_t now_us = mTimeSource->getRealTimeUs();
-        now_us -= mTimeSourceDeltaUs;
-
-        int64_t delay_us = pts_us - now_us;
-
-        if (delay_us < -15000) {
-            // We're late.
-
-            LOGV("we're late by %lld ms, dropping a frame\n",
-                 -delay_us / 1000);
-
-            buffer->release();
-            buffer = NULL;
-            return;
-        } else if (delay_us > 100000) {
-            LOGV("we're much too early (by %lld ms)\n",
-                 delay_us / 1000);
-            usleep(100000);
-            continue;
-        } else if (delay_us > 0) {
-            usleep(delay_us);
-        }
-
-        break;
-    }
-
-    {
-        Mutex::Autolock autoLock(mLock);
-        if (mVideoRenderer.get() != NULL) {
-            sendFrameToISurface(buffer);
-        }
-    }
-
-    buffer->release();
-    buffer = NULL;
-}
-
-void MediaPlayerImpl::init() {
-    if (mExtractor != NULL) {
-        size_t num_tracks = mExtractor->countTracks();
-
-        mDuration = 0;
-
-        for (size_t i = 0; i < num_tracks; ++i) {
-            const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
-            CHECK(meta != NULL);
-
-            const char *mime;
-            if (!meta->findCString(kKeyMIMEType, &mime)) {
-                continue;
-            }
-
-            bool is_audio = false;
-            bool is_acceptable = false;
-            if (!strncasecmp(mime, "audio/", 6)) {
-                is_audio = true;
-                is_acceptable = (mAudioSource == NULL);
-            } else if (!strncasecmp(mime, "video/", 6)) {
-                is_acceptable = (mVideoSource == NULL);
-            }
-
-            if (!is_acceptable) {
-                continue;
-            }
-
-            sp<MediaSource> source = mExtractor->getTrack(i);
-
-            int64_t durationUs;
-            if (meta->findInt64(kKeyDuration, &durationUs)) {
-                if (durationUs > mDuration) {
-                    mDuration = durationUs;
-                }
-            }
-
-            if (is_audio) {
-                setAudioSource(source);
-            } else {
-                setVideoSource(source);
-            }
-        }
-    }
-}
-
-void MediaPlayerImpl::setAudioSource(const sp<MediaSource> &source) {
-    LOGV("setAudioSource");
-    mAudioSource = source;
-
-    sp<MetaData> meta = source->getFormat();
-
-    const char *mime;
-    CHECK(meta->findCString(kKeyMIMEType, &mime));
-
-    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
-        mAudioDecoder = source;
-    } else {
-        mAudioDecoder = OMXCodec::Create(
-                mClient.interface(), meta, false /* createEncoder */, source);
-    }
-}
-
-void MediaPlayerImpl::setVideoSource(const sp<MediaSource> &source) {
-    LOGV("setVideoSource");
-    mVideoSource = source;
-
-    sp<MetaData> meta = source->getFormat();
-
-    bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
-    CHECK(success);
-
-    success = meta->findInt32(kKeyHeight, &mVideoHeight);
-    CHECK(success);
-
-    mVideoDecoder = OMXCodec::Create(
-            mClient.interface(), meta, false /* createEncoder */, source);
-
-    if (mISurface.get() != NULL || mSurface.get() != NULL) {
-        depopulateISurface();
-        populateISurface();
-    }
-}
-
-void MediaPlayerImpl::setSurface(const sp<Surface> &surface) {
-    LOGV("setSurface %p", surface.get());
-    Mutex::Autolock autoLock(mLock);
-
-    depopulateISurface();
-
-    mSurface = surface;
-    mISurface = NULL;
-
-    if (mSurface.get() != NULL) {
-        populateISurface();
-    }
-}
-
-void MediaPlayerImpl::setISurface(const sp<ISurface> &isurface) {
-    LOGV("setISurface %p", isurface.get());
-    Mutex::Autolock autoLock(mLock);
-
-    depopulateISurface();
-
-    mSurface = NULL;
-    mISurface = isurface;
-
-    if (mISurface.get() != NULL) {
-        populateISurface();
-    }
-}
-
-MediaSource *MediaPlayerImpl::makeShoutcastSource(const char *uri) {
-    if (strncasecmp(uri, "shoutcast://", 12)) {
-        return NULL;
-    }
-
-    string host;
-    string path;
-    int port;
-
-    char *slash = strchr(uri + 12, '/');
-    if (slash == NULL) {
-        host = uri + 12;
-        path = "/";
-    } else {
-        host = string(uri + 12, slash - (uri + 12));
-        path = slash;
-    }
-
-    char *colon = strchr(host.c_str(), ':');
-    if (colon == NULL) {
-        port = 80;
-    } else {
-        char *end;
-        long tmp = strtol(colon + 1, &end, 10);
-        CHECK(end > colon + 1);
-        CHECK(tmp > 0 && tmp < 65536);
-        port = tmp;
-
-        host = string(host, 0, colon - host.c_str());
-    }
-
-    LOGV("Connecting to host '%s', port %d, path '%s'",
-         host.c_str(), port, path.c_str());
-
-    HTTPStream *http = new HTTPStream;
-    int http_status;
-
-    for (;;) {
-        status_t err = http->connect(host.c_str(), port);
-        CHECK_EQ(err, OK);
-
-        err = http->send("GET ");
-        err = http->send(path.c_str());
-        err = http->send(" HTTP/1.1\r\n");
-        err = http->send("Host: ");
-        err = http->send(host.c_str());
-        err = http->send("\r\n");
-        err = http->send("Icy-MetaData: 1\r\n\r\n");
-
-        CHECK_EQ(OK, http->receive_header(&http_status));
-
-        if (http_status == 301 || http_status == 302) {
-            string location;
-            CHECK(http->find_header_value("Location", &location));
-
-            CHECK(string(location, 0, 7) == "http://");
-            location.erase(0, 7);
-            string::size_type slashPos = location.find('/');
-            if (slashPos == string::npos) {
-                slashPos = location.size();
-                location += '/';
-            }
-
-            http->disconnect();
-
-            LOGV("Redirecting to %s\n", location.c_str());
-
-            host = string(location, 0, slashPos);
-
-            string::size_type colonPos = host.find(':');
-            if (colonPos != string::npos) {
-                const char *start = host.c_str() + colonPos + 1;
-                char *end;
-                long tmp = strtol(start, &end, 10);
-                CHECK(end > start && (*end == '\0'));
-
-                port = (tmp >= 0 && tmp < 65536) ? (int)tmp : 80;
-            } else {
-                port = 80;
-            }
-
-            path = string(location, slashPos);
-
-            continue;
-        }
-
-        break;
-    }
-
-    if (http_status != 200) {
-        LOGE("Connection failed: http_status = %d", http_status);
-        return NULL;
-    }
-
-    MediaSource *source = new ShoutcastSource(http);
-
-    return source;
-}
-
-bool MediaPlayerImpl::isPlaying() const {
-    return mPlaying && !mPaused;
-}
-
-int64_t MediaPlayerImpl::getDuration() {
-    return mDuration;
-}
-
-int64_t MediaPlayerImpl::getPosition() {
-    int64_t position = 0;
-    if (mVideoSource != NULL) {
-        Mutex::Autolock autoLock(mLock);
-        position = mVideoPosition;
-    } else if (mAudioPlayer != NULL) {
-        position = mAudioPlayer->getMediaTimeUs();
-    }
-
-    return position;
-}
-
-status_t MediaPlayerImpl::seekTo(int64_t time) {
-    LOGV("seekTo %lld", time);
-
-    if (mPaused) {
-        return UNKNOWN_ERROR;
-    }
-
-    if (mVideoSource == NULL && mAudioPlayer != NULL) {
-        mAudioPlayer->seekTo(time);
-    } else {
-        Mutex::Autolock autoLock(mLock);
-        mSeekTimeUs = time;
-        mSeeking = true;
-    }
-
-    return OK;
-}
-
-void MediaPlayerImpl::populateISurface() {
-    if (mVideoSource == NULL) {
-        return;
-    }
-
-    sp<MetaData> meta = mVideoDecoder->getFormat();
-
-    int32_t format;
-    const char *component;
-    int32_t decodedWidth, decodedHeight;
-    bool success = meta->findInt32(kKeyColorFormat, &format);
-    success = success && meta->findCString(kKeyDecoderComponent, &component);
-    success = success && meta->findInt32(kKeyWidth, &decodedWidth);
-    success = success && meta->findInt32(kKeyHeight, &decodedHeight);
-    CHECK(success);
-
-    LOGV("mVideoWidth=%d, mVideoHeight=%d, decodedWidth=%d, decodedHeight=%d",
-         mVideoWidth, mVideoHeight, decodedWidth, decodedHeight);
-
-    if (mSurface.get() != NULL) {
-        mVideoRenderer =
-            mClient.interface()->createRenderer(
-                    mSurface, component,
-                    (OMX_COLOR_FORMATTYPE)format,
-                    decodedWidth, decodedHeight,
-                    mVideoWidth, mVideoHeight);
-    } else {
-        mVideoRenderer =
-            mClient.interface()->createRenderer(
-                    mISurface, component,
-                    (OMX_COLOR_FORMATTYPE)format,
-                    decodedWidth, decodedHeight,
-                    mVideoWidth, mVideoHeight);
-    }
-}
-
-void MediaPlayerImpl::depopulateISurface() {
-    mVideoRenderer.clear();
-}
-
-void MediaPlayerImpl::sendFrameToISurface(MediaBuffer *buffer) {
-    void *id;
-    if (buffer->meta_data()->findPointer(kKeyBufferID, &id)) {
-        mVideoRenderer->render((IOMX::buffer_id)id);
-    }
-}
-
-void MediaPlayerImpl::setAudioSink(
-        const sp<MediaPlayerBase::AudioSink> &audioSink) {
-    LOGV("setAudioSink %p", audioSink.get());
-    mAudioSink = audioSink;
-}
-
-}  // namespace android
-
diff --git a/media/libstagefright/MmapSource.cpp b/media/libstagefright/MmapSource.cpp
deleted file mode 100644
index 42749cf..0000000
--- a/media/libstagefright/MmapSource.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2009 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 "MmapSource"
-#include <utils/Log.h>
-
-#include <sys/mman.h>
-
-#include <fcntl.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/MmapSource.h>
-
-namespace android {
-
-MmapSource::MmapSource(const char *filename)
-    : mFd(open(filename, O_RDONLY)),
-      mBase(NULL),
-      mSize(0) {
-    LOGV("MmapSource '%s'", filename);
-
-    if (mFd < 0) {
-        return;
-    }
-
-    off_t size = lseek(mFd, 0, SEEK_END);
-    mSize = (size_t)size;
-
-    mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, 0);
-
-    if (mBase == (void *)-1) {
-        mBase = NULL;
-
-        close(mFd);
-        mFd = -1;
-    }
-}
-
-MmapSource::MmapSource(int fd, int64_t offset, int64_t length)
-    : mFd(fd),
-      mBase(NULL),
-      mSize(length) {
-    LOGV("MmapSource fd:%d offset:%lld length:%lld", fd, offset, length);
-    CHECK(fd >= 0);
-
-    mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, offset);
-
-    if (mBase == (void *)-1) {
-        mBase = NULL;
-
-        close(mFd);
-        mFd = -1;
-    }
-
-}
-
-MmapSource::~MmapSource() {
-    if (mFd != -1) {
-        munmap(mBase, mSize);
-        mBase = NULL;
-        mSize = 0;
-
-        close(mFd);
-        mFd = -1;
-    }
-}
-
-status_t MmapSource::initCheck() const {
-    return mFd == -1 ? NO_INIT : OK;
-}
-
-ssize_t MmapSource::readAt(off_t offset, void *data, size_t size) {
-    LOGV("readAt offset:%ld data:%p size:%d", offset, data, size);
-    CHECK(offset >= 0);
-
-    size_t avail = 0;
-    if (offset >= 0 && offset < (off_t)mSize) {
-        avail = mSize - offset;
-    }
-
-    if (size > avail) {
-        size = avail;
-    }
-
-    memcpy(data, (const uint8_t *)mBase + offset, size);
-
-    return (ssize_t)size;
-}
-
-status_t MmapSource::getSize(off_t *size) {
-    *size = mSize;
-
-    return OK;
-}
-
-}  // namespace android
-
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index d36653e..9297aff0 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -30,7 +30,6 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MetaData.h>
-#include <media/stagefright/MmapSource.h>
 #include <media/stagefright/OMXCodec.h>
 #include <media/stagefright/Utils.h>
 #include <utils/Vector.h>
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index fa68771..d079e70 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -135,7 +135,9 @@
 }
 
 bool TimedEventQueue::cancelEvent(event_id id) {
-    CHECK(id != 0);
+    if (id == 0) {
+        return false;
+    }
 
     cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
 
@@ -162,6 +164,7 @@
             mQueueHeadChangedCondition.signal();
         }
 
+        (*it).event->setEventID(0);
         it = mQueue.erase(it);
 
         if (stopAfterFirstMatch) {
@@ -228,7 +231,12 @@
                 }
             }
 
+            if (mQueue.empty()) {
+                continue;
+            }
+
             event = (*it).event;
+            event->setEventID(0);
             mQueue.erase(it);
         }
 
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
index debf006..1972a1c 100644
--- a/media/libstagefright/include/AMRExtractor.h
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -32,14 +32,14 @@
     virtual sp<MediaSource> getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
-    static sp<MetaData> makeAMRFormat(bool isWide);
-
 protected:
     virtual ~AMRExtractor();
 
 private:
     sp<DataSource> mDataSource;
+    sp<MetaData> mMeta;
     status_t mInitCheck;
+    size_t mFrameSize;
     bool mIsWide;
 
     AMRExtractor(const AMRExtractor &);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
new file mode 100644
index 0000000..2727c3c
--- /dev/null
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2009 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 AWESOME_PLAYER_H_
+
+#define AWESOME_PLAYER_H_
+
+#include "TimedEventQueue.h"
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/OMXClient.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct MediaBuffer;
+struct MediaExtractor;
+struct MediaSource;
+struct AudioPlayer;
+struct TimeSource;
+
+struct AwesomePlayer {
+    AwesomePlayer();
+    ~AwesomePlayer();
+
+    void setListener(const sp<MediaPlayerBase> &listener);
+
+    status_t setDataSource(const char *uri);
+    status_t setDataSource(int fd, int64_t offset, int64_t length);
+
+    void reset();
+
+    status_t play();
+    status_t pause();
+
+    bool isPlaying() const;
+
+    void setISurface(const sp<ISurface> &isurface);
+    void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
+    status_t setLooping(bool shouldLoop);
+
+    status_t getDuration(int64_t *durationUs);
+    status_t getPosition(int64_t *positionUs);
+
+    status_t seekTo(int64_t timeUs);
+
+    status_t getVideoDimensions(int32_t *width, int32_t *height) const;
+
+private:
+    friend struct AwesomeEvent;
+
+    enum Flags {
+        PLAYING     = 1,
+        LOOPING     = 2,
+        FIRST_FRAME = 4,
+    };
+
+    mutable Mutex mLock;
+
+    OMXClient mClient;
+    TimedEventQueue mQueue;
+    sp<MediaPlayerBase> mListener;
+
+    sp<ISurface> mISurface;
+    sp<MediaPlayerBase::AudioSink> mAudioSink;
+
+    TimeSource *mTimeSource;
+
+    sp<MediaSource> mVideoSource;
+    sp<IOMXRenderer> mVideoRenderer;
+
+    sp<MediaSource> mAudioSource;
+    AudioPlayer *mAudioPlayer;
+    int64_t mDurationUs;
+
+    uint32_t mFlags;
+
+    int32_t mVideoWidth, mVideoHeight;
+    int64_t mTimeSourceDeltaUs;
+    int64_t mVideoTimeUs;
+
+    bool mSeeking;
+    int64_t mSeekTimeUs;
+
+    sp<TimedEventQueue::Event> mVideoEvent;
+    bool mVideoEventPending;
+    sp<TimedEventQueue::Event> mStreamDoneEvent;
+    bool mStreamDoneEventPending;
+
+    void postVideoEvent_l(int64_t delayUs = -1);
+    void postStreamDoneEvent_l();
+
+    MediaBuffer *mLastVideoBuffer;
+    MediaBuffer *mVideoBuffer;
+
+    status_t setDataSource_l(const sp<MediaExtractor> &extractor);
+    void reset_l();
+    status_t seekTo_l(int64_t timeUs);
+    status_t pause_l();
+    void initRenderer_l();
+    void seekAudioIfNecessary_l();
+
+    void cancelPlayerEvents();
+
+    status_t setAudioSource(const sp<MediaSource> &source);
+    status_t setVideoSource(const sp<MediaSource> &source);
+
+    void onEvent(int32_t code);
+
+    static void AudioNotify(void *me, int what);
+    void onStreamDone();
+
+    AwesomePlayer(const AwesomePlayer &);
+    AwesomePlayer &operator=(const AwesomePlayer &);
+};
+
+}  // namespace android
+
+#endif  // AWESOME_PLAYER_H_
+
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 58fa69e..38ccde0 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -43,6 +43,8 @@
 
     private static final int BIT_HEADSET = (1 << 0);
     private static final int BIT_HEADSET_NO_MIC = (1 << 1);
+    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC);
+    private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
 
     private int mHeadsetState;
     private int mPrevHeadsetState;
@@ -100,68 +102,76 @@
 
     private synchronized final void update(String newName, int newState) {
         // Retain only relevant bits
-        int headsetState = newState & (BIT_HEADSET|BIT_HEADSET_NO_MIC);
+        int headsetState = newState & SUPPORTED_HEADSETS;
+        int newOrOld = headsetState | mHeadsetState;
+        // reject all suspect transitions: only accept state changes from:
+        // - a: 0 heaset to 1 headset
+        // - b: 1 headset to 0 headset
+        if (mHeadsetState == headsetState || ((newOrOld & (newOrOld - 1)) != 0)) {
+            return;
+        }
 
-        if (headsetState != mHeadsetState) {
-            boolean isUnplug = false;
-            if (((mHeadsetState & BIT_HEADSET) != 0 && (headsetState & BIT_HEADSET) == 0) ||
-                ((mHeadsetState & BIT_HEADSET_NO_MIC) != 0 && (headsetState & BIT_HEADSET_NO_MIC) == 0)) {
-                isUnplug = true;
-            }
-            mHeadsetName = newName;
-            mPrevHeadsetState = mHeadsetState;
-            mHeadsetState = headsetState;
-            mPendingIntent = true;
+        mHeadsetName = newName;
+        mPrevHeadsetState = mHeadsetState;
+        mHeadsetState = headsetState;
+        mPendingIntent = true;
 
-            if (isUnplug) {
-                Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
-                mContext.sendBroadcast(intent);
+        if (headsetState == 0) {
+            Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+            mContext.sendBroadcast(intent);
 
-                // It can take hundreds of ms flush the audio pipeline after
-                // apps pause audio playback, but audio route changes are
-                // immediate, so delay the route change by 1000ms.
-                // This could be improved once the audio sub-system provides an
-                // interface to clear the audio pipeline.
-                mWakeLock.acquire();
-                mHandler.sendEmptyMessageDelayed(0, 1000);
-            } else {
-                sendIntent();
-                mPendingIntent = false;
+            // It can take hundreds of ms flush the audio pipeline after
+            // apps pause audio playback, but audio route changes are
+            // immediate, so delay the route change by 1000ms.
+            // This could be improved once the audio sub-system provides an
+            // interface to clear the audio pipeline.
+            mWakeLock.acquire();
+            mHandler.sendEmptyMessageDelayed(0, 1000);
+        } else {
+            sendIntents();
+            mPendingIntent = false;
+        }
+    }
+
+    private synchronized final void sendIntents() {
+        int allHeadsets = SUPPORTED_HEADSETS;
+        for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
+            if ((curHeadset & allHeadsets) != 0) {
+                sendIntent(curHeadset);
+                allHeadsets &= ~curHeadset;
             }
         }
     }
 
-    private synchronized final void sendIntent() {
-        //  Pack up the values and broadcast them to everyone
-        Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        int state = 0;
-        int microphone = 0;
+    private final void sendIntent(int headset) {
+        if ((mHeadsetState & headset) != (mPrevHeadsetState & headset)) {
+            //  Pack up the values and broadcast them to everyone
+            Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            int state = 0;
+            int microphone = 0;
 
-        if ((mHeadsetState & BIT_HEADSET) != (mPrevHeadsetState & BIT_HEADSET)) {
-            microphone = 1;
-            if ((mHeadsetState & BIT_HEADSET) != 0) {
+            if ((headset & HEADSETS_WITH_MIC) != 0) {
+                microphone = 1;
+            }
+            if ((mHeadsetState & headset) != 0) {
                 state = 1;
             }
-        } else if ((mHeadsetState & BIT_HEADSET_NO_MIC) != (mPrevHeadsetState & BIT_HEADSET_NO_MIC)) {
-            if ((mHeadsetState & BIT_HEADSET_NO_MIC) != 0) {
-                state = 1;
-            }
+            intent.putExtra("state", state);
+            intent.putExtra("name", mHeadsetName);
+            intent.putExtra("microphone", microphone);
+
+            if (LOG) Log.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+mHeadsetName+" mic: "+microphone);
+            // TODO: Should we require a permission?
+            ActivityManagerNative.broadcastStickyIntent(intent, null);
         }
-
-        intent.putExtra("state", state);
-        intent.putExtra("name", mHeadsetName);
-        intent.putExtra("microphone", microphone);
-
-        // TODO: Should we require a permission?
-        ActivityManagerNative.broadcastStickyIntent(intent, null);
     }
 
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             if (mPendingIntent) {
-                sendIntent();
+                sendIntents();
                 mPendingIntent = false;
             }
             mWakeLock.release();
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index a838e89..feab7d2 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -209,7 +209,9 @@
     private boolean mLightSensorEnabled;
     private float mLightSensorValue = -1;
     private float mLightSensorPendingValue = -1;
-    private int mLightSensorBrightness = -1;
+    private int mLightSensorScreenBrightness = -1;
+    private int mLightSensorButtonBrightness = -1;
+    private int mLightSensorKeyboardBrightness = -1;
     private boolean mDimScreen = true;
     private long mNextTimeout;
     private volatile int mPokey = 0;
@@ -220,6 +222,7 @@
     private long mLastScreenOnTime;
     private boolean mPreventScreenOn;
     private int mScreenBrightnessOverride = -1;
+    private int mButtonBrightnessOverride = -1;
     private boolean mUseSoftwareAutoBrightness;
     private boolean mAutoBrightessEnabled;
     private int[] mAutoBrightnessLevels;
@@ -919,7 +922,8 @@
         pw.println("  mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay
                 + " mScreenOffDelay=" + mScreenOffDelay);
         pw.println("  mPreventScreenOn=" + mPreventScreenOn
-                + "  mScreenBrightnessOverride=" + mScreenBrightnessOverride);
+                + "  mScreenBrightnessOverride=" + mScreenBrightnessOverride
+                + "  mButtonBrightnessOverride=" + mButtonBrightnessOverride);
         pw.println("  mTotalDelaySetting=" + mTotalDelaySetting);
         pw.println("  mLastScreenOnTime=" + mLastScreenOnTime);
         pw.println("  mBroadcastWakeLock=" + mBroadcastWakeLock);
@@ -933,8 +937,11 @@
         pw.println("  mProximityPendingValue=" + mProximityPendingValue);
         pw.println("  mLastProximityEventTime=" + mLastProximityEventTime);
         pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
-        pw.println("  mLightSensorValue=" + mLightSensorValue);
-        pw.println("  mLightSensorPendingValue=" + mLightSensorPendingValue);
+        pw.println("  mLightSensorValue=" + mLightSensorValue
+                + " mLightSensorPendingValue=" + mLightSensorPendingValue);
+        pw.println("  mLightSensorScreenBrightness=" + mLightSensorScreenBrightness
+                + " mLightSensorButtonBrightness=" + mLightSensorButtonBrightness
+                + " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness);
         pw.println("  mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness);
         pw.println("  mAutoBrightessEnabled=" + mAutoBrightessEnabled);
         mScreenBrightness.dump(pw, "  mScreenBrightness: ");
@@ -1302,7 +1309,18 @@
             }
         }
     }
-    
+
+    public void setButtonBrightnessOverride(int brightness) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+         synchronized (mLocks) {
+           if (mButtonBrightnessOverride != brightness) {
+                mButtonBrightnessOverride = brightness;
+                updateLightsLocked(mPowerState, BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT);
+            }
+        }
+    }
+
     /**
      * Sanity-check that gets called 5 seconds after any call to
      * preventScreenOn(true).  This ensures that the original call
@@ -1451,8 +1469,7 @@
                         err = setScreenStateLocked(true);
                         long identity = Binder.clearCallingIdentity();
                         try {
-                            mBatteryStats.noteScreenBrightness(
-                                    getPreferredBrightness());
+                            mBatteryStats.noteScreenBrightness(getPreferredBrightness());
                             mBatteryStats.noteScreenOn();
                         } catch (RemoteException e) {
                             Log.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e);
@@ -1523,6 +1540,8 @@
 
     private void updateLightsLocked(int newState, int forceState) {
         final int oldState = mPowerState;
+        newState = applyButtonState(newState);
+        newState = applyKeyboardState(newState);
         final int realDifference = (newState ^ oldState);
         final int difference = realDifference | forceState;
         if (difference == 0) {
@@ -1541,9 +1560,9 @@
                 if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
                     mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
                             ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS,
-                            preferredBrightness);
+                            Power.BRIGHTNESS_ON);
                 } else {
-                    mKeyboardBrightness.setTargetLocked(preferredBrightness,
+                    mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_ON,
                             ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS,
                             Power.BRIGHTNESS_OFF);
                 }
@@ -1562,9 +1581,9 @@
                 if ((newState & BUTTON_BRIGHT_BIT) == 0) {
                     mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
                             ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
-                            preferredBrightness);
+                            Power.BRIGHTNESS_ON);
                 } else {
-                    mButtonBrightness.setTargetLocked(preferredBrightness,
+                    mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_ON,
                             ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
                             Power.BRIGHTNESS_OFF);
                 }
@@ -1820,9 +1839,9 @@
         try {
             if (mScreenBrightnessOverride >= 0) {
                 return mScreenBrightnessOverride;
-            } else if (mLightSensorBrightness >= 0 && mUseSoftwareAutoBrightness
+            } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness
                     && mAutoBrightessEnabled) {
-                return mLightSensorBrightness;
+                return mLightSensorScreenBrightness;
             }
             final int brightness = Settings.System.getInt(mContext.getContentResolver(),
                                                           SCREEN_BRIGHTNESS);
@@ -1833,6 +1852,40 @@
         }
     }
 
+    private int applyButtonState(int state) {
+        int brightness = -1;
+        if (mButtonBrightnessOverride >= 0) {
+            brightness = mButtonBrightnessOverride;
+        } else if (mLightSensorButtonBrightness >= 0 && mUseSoftwareAutoBrightness) {
+            brightness = mLightSensorButtonBrightness;
+        }
+        if (brightness > 0) {
+            return state | BUTTON_BRIGHT_BIT;
+        } else if (brightness == 0) {
+            return state & ~BUTTON_BRIGHT_BIT;
+        } else {
+            return state;
+        }
+    }
+
+    private int applyKeyboardState(int state) {
+        int brightness = -1;
+        if (!mKeyboardVisible) {
+            brightness = 0;
+        } else if (mButtonBrightnessOverride >= 0) {
+            brightness = mButtonBrightnessOverride;
+        } else if (mLightSensorKeyboardBrightness >= 0 && mUseSoftwareAutoBrightness) {
+            brightness =  mLightSensorKeyboardBrightness;
+        }
+        if (brightness > 0) {
+            return state | KEYBOARD_BRIGHT_BIT;
+        } else if (brightness == 0) {
+            return state & ~KEYBOARD_BRIGHT_BIT;
+        } else {
+            return state;
+        }
+    }
+
     public boolean isScreenOn() {
         synchronized (mLocks) {
             return (mPowerState & SCREEN_ON_BIT) != 0;
@@ -2008,7 +2061,9 @@
                 } else {
                     keyboardValue = 0;
                 }
-                mLightSensorBrightness = lcdValue;
+                mLightSensorScreenBrightness = lcdValue;
+                mLightSensorButtonBrightness = buttonValue;
+                mLightSensorKeyboardBrightness = keyboardValue;
 
                 if (mDebugLightSensor) {
                     Log.d(TAG, "lcdValue " + lcdValue);
@@ -2032,31 +2087,35 @@
                                 lcdValue, brightnessMode);
                     }
                 }
-                if (ANIMATE_BUTTON_LIGHTS) {
-                    if (mButtonBrightness.setTargetLocked(buttonValue,
-                            AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
-                            (int)mButtonBrightness.curValue)) {
-                        startAnimation = true;
+                if (mButtonBrightnessOverride < 0) {
+                    if (ANIMATE_BUTTON_LIGHTS) {
+                        if (mButtonBrightness.setTargetLocked(buttonValue,
+                                AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
+                                (int)mButtonBrightness.curValue)) {
+                            startAnimation = true;
+                        }
+                    } else {
+                        int brightnessMode = (mUseSoftwareAutoBrightness
+                                            ? HardwareService.BRIGHTNESS_MODE_SENSOR
+                                            : HardwareService.BRIGHTNESS_MODE_USER);
+                        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS,
+                                buttonValue, brightnessMode);
                     }
-                } else {
-                    int brightnessMode = (mUseSoftwareAutoBrightness
-                                        ? HardwareService.BRIGHTNESS_MODE_SENSOR
-                                        : HardwareService.BRIGHTNESS_MODE_USER);
-                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS,
-                            buttonValue, brightnessMode);
                 }
-                if (ANIMATE_KEYBOARD_LIGHTS) {
-                    if (mKeyboardBrightness.setTargetLocked(keyboardValue,
-                            AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
-                            (int)mKeyboardBrightness.curValue)) {
-                        startAnimation = true;
+                if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) {
+                    if (ANIMATE_KEYBOARD_LIGHTS) {
+                        if (mKeyboardBrightness.setTargetLocked(keyboardValue,
+                                AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
+                                (int)mKeyboardBrightness.curValue)) {
+                            startAnimation = true;
+                        }
+                    } else {
+                        int brightnessMode = (mUseSoftwareAutoBrightness
+                                            ? HardwareService.BRIGHTNESS_MODE_SENSOR
+                                            : HardwareService.BRIGHTNESS_MODE_USER);
+                        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD,
+                                keyboardValue, brightnessMode);
                     }
-                } else {
-                    int brightnessMode = (mUseSoftwareAutoBrightness
-                                        ? HardwareService.BRIGHTNESS_MODE_SENSOR
-                                        : HardwareService.BRIGHTNESS_MODE_USER);
-                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD,
-                            keyboardValue, brightnessMode);
                 }
                 if (startAnimation) {
                     if (mDebugLightSensor) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 4bac178..0481340 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -224,20 +224,22 @@
     /**
      * Condition waited on by {@link #reenableKeyguard} to know the call to
      * the window policy has finished.
+     * This is set to true only if mKeyguardTokenWatcher.acquired() has
+     * actually disabled the keyguard.
      */
-    private boolean mWaitingUntilKeyguardReenabled = false;
+    private boolean mKeyguardDisabled = false;
 
-
-    final TokenWatcher mKeyguardDisabled = new TokenWatcher(
-            new Handler(), "WindowManagerService.mKeyguardDisabled") {
+    final TokenWatcher mKeyguardTokenWatcher = new TokenWatcher(
+            new Handler(), "WindowManagerService.mKeyguardTokenWatcher") {
         public void acquired() {
             mPolicy.enableKeyguard(false);
+            mKeyguardDisabled = true;
         }
         public void released() {
             mPolicy.enableKeyguard(true);
-            synchronized (mKeyguardDisabled) {
-                mWaitingUntilKeyguardReenabled = false;
-                mKeyguardDisabled.notifyAll();
+            synchronized (mKeyguardTokenWatcher) {
+                mKeyguardDisabled = false;
+                mKeyguardTokenWatcher.notifyAll();
             }
         }
     };
@@ -2447,7 +2449,12 @@
             boolean assignLayers = false;
 
             if (imMayMove) {
-                if (moveInputMethodWindowsIfNeededLocked(false)) {
+                if (moveInputMethodWindowsIfNeededLocked(false) || displayed) {
+                    // Little hack here -- we -should- be able to rely on the
+                    // function to return true if the IME has moved and needs
+                    // its layer recomputed.  However, if the IME was hidden
+                    // and isn't actually moved in the list, its layer may be
+                    // out of data so we make sure to recompute it.
                     assignLayers = true;
                 }
             }
@@ -4040,8 +4047,8 @@
             != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
-        synchronized (mKeyguardDisabled) {
-            mKeyguardDisabled.acquire(token, tag);
+        synchronized (mKeyguardTokenWatcher) {
+            mKeyguardTokenWatcher.acquire(token, tag);
         }
     }
 
@@ -4050,16 +4057,20 @@
             != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
-        synchronized (mKeyguardDisabled) {
-            mKeyguardDisabled.release(token);
+        synchronized (mKeyguardTokenWatcher) {
+            mKeyguardTokenWatcher.release(token);
 
-            if (!mKeyguardDisabled.isAcquired()) {
-                // if we are the last one to reenable the keyguard wait until
-                // we have actaully finished reenabling until returning
-                mWaitingUntilKeyguardReenabled = true;
-                while (mWaitingUntilKeyguardReenabled) {
+            if (!mKeyguardTokenWatcher.isAcquired()) {
+                // If we are the last one to reenable the keyguard wait until
+                // we have actaully finished reenabling until returning.
+                // It is possible that reenableKeyguard() can be called before
+                // the previous disableKeyguard() is handled, in which case
+                // neither mKeyguardTokenWatcher.acquired() or released() would
+                // be called.  In that case mKeyguardDisabled will be false here
+                // and we have nothing to wait for.
+                while (mKeyguardDisabled) {
                     try {
-                        mKeyguardDisabled.wait();
+                        mKeyguardTokenWatcher.wait();
                     } catch (InterruptedException e) {
                         Thread.currentThread().interrupt();
                     }
@@ -9302,6 +9313,7 @@
         boolean orientationChangeComplete = true;
         Session holdScreen = null;
         float screenBrightness = -1;
+        float buttonBrightness = -1;
         boolean focusDisplayed = false;
         boolean animating = false;
 
@@ -10061,6 +10073,10 @@
                                 && screenBrightness < 0) {
                             screenBrightness = w.mAttrs.screenBrightness;
                         }
+                        if (!syswin && w.mAttrs.buttonBrightness >= 0
+                                && buttonBrightness < 0) {
+                            buttonBrightness = w.mAttrs.buttonBrightness;
+                        }
                         if (attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG
                                 || attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD
                                 || attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_ERROR) {
@@ -10307,6 +10323,12 @@
             mPowerManager.setScreenBrightnessOverride((int)
                     (screenBrightness * Power.BRIGHTNESS_ON));
         }
+        if (buttonBrightness < 0 || buttonBrightness > 1.0f) {
+            mPowerManager.setButtonBrightnessOverride(-1);
+        } else {
+            mPowerManager.setButtonBrightnessOverride((int)
+                    (buttonBrightness * Power.BRIGHTNESS_ON));
+        }
         if (holdScreen != mHoldingScreenOn) {
             mHoldingScreenOn = holdScreen;
             Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
@@ -10854,7 +10876,7 @@
 
     public void monitor() {
         synchronized (mWindowMap) { }
-        synchronized (mKeyguardDisabled) { }
+        synchronized (mKeyguardTokenWatcher) { }
         synchronized (mKeyWaiter) { }
     }