Merge "Support custom themes in TabHost."
diff --git a/api/11.xml b/api/11.xml
index 0edb67c..5fd9c8c 100644
--- a/api/11.xml
+++ b/api/11.xml
@@ -17275,19 +17275,6 @@
 <parameter name="newIntent" type="android.content.Intent">
 </parameter>
 </method>
-<method name="setPersistent"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<parameter name="isPersistent" type="boolean">
-</parameter>
-</method>
 <method name="setProgress"
  return="void"
  abstract="false"
@@ -26050,19 +26037,6 @@
 <parameter name="intent" type="android.content.Intent">
 </parameter>
 </method>
-<method name="setForeground"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="deprecated"
- visibility="public"
->
-<parameter name="isForeground" type="boolean">
-</parameter>
-</method>
 <method name="startForeground"
  return="void"
  abstract="false"
diff --git a/api/current.xml b/api/current.xml
index cf4464b..0cbfd76 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -23636,19 +23636,6 @@
 <parameter name="newIntent" type="android.content.Intent">
 </parameter>
 </method>
-<method name="setPersistent"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<parameter name="isPersistent" type="boolean">
-</parameter>
-</method>
 <method name="setProgress"
  return="void"
  abstract="false"
@@ -35347,19 +35334,6 @@
 <parameter name="intent" type="android.content.Intent">
 </parameter>
 </method>
-<method name="setForeground"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="deprecated"
- visibility="public"
->
-<parameter name="isForeground" type="boolean">
-</parameter>
-</method>
 <method name="startForeground"
  return="void"
  abstract="false"
@@ -110561,7 +110535,7 @@
  synchronized="false"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -110572,7 +110546,7 @@
  synchronized="false"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -110583,7 +110557,7 @@
  synchronized="false"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="ctx" type="android.content.Context">
@@ -110596,7 +110570,7 @@
  synchronized="false"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="ctx" type="android.content.Context">
@@ -335171,7 +335145,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -335239,7 +335213,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -335263,7 +335237,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -340906,7 +340880,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -340917,7 +340891,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -340928,7 +340902,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -343298,7 +343272,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -358372,7 +358346,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index f8650eb..178032d 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -122,3 +122,29 @@
 LOCAL_MODULE:= stream
 
 include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=         \
+        sf2.cpp    \
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright liblog libutils libbinder libstagefright_foundation \
+        libmedia libsurfaceflinger_client libcutils libui
+
+LOCAL_C_INCLUDES:= \
+	$(JNI_H_INCLUDE) \
+	frameworks/base/media/libstagefright \
+	$(TOP)/frameworks/base/include/media/stagefright/openmax
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_MODULE:= sf2
+
+include $(BUILD_EXECUTABLE)
+
+
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
new file mode 100644
index 0000000..1dc08ea2
--- /dev/null
+++ b/cmds/stagefright/sf2.cpp
@@ -0,0 +1,564 @@
+#include <binder/ProcessState.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include "include/ESDS.h"
+
+using namespace android;
+
+struct Controller : public AHandler {
+    Controller(const char *uri, bool decodeAudio, const sp<Surface> &surface)
+        : mURI(uri),
+          mDecodeAudio(decodeAudio),
+          mSurface(surface),
+          mCodec(new ACodec) {
+        CHECK(!mDecodeAudio || mSurface == NULL);
+    }
+
+    void startAsync() {
+        (new AMessage(kWhatStart, id()))->post();
+    }
+
+protected:
+    virtual ~Controller() {
+    }
+
+    virtual void onMessageReceived(const sp<AMessage> &msg) {
+        switch (msg->what()) {
+            case kWhatStart:
+            {
+#if 1
+                mDecodeLooper = looper();
+#else
+                mDecodeLooper = new ALooper;
+                mDecodeLooper->setName("sf2 decode looper");
+                mDecodeLooper->start();
+#endif
+
+                sp<DataSource> dataSource =
+                    DataSource::CreateFromURI(mURI.c_str());
+
+                sp<MediaExtractor> extractor =
+                    MediaExtractor::Create(dataSource);
+
+                for (size_t i = 0; i < extractor->countTracks(); ++i) {
+                    sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+                    const char *mime;
+                    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+                    if (!strncasecmp(mDecodeAudio ? "audio/" : "video/",
+                                     mime, 6)) {
+                        mSource = extractor->getTrack(i);
+                        break;
+                    }
+                }
+                CHECK(mSource != NULL);
+
+                CHECK_EQ(mSource->start(), (status_t)OK);
+
+                mDecodeLooper->registerHandler(mCodec);
+
+                mCodec->setNotificationMessage(
+                        new AMessage(kWhatCodecNotify, id()));
+
+                sp<AMessage> format = makeFormat(mSource->getFormat());
+
+                if (mSurface != NULL) {
+                    format->setObject("surface", mSurface);
+                }
+
+                mCodec->initiateSetup(format);
+
+                mCSDIndex = 0;
+                mStartTimeUs = ALooper::GetNowUs();
+                mNumOutputBuffersReceived = 0;
+                mTotalBytesReceived = 0;
+                mLeftOverBuffer = NULL;
+                mFinalResult = OK;
+                mSeekState = SEEK_NONE;
+
+                // (new AMessage(kWhatSeek, id()))->post(5000000ll);
+                break;
+            }
+
+            case kWhatSeek:
+            {
+                printf("+");
+                fflush(stdout);
+
+                CHECK(mSeekState == SEEK_NONE
+                        || mSeekState == SEEK_FLUSH_COMPLETED);
+
+                if (mLeftOverBuffer != NULL) {
+                    mLeftOverBuffer->release();
+                    mLeftOverBuffer = NULL;
+                }
+
+                mSeekState = SEEK_FLUSHING;
+                mSeekTimeUs = 30000000ll;
+
+                mCodec->signalFlush();
+                break;
+            }
+
+            case kWhatStop:
+            {
+                if (mLeftOverBuffer != NULL) {
+                    mLeftOverBuffer->release();
+                    mLeftOverBuffer = NULL;
+                }
+
+                CHECK_EQ(mSource->stop(), (status_t)OK);
+                mSource.clear();
+
+                mCodec->initiateShutdown();
+                break;
+            }
+
+            case kWhatCodecNotify:
+            {
+                int32_t what;
+                CHECK(msg->findInt32("what", &what));
+
+                if (what == ACodec::kWhatFillThisBuffer) {
+                    onFillThisBuffer(msg);
+                } else if (what == ACodec::kWhatDrainThisBuffer) {
+                    if ((mNumOutputBuffersReceived++ % 16) == 0) {
+                        printf(".");
+                        fflush(stdout);
+                    }
+
+                    onDrainThisBuffer(msg);
+                } else if (what == ACodec::kWhatEOS) {
+                    printf("$\n");
+
+                    int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
+
+                    if (mDecodeAudio) {
+                        printf("%lld bytes received. %.2f KB/sec\n",
+                               mTotalBytesReceived,
+                               mTotalBytesReceived * 1E6 / 1024 / delayUs);
+                    } else {
+                        printf("%d frames decoded, %.2f fps. %lld bytes "
+                               "received. %.2f KB/sec\n",
+                               mNumOutputBuffersReceived,
+                               mNumOutputBuffersReceived * 1E6 / delayUs,
+                               mTotalBytesReceived,
+                               mTotalBytesReceived * 1E6 / 1024 / delayUs);
+                    }
+
+                    (new AMessage(kWhatStop, id()))->post();
+                } else if (what == ACodec::kWhatFlushCompleted) {
+                    mSeekState = SEEK_FLUSH_COMPLETED;
+                    mCodec->signalResume();
+
+                    (new AMessage(kWhatSeek, id()))->post(5000000ll);
+                } else {
+                    CHECK_EQ(what, (int32_t)ACodec::kWhatShutdownCompleted);
+
+                    mDecodeLooper->unregisterHandler(mCodec->id());
+
+                    if (mDecodeLooper != looper()) {
+                        mDecodeLooper->stop();
+                    }
+
+                    looper()->stop();
+                }
+                break;
+            }
+
+            default:
+                TRESPASS();
+                break;
+        }
+    }
+
+private:
+    enum {
+        kWhatStart             = 'strt',
+        kWhatStop              = 'stop',
+        kWhatCodecNotify       = 'noti',
+        kWhatSeek              = 'seek',
+    };
+
+    sp<ALooper> mDecodeLooper;
+
+    AString mURI;
+    bool mDecodeAudio;
+    sp<Surface> mSurface;
+    sp<ACodec> mCodec;
+    sp<MediaSource> mSource;
+
+    Vector<sp<ABuffer> > mCSD;
+    size_t mCSDIndex;
+
+    MediaBuffer *mLeftOverBuffer;
+    status_t mFinalResult;
+
+    int64_t mStartTimeUs;
+    int32_t mNumOutputBuffersReceived;
+    int64_t mTotalBytesReceived;
+
+    enum SeekState {
+        SEEK_NONE,
+        SEEK_FLUSHING,
+        SEEK_FLUSH_COMPLETED,
+    };
+    SeekState mSeekState;
+    int64_t mSeekTimeUs;
+
+    sp<AMessage> makeFormat(const sp<MetaData> &meta) {
+        CHECK(mCSD.isEmpty());
+
+        const char *mime;
+        CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+        sp<AMessage> msg = new AMessage;
+        msg->setString("mime", mime);
+
+        if (!strncasecmp("video/", mime, 6)) {
+            int32_t width, height;
+            CHECK(meta->findInt32(kKeyWidth, &width));
+            CHECK(meta->findInt32(kKeyHeight, &height));
+
+            msg->setInt32("width", width);
+            msg->setInt32("height", height);
+        } else {
+            CHECK(!strncasecmp("audio/", mime, 6));
+
+            int32_t numChannels, sampleRate;
+            CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+            CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+            msg->setInt32("channel-count", numChannels);
+            msg->setInt32("sample-rate", sampleRate);
+        }
+
+        uint32_t type;
+        const void *data;
+        size_t size;
+        if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+            // Parse the AVCDecoderConfigurationRecord
+
+            const uint8_t *ptr = (const uint8_t *)data;
+
+            CHECK(size >= 7);
+            CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+            uint8_t profile = ptr[1];
+            uint8_t level = ptr[3];
+
+            // There is decodable content out there that fails the following
+            // assertion, let's be lenient for now...
+            // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
+
+            size_t lengthSize = 1 + (ptr[4] & 3);
+
+            // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+            // violates it...
+            // CHECK((ptr[5] >> 5) == 7);  // reserved
+
+            size_t numSeqParameterSets = ptr[5] & 31;
+
+            ptr += 6;
+            size -= 6;
+
+            sp<ABuffer> buffer = new ABuffer(1024);
+            buffer->setRange(0, 0);
+
+            for (size_t i = 0; i < numSeqParameterSets; ++i) {
+                CHECK(size >= 2);
+                size_t length = U16_AT(ptr);
+
+                ptr += 2;
+                size -= 2;
+
+                CHECK(size >= length);
+
+                memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+                memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+                buffer->setRange(0, buffer->size() + 4 + length);
+
+                ptr += length;
+                size -= length;
+            }
+
+            buffer->meta()->setInt32("csd", true);
+            mCSD.push(buffer);
+
+            buffer = new ABuffer(1024);
+            buffer->setRange(0, 0);
+
+            CHECK(size >= 1);
+            size_t numPictureParameterSets = *ptr;
+            ++ptr;
+            --size;
+
+            for (size_t i = 0; i < numPictureParameterSets; ++i) {
+                CHECK(size >= 2);
+                size_t length = U16_AT(ptr);
+
+                ptr += 2;
+                size -= 2;
+
+                CHECK(size >= length);
+
+                memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+                memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+                buffer->setRange(0, buffer->size() + 4 + length);
+
+                ptr += length;
+                size -= length;
+            }
+
+            buffer->meta()->setInt32("csd", true);
+            mCSD.push(buffer);
+
+            msg->setObject("csd", buffer);
+        } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+            ESDS esds((const char *)data, size);
+            CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+            const void *codec_specific_data;
+            size_t codec_specific_data_size;
+            esds.getCodecSpecificInfo(
+                    &codec_specific_data, &codec_specific_data_size);
+
+            sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+            memcpy(buffer->data(), codec_specific_data,
+                   codec_specific_data_size);
+
+            buffer->meta()->setInt32("csd", true);
+            mCSD.push(buffer);
+        }
+
+        int32_t maxInputSize;
+        if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+            msg->setInt32("max-input-size", maxInputSize);
+        }
+
+        return msg;
+    }
+
+    void onFillThisBuffer(const sp<AMessage> &msg) {
+        sp<AMessage> reply;
+        CHECK(msg->findMessage("reply", &reply));
+
+        if (mSeekState == SEEK_FLUSHING) {
+            reply->post();
+            return;
+        }
+
+        sp<RefBase> obj;
+        CHECK(msg->findObject("buffer", &obj));
+        sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+
+        if (mCSDIndex < mCSD.size()) {
+            outBuffer = mCSD.editItemAt(mCSDIndex++);
+            outBuffer->meta()->setInt64("timeUs", 0);
+        } else {
+            size_t sizeLeft = outBuffer->capacity();
+            outBuffer->setRange(0, 0);
+
+            int32_t n = 0;
+
+            for (;;) {
+                MediaBuffer *inBuffer;
+
+                if (mLeftOverBuffer != NULL) {
+                    inBuffer = mLeftOverBuffer;
+                    mLeftOverBuffer = NULL;
+                } else if (mFinalResult != OK) {
+                    break;
+                } else {
+                    MediaSource::ReadOptions options;
+                    if (mSeekState == SEEK_FLUSH_COMPLETED) {
+                        options.setSeekTo(mSeekTimeUs);
+                        mSeekState = SEEK_NONE;
+                    }
+                    status_t err = mSource->read(&inBuffer, &options);
+
+                    if (err != OK) {
+                        mFinalResult = err;
+                        break;
+                    }
+                }
+
+                if (inBuffer->range_length() > sizeLeft) {
+                    if (outBuffer->size() == 0) {
+                        LOGE("Unable to fit even a single input buffer of size %d.",
+                             inBuffer->range_length());
+                    }
+                    CHECK_GT(outBuffer->size(), 0u);
+
+                    mLeftOverBuffer = inBuffer;
+                    break;
+                }
+
+                ++n;
+
+                if (outBuffer->size() == 0) {
+                    int64_t timeUs;
+                    CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+                    outBuffer->meta()->setInt64("timeUs", timeUs);
+                }
+
+                memcpy(outBuffer->data() + outBuffer->size(),
+                       (const uint8_t *)inBuffer->data()
+                        + inBuffer->range_offset(),
+                       inBuffer->range_length());
+
+                outBuffer->setRange(
+                        0, outBuffer->size() + inBuffer->range_length());
+
+                sizeLeft -= inBuffer->range_length();
+
+                inBuffer->release();
+                inBuffer = NULL;
+
+                // break;  // Don't coalesce
+            }
+
+            LOGV("coalesced %d input buffers", n);
+
+            if (outBuffer->size() == 0) {
+                CHECK_NE(mFinalResult, (status_t)OK);
+
+                reply->setInt32("err", mFinalResult);
+                reply->post();
+                return;
+            }
+        }
+
+        reply->setObject("buffer", outBuffer);
+        reply->post();
+    }
+
+    void onDrainThisBuffer(const sp<AMessage> &msg) {
+        sp<RefBase> obj;
+        CHECK(msg->findObject("buffer", &obj));
+
+        sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+        mTotalBytesReceived += buffer->size();
+
+        sp<AMessage> reply;
+        CHECK(msg->findMessage("reply", &reply));
+
+        reply->post();
+    }
+
+    DISALLOW_EVIL_CONSTRUCTORS(Controller);
+};
+
+static void usage(const char *me) {
+    fprintf(stderr, "usage: %s\n", me);
+    fprintf(stderr, "       -h(elp)\n");
+    fprintf(stderr, "       -a(udio)\n");
+
+    fprintf(stderr,
+            "       -s(surface) Allocate output buffers on a surface.\n");
+}
+
+int main(int argc, char **argv) {
+    android::ProcessState::self()->startThreadPool();
+
+    bool decodeAudio = false;
+    bool useSurface = false;
+
+    int res;
+    while ((res = getopt(argc, argv, "has")) >= 0) {
+        switch (res) {
+            case 'a':
+                decodeAudio = true;
+                break;
+
+            case 's':
+                useSurface = true;
+                break;
+
+            case '?':
+            case 'h':
+            default:
+            {
+                usage(argv[0]);
+                return 1;
+            }
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc != 1) {
+        usage(argv[-optind]);
+        return 1;
+    }
+
+    DataSource::RegisterDefaultSniffers();
+
+    sp<ALooper> looper = new ALooper;
+    looper->setName("sf2");
+
+    sp<SurfaceComposerClient> composerClient;
+    sp<SurfaceControl> control;
+    sp<Surface> surface;
+
+    if (!decodeAudio && useSurface) {
+        composerClient = new SurfaceComposerClient;
+        CHECK_EQ(composerClient->initCheck(), (status_t)OK);
+
+        control = composerClient->createSurface(
+                getpid(),
+                String8("A Surface"),
+                0,
+                1280,
+                800,
+                PIXEL_FORMAT_RGB_565,
+                0);
+
+        CHECK(control != NULL);
+        CHECK(control->isValid());
+
+        CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
+        CHECK_EQ(control->setLayer(30000), (status_t)OK);
+        CHECK_EQ(control->show(), (status_t)OK);
+        CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
+
+        surface = control->getSurface();
+        CHECK(surface != NULL);
+    }
+
+    sp<Controller> controller = new Controller(argv[0], decodeAudio, surface);
+    looper->registerHandler(controller);
+
+    controller->startAsync();
+
+    CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK);
+
+    looper->unregisterHandler(controller->id());
+
+    if (!decodeAudio && useSurface) {
+        composerClient->dispose();
+    }
+
+    return 0;
+}
+
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index a3d7a6e..ccae92eb 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -28,6 +28,8 @@
 
 private:
     int mFd;
+    off64_t mFileSize;
+    int64_t mNextSeekTimeUs;
 
     sp<IStreamListener> mListener;
     Vector<sp<IMemory> > mBuffers;
@@ -36,8 +38,13 @@
 };
 
 MyStreamSource::MyStreamSource(int fd)
-    : mFd(fd) {
+    : mFd(fd),
+      mFileSize(0),
+      mNextSeekTimeUs(ALooper::GetNowUs() + 5000000ll) {
     CHECK_GE(fd, 0);
+
+    mFileSize = lseek64(fd, 0, SEEK_END);
+    lseek64(fd, 0, SEEK_SET);
 }
 
 MyStreamSource::~MyStreamSource() {
@@ -53,6 +60,20 @@
 
 void MyStreamSource::onBufferAvailable(size_t index) {
     CHECK_LT(index, mBuffers.size());
+
+    if (mNextSeekTimeUs >= 0 && mNextSeekTimeUs <= ALooper::GetNowUs()) {
+        off64_t offset = (off64_t)(((float)rand() / RAND_MAX) * mFileSize * 0.8);
+        offset = (offset / 188) * 188;
+
+        lseek(mFd, offset, SEEK_SET);
+
+        mListener->issueCommand(
+                IStreamListener::DISCONTINUITY, false /* synchronous */);
+
+        mNextSeekTimeUs = -1;
+        mNextSeekTimeUs = ALooper::GetNowUs() + 5000000ll;
+    }
+
     sp<IMemory> mem = mBuffers.itemAt(index);
 
     ssize_t n = read(mFd, mem->pointer(), mem->size());
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5f460a2..77cbe0a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1730,6 +1730,7 @@
     /**
      * @deprecated As of {@link android.os.Build.VERSION_CODES#GINGERBREAD}
      * this is a no-op.
+     * @hide
      */
     @Deprecated
     public void setPersistent(boolean isPersistent) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7589e99..4018703 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -101,24 +101,23 @@
         Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
         intentToResolve.addCategory(Intent.CATEGORY_INFO);
         intentToResolve.setPackage(packageName);
-        ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0);
+        List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0);
 
         // Otherwise, try to find a main launcher activity.
-        if (resolveInfo == null) {
+        if (ris == null || ris.size() <= 0) {
             // reuse the intent instance
             intentToResolve.removeCategory(Intent.CATEGORY_INFO);
             intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
             intentToResolve.setPackage(packageName);
-            resolveInfo = resolveActivity(intentToResolve, 0);
+            ris = queryIntentActivities(intentToResolve, 0);
         }
-        if (resolveInfo == null) {
+        if (ris == null || ris.size() <= 0) {
             return null;
         }
         Intent intent = new Intent(intentToResolve);
-        // Note: we do NOT fill in the component name; we'll leave the
-        // Intent unspecified, so if there are multiple matches within the
-        // package something reasonable will happen.
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setClassName(ris.get(0).activityInfo.packageName,
+                ris.get(0).activityInfo.name);
         return intent;
     }
 
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 00063af..024c5f3 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -568,6 +568,8 @@
      * be killed when they would like to avoid it), vs allowing the performance
      * of the entire system to be decreased, this method was deemed less
      * important.
+     * 
+     * @hide
      */
     @Deprecated
     public final void setForeground(boolean isForeground) {
@@ -585,7 +587,7 @@
      * would notice if their music stopped playing.
      * 
      * <p>If you need your application to run on platform versions prior to API
-     * level 5, you can use the following model to call the the older {@link #setForeground}
+     * level 5, you can use the following model to call the the older setForeground()
      * or this modern method as appropriate:
      * 
      * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index e07dbb8..599429b 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -17,7 +17,6 @@
 package android.content;
 
 import android.os.Bundle;
-import android.os.Debug;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -246,6 +245,7 @@
                 Log.v(TAG, "received connectivity action.  network info: " + networkInfo);
             }
 
+            final boolean wasConnected = mDataConnectionIsConnected;
             // only pay attention to the CONNECTED and DISCONNECTED states.
             // if connected, we are connected.
             // if disconnected, we may not be connected.  in some cases, we may be connected on
@@ -259,16 +259,19 @@
                     mDataConnectionIsConnected = true;
                     break;
                 case DISCONNECTED:
-                    if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
-                        mDataConnectionIsConnected = false;
-                    } else {
-                        mDataConnectionIsConnected = true;
-                    }
+                    mDataConnectionIsConnected = !intent.getBooleanExtra(
+                            ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
                     break;
                 default:
                     // ignore the rest of the states -- leave our boolean alone.
             }
             if (mDataConnectionIsConnected) {
+                if (!wasConnected) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "Reconnection detected: clearing all backoffs");
+                    }
+                    mSyncStorageEngine.clearAllBackoffs();
+                }
                 sendCheckAlarmsMessage();
             }
         }
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 4a4edd2..c8ca6189 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -525,6 +525,33 @@
         }
     }
 
+    public void clearAllBackoffs() {
+        boolean changed = false;
+        synchronized (mAuthorities) {
+            for (AccountInfo accountInfo : mAccounts.values()) {
+                for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
+                    if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
+                            || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "clearAllBackoffs:"
+                                    + " authority:" + authorityInfo.authority
+                                    + " account:" + accountInfo.account.name
+                                    + " backoffTime was: " + authorityInfo.backoffTime
+                                    + " backoffDelay was: " + authorityInfo.backoffDelay);
+                        }
+                        authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
+                        authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
+                        changed = true;
+                    }
+                }
+            }
+        }
+
+        if (changed) {
+            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        }
+    }
+
     public void setDelayUntilTime(Account account, String providerName, long delayUntil) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2c2e7d7..ac7a95a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -926,7 +926,7 @@
      *
      * @param packageName The name of the package to inspect.
      * 
-     * @return Returns either an Intent that can be used to
+     * @return Returns either a fully-qualified Intent that can be used to
      * launch the main activity in the package, or null if the package does
      * not contain such an activity.
      */
diff --git a/core/java/android/hardware/UsbManager.java b/core/java/android/hardware/UsbManager.java
index 9709c2d..1003bf9 100644
--- a/core/java/android/hardware/UsbManager.java
+++ b/core/java/android/hardware/UsbManager.java
@@ -27,32 +27,12 @@
  */
 public class UsbManager {
    /**
-     * Broadcast Action:  A broadcast for USB connected events.
-     *
-     * The extras bundle will name/value pairs with the name of the function
-     * and a value of either {@link #USB_FUNCTION_ENABLED} or {@link #USB_FUNCTION_DISABLED}.
-     * Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE},
-     * {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}.
-     */
-    public static final String ACTION_USB_CONNECTED =
-            "android.hardware.action.USB_CONNECTED";
-
-   /**
-     * Broadcast Action:  A broadcast for USB disconnected events.
-     */
-    public static final String ACTION_USB_DISCONNECTED =
-            "android.hardware.action.USB_DISCONNECTED";
-
-   /**
      * Broadcast Action:  A sticky broadcast for USB state change events.
      *
-     * This is a sticky broadcast for clients that are interested in both USB connect and
-     * disconnect events.  If you are only concerned with one or the other, you can use
-     * {@link #ACTION_USB_CONNECTED} or {@link #ACTION_USB_DISCONNECTED} to avoid receiving
-     * unnecessary broadcasts.  The boolean {@link #USB_CONNECTED} extra indicates whether
-     * USB is connected or disconnected.
-     * The extras bundle will also contain name/value pairs with the name of the function
-     * and a value of either {@link #USB_FUNCTION_ENABLED} or {@link #USB_FUNCTION_DISABLED}.
+     * This is a sticky broadcast for clients that includes USB connected/disconnected state,
+     * the USB configuration that is currently set and a bundle containing name/value pairs
+     * with the names of the functions and a value of either {@link #USB_FUNCTION_ENABLED}
+     * or {@link #USB_FUNCTION_DISABLED}.
      * Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE},
      * {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}.
      */
@@ -84,38 +64,44 @@
     public static final String USB_CONNECTED = "connected";
 
     /**
+     * Integer extra containing currently set USB configuration.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
+     */
+    public static final String USB_CONFIGURATION = "configuration";
+
+    /**
      * Name of the USB mass storage USB function.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      */
     public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage";
 
     /**
      * Name of the adb USB function.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      */
     public static final String USB_FUNCTION_ADB = "adb";
 
     /**
      * Name of the RNDIS ethernet USB function.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      */
     public static final String USB_FUNCTION_RNDIS = "rndis";
 
     /**
      * Name of the MTP USB function.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      */
     public static final String USB_FUNCTION_MTP = "mtp";
 
     /**
      * Value indicating that a USB function is enabled.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      */
     public static final String USB_FUNCTION_ENABLED = "enabled";
 
     /**
      * Value indicating that a USB function is disabled.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      */
     public static final String USB_FUNCTION_DISABLED = "disabled";
 
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 3b9b9fe..0ad80dd 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -131,13 +131,14 @@
     }
 
 
-    // TODO: deprecate this function
     /**
      * Return the proxy host set by the user.
      * @param ctx A Context used to get the settings for the proxy host.
      * @return String containing the host name. If the user did not set a host
      *         name it returns the default host. A null value means that no
      *         host is to be used.
+     * @deprecated Use standard java vm proxy values to find the host, port
+     *         and exclusion list.  This call ignores the exclusion list.
      */
     public static final String getHost(Context ctx) {
         java.net.Proxy proxy = getProxy(ctx, null);
@@ -149,11 +150,12 @@
         }
     }
 
-    // TODO: deprecate this function
     /**
      * Return the proxy port set by the user.
      * @param ctx A Context used to get the settings for the proxy port.
      * @return The port number to use or -1 if no proxy is to be used.
+     * @deprecated Use standard java vm proxy values to find the host, port
+     *         and exclusion list.  This call ignores the exclusion list.
      */
     public static final int getPort(Context ctx) {
         java.net.Proxy proxy = getProxy(ctx, null);
@@ -165,31 +167,40 @@
         }
     }
 
-    // TODO: deprecate this function
     /**
      * Return the default proxy host specified by the carrier.
      * @return String containing the host name or null if there is no proxy for
      * this carrier.
+     * @deprecated Use standard java vm proxy values to find the host, port and
+     *         exclusion list.  This call ignores the exclusion list and no
+     *         longer reports only mobile-data apn-based proxy values.
      */
     public static final String getDefaultHost() {
-        return null;
+        String host = System.getProperty("http.proxyHost");
+        if (TextUtils.isEmpty(host)) return null;
+        return host;
     }
 
-    // TODO: deprecate this function
     /**
      * Return the default proxy port specified by the carrier.
      * @return The port number to be used with the proxy host or -1 if there is
      * no proxy for this carrier.
+     * @deprecated Use standard java vm proxy values to find the host, port and
+     *         exclusion list.  This call ignores the exclusion list and no
+     *         longer reports only mobile-data apn-based proxy values.
      */
     public static final int getDefaultPort() {
-        return -1;
+        if (getDefaultHost() == null) return -1;
+        try {
+            return Integer.parseInt(System.getProperty("http.proxyPort"));
+        } catch (NumberFormatException e) {
+            return -1;
+        }
     }
 
-    // TODO: remove this function / deprecate
     /**
      * Returns the preferred proxy to be used by clients. This is a wrapper
-     * around {@link android.net.Proxy#getHost()}. Currently no proxy will
-     * be returned for localhost or if the active network is Wi-Fi.
+     * around {@link android.net.Proxy#getHost()}.
      *
      * @param context the context which will be passed to
      * {@link android.net.Proxy#getHost()}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index d577b74..38903ab 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -273,7 +273,7 @@
      */
     public static int wtf(String tag, String msg, Throwable tr) {
         TerribleFailure what = new TerribleFailure(msg, tr);
-        int bytes = println_native(LOG_ID_MAIN, ASSERT, tag, getStackTraceString(tr));
+        int bytes = println_native(LOG_ID_MAIN, ASSERT, tag, msg + '\n' + getStackTraceString(tr));
         sWtfHandler.onTerribleFailure(tag, what);
         return bytes;
     }
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 2c38c93..97bbd5a 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -28,6 +28,8 @@
 import android.net.WebAddress;
 import android.net.http.ErrorStrings;
 import android.net.http.SslCertificate;
+import android.net.http.SslError;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.provider.OpenableColumns;
@@ -44,11 +46,15 @@
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
 import java.net.URLEncoder;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Iterator;
 
+import org.apache.harmony.security.provider.cert.X509CertImpl;
+
 class BrowserFrame extends Handler {
 
     private static final String LOGTAG = "webkit";
@@ -1102,6 +1108,47 @@
     }
 
     /**
+     * Called by JNI when the native HTTP(S) stack gets a invalid cert chain.
+     *
+     * We delegate the request to CallbackProxy, and route its response to
+     * {@link #nativeSslCertErrorProceed(int)} or
+     * {@link #nativeSslCertErrorCancel(int, int)}.
+     */
+    private void reportSslCertError(final int handle, final int cert_error, byte cert_der[]) {
+        final SslError ssl_error;
+        try {
+            X509Certificate cert = new X509CertImpl(cert_der);
+            ssl_error = new SslError(cert_error, cert);
+        } catch (IOException e) {
+            // Can't get the cert, not much to do.
+            Log.e(LOGTAG, "Can't get the certificate from WebKit, cancling");
+            nativeSslCertErrorCancel(handle, cert_error);
+            return;
+        }
+
+        SslErrorHandler handler = new SslErrorHandler() {
+
+            @Override
+            public void proceed() {
+                SslCertLookupTable.getInstance().Allow(ssl_error);
+                nativeSslCertErrorProceed(handle);
+            }
+
+            @Override
+            public void cancel() {
+                SslCertLookupTable.getInstance().Deny(ssl_error);
+                nativeSslCertErrorCancel(handle, cert_error);
+            }
+        };
+
+        if (SslCertLookupTable.getInstance().IsAllowed(ssl_error)) {
+            nativeSslCertErrorProceed(handle);
+        } else {
+            mCallbackProxy.onReceivedSslError(handler, ssl_error);
+        }
+    }
+
+    /**
      * Called by JNI when the native HTTP stack needs to download a file.
      *
      * We delegate the request to CallbackProxy, which owns the current app's
@@ -1246,4 +1293,7 @@
 
     private native void nativeAuthenticationProceed(int handle, String username, String password);
     private native void nativeAuthenticationCancel(int handle);
+
+    private native void nativeSslCertErrorProceed(int handle);
+    private native void nativeSslCertErrorCancel(int handle, int cert_error);
 }
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index ecce2ce..35483c9 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -43,10 +43,16 @@
      */
     public static String getFileExtensionFromUrl(String url) {
         if (!TextUtils.isEmpty(url)) {
+            int fragment = url.lastIndexOf('#');
+            if (fragment > 0) {
+                url = url.substring(0, fragment);
+            }
+
             int query = url.lastIndexOf('?');
             if (query > 0) {
                 url = url.substring(0, query);
             }
+
             int filenamePos = url.lastIndexOf('/');
             String filename =
                 0 <= filenamePos ? url.substring(filenamePos + 1) : url;
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index 59bec24..30bbb04 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -73,7 +73,7 @@
      * SSL error handler: takes care of synchronization of multiple async
      * loaders with SSL-related problems.
      */
-    private SslErrorHandler mSslErrorHandler;
+    private SslErrorHandlerImpl mSslErrorHandler;
 
     /**
      * HTTP authentication handler: takes care of synchronization of HTTP
@@ -157,7 +157,7 @@
                     getName().equals(WebViewCore.THREAD_NAME));
         }
         mContext = context;
-        mSslErrorHandler = new SslErrorHandler();
+        mSslErrorHandler = new SslErrorHandlerImpl();
         mHttpAuthHandler = new HttpAuthHandlerImpl(this);
 
         mRequestQueue = new RequestQueue(context);
diff --git a/core/java/android/webkit/SslCertLookupTable.java b/core/java/android/webkit/SslCertLookupTable.java
new file mode 100644
index 0000000..abf612e
--- /dev/null
+++ b/core/java/android/webkit/SslCertLookupTable.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.os.Bundle;
+import android.net.http.SslError;
+
+/*
+ * A simple class to store the wrong certificates that user is aware but
+ * chose to proceed.
+ */
+class SslCertLookupTable {
+    private static SslCertLookupTable sTable;
+    private final Bundle table;
+
+    public static synchronized SslCertLookupTable getInstance() {
+        if (sTable == null) {
+            sTable = new SslCertLookupTable();
+        }
+        return sTable;
+    }
+
+    private SslCertLookupTable() {
+        table = new Bundle();
+    }
+
+    public void Allow(SslError ssl_error) {
+        table.putBoolean(ssl_error.toString(), true);
+    }
+
+    public void Deny(SslError ssl_error) {
+        table.putBoolean(ssl_error.toString(), false);
+    }
+
+    public boolean IsAllowed(SslError ssl_error) {
+        return table.getBoolean(ssl_error.toString());
+    }
+}
diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java
index 1b0afaf..426145a 100644
--- a/core/java/android/webkit/SslErrorHandler.java
+++ b/core/java/android/webkit/SslErrorHandler.java
@@ -16,258 +16,28 @@
 
 package android.webkit;
 
-import junit.framework.Assert;
-
-import android.net.http.SslError;
-import android.os.Bundle;
 import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-import java.util.LinkedList;
-import java.util.ListIterator;
 
 /**
- * SslErrorHandler: class responsible for handling SSL errors. This class is
- * passed as a parameter to BrowserCallback.displaySslErrorDialog and is meant
- * to receive the user's response.
+ * SslErrorHandler: class responsible for handling SSL errors.
+ * This class is passed as a parameter to BrowserCallback.displaySslErrorDialog
+ * and is meant to receive the user's response.
  */
 public class SslErrorHandler extends Handler {
-    /* One problem here is that there may potentially be multiple SSL errors
-     * coming from mutiple loaders. Therefore, we keep a queue of loaders
-     * that have SSL-related problems and process errors one by one in the
-     * order they were received.
-     */
-
-    private static final String LOGTAG = "network";
 
     /**
-     * Queue of loaders that experience SSL-related problems.
+     * Package-private constructor needed for API compatibility.
      */
-    private LinkedList<LoadListener> mLoaderQueue;
-
-    /**
-     * SSL error preference table.
-     */
-    private Bundle mSslPrefTable;
-
-    // These are only used in the client facing SslErrorHandler.
-    private final SslErrorHandler mOriginHandler;
-    private final LoadListener mLoadListener;
-
-    // Message id for handling the response
-    private static final int HANDLE_RESPONSE = 100;
-
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case HANDLE_RESPONSE:
-                LoadListener loader = (LoadListener) msg.obj;
-                synchronized (SslErrorHandler.this) {
-                    handleSslErrorResponse(loader, loader.sslError(),
-                            msg.arg1 == 1);
-                    mLoaderQueue.remove(loader);
-                    fastProcessQueuedSslErrors();
-                }
-                break;
-        }
-    }
-
-    /**
-     * Creates a new error handler with an empty loader queue.
-     */
-    /* package */ SslErrorHandler() {
-        mLoaderQueue = new LinkedList<LoadListener>();
-        mSslPrefTable = new Bundle();
-
-        // These are used by client facing SslErrorHandlers.
-        mOriginHandler = null;
-        mLoadListener = null;
-    }
-
-    /**
-     * Create a new error handler that will be passed to the client.
-     */
-    private SslErrorHandler(SslErrorHandler origin, LoadListener listener) {
-        mOriginHandler = origin;
-        mLoadListener = listener;
-    }
-
-    /**
-     * Saves this handler's state into a map.
-     * @return True iff succeeds.
-     */
-    /* package */ synchronized boolean saveState(Bundle outState) {
-        boolean success = (outState != null);
-        if (success) {
-            // TODO?
-            outState.putBundle("ssl-error-handler", mSslPrefTable);
-        }
-
-        return success;
-    }
-
-    /**
-     * Restores this handler's state from a map.
-     * @return True iff succeeds.
-     */
-    /* package */ synchronized boolean restoreState(Bundle inState) {
-        boolean success = (inState != null);
-        if (success) {
-            success = inState.containsKey("ssl-error-handler");
-            if (success) {
-                mSslPrefTable = inState.getBundle("ssl-error-handler");
-            }
-        }
-
-        return success;
-    }
-
-    /**
-     * Clears SSL error preference table.
-     */
-    /* package */ synchronized void clear() {
-        mSslPrefTable.clear();
-    }
-
-    /**
-     * Handles SSL error(s) on the way up to the user.
-     */
-    /* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
-        if (DebugFlags.SSL_ERROR_HANDLER) {
-            Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
-                  "url=" + loader.url());
-        }
-
-        if (!loader.cancelled()) {
-            mLoaderQueue.offer(loader);
-            if (loader == mLoaderQueue.peek()) {
-                fastProcessQueuedSslErrors();
-            }
-        }
-    }
-
-    /**
-     * Check the preference table for a ssl error that has already been shown
-     * to the user.
-     */
-    /* package */ synchronized boolean checkSslPrefTable(LoadListener loader,
-            SslError error) {
-        final String host = loader.host();
-        final int primary = error.getPrimaryError();
-
-        if (DebugFlags.SSL_ERROR_HANDLER) {
-            Assert.assertTrue(host != null && primary != 0);
-        }
-
-        if (mSslPrefTable.containsKey(host)) {
-            if (primary <= mSslPrefTable.getInt(host)) {
-                handleSslErrorResponse(loader, error, true);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Processes queued SSL-error confirmation requests in
-     * a tight loop while there is no need to ask the user.
-     */
-    /* package */void fastProcessQueuedSslErrors() {
-        while (processNextLoader());
-    }
-
-    /**
-     * Processes the next loader in the queue.
-     * @return True iff should proceed to processing the
-     * following loader in the queue
-     */
-    private synchronized boolean processNextLoader() {
-        LoadListener loader = mLoaderQueue.peek();
-        if (loader != null) {
-            // if this loader has been cancelled
-            if (loader.cancelled()) {
-                // go to the following loader in the queue. Make sure this
-                // loader has been removed from the queue.
-                mLoaderQueue.remove(loader);
-                return true;
-            }
-
-            SslError error = loader.sslError();
-
-            if (DebugFlags.SSL_ERROR_HANDLER) {
-                Assert.assertNotNull(error);
-            }
-
-            // checkSslPrefTable will handle the ssl error response if the
-            // answer is available. It does not remove the loader from the
-            // queue.
-            if (checkSslPrefTable(loader, error)) {
-                mLoaderQueue.remove(loader);
-                return true;
-            }
-
-            // if we do not have information on record, ask
-            // the user (display a dialog)
-            CallbackProxy proxy = loader.getFrame().getCallbackProxy();
-            proxy.onReceivedSslError(new SslErrorHandler(this, loader), error);
-        }
-
-        // the queue must be empty, stop
-        return false;
-    }
+    SslErrorHandler() {}
 
     /**
      * Proceed with the SSL certificate.
      */
-    public void proceed() {
-        mOriginHandler.sendMessage(
-                mOriginHandler.obtainMessage(
-                        HANDLE_RESPONSE, 1, 0, mLoadListener));
-    }
+    public void proceed() {}
 
     /**
      * Cancel this request and all pending requests for the WebView that had
      * the error.
      */
-    public void cancel() {
-        mOriginHandler.sendMessage(
-                mOriginHandler.obtainMessage(
-                        HANDLE_RESPONSE, 0, 0, mLoadListener));
-    }
-
-    /**
-     * Handles SSL error(s) on the way down from the user.
-     */
-    /* package */ synchronized void handleSslErrorResponse(LoadListener loader,
-            SslError error, boolean proceed) {
-        if (DebugFlags.SSL_ERROR_HANDLER) {
-            Assert.assertNotNull(loader);
-            Assert.assertNotNull(error);
-        }
-
-        if (DebugFlags.SSL_ERROR_HANDLER) {
-            Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
-                  + " proceed: " + proceed
-                  + " url:" + loader.url());
-        }
-
-        if (!loader.cancelled()) {
-            if (proceed) {
-                // update the user's SSL error preference table
-                int primary = error.getPrimaryError();
-                String host = loader.host();
-
-                if (DebugFlags.SSL_ERROR_HANDLER) {
-                    Assert.assertTrue(host != null && primary != 0);
-                }
-                boolean hasKey = mSslPrefTable.containsKey(host);
-                if (!hasKey ||
-                    primary > mSslPrefTable.getInt(host)) {
-                    mSslPrefTable.putInt(host, primary);
-                }
-            }
-            loader.handleSslErrorResponse(proceed);
-        }
-    }
+    public void cancel() {}
 }
diff --git a/core/java/android/webkit/SslErrorHandlerImpl.java b/core/java/android/webkit/SslErrorHandlerImpl.java
new file mode 100644
index 0000000..e029e37
--- /dev/null
+++ b/core/java/android/webkit/SslErrorHandlerImpl.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import junit.framework.Assert;
+
+import android.net.http.SslError;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+/**
+ * SslErrorHandler's implementation for Android Java HTTP stack.
+ * This class is not needed if the Chromium HTTP stack is used.
+ */
+class SslErrorHandlerImpl extends SslErrorHandler {
+    /* One problem here is that there may potentially be multiple SSL errors
+     * coming from multiple loaders. Therefore, we keep a queue of loaders
+     * that have SSL-related problems and process errors one by one in the
+     * order they were received.
+     */
+
+    private static final String LOGTAG = "network";
+
+    /**
+     * Queue of loaders that experience SSL-related problems.
+     */
+    private LinkedList<LoadListener> mLoaderQueue;
+
+    /**
+     * SSL error preference table.
+     */
+    private Bundle mSslPrefTable;
+
+    // These are only used in the client facing SslErrorHandler.
+    private final SslErrorHandler mOriginHandler;
+    private final LoadListener mLoadListener;
+
+    // Message id for handling the response
+    private static final int HANDLE_RESPONSE = 100;
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case HANDLE_RESPONSE:
+                LoadListener loader = (LoadListener) msg.obj;
+                synchronized (SslErrorHandlerImpl.this) {
+                    handleSslErrorResponse(loader, loader.sslError(),
+                            msg.arg1 == 1);
+                    mLoaderQueue.remove(loader);
+                    fastProcessQueuedSslErrors();
+                }
+                break;
+        }
+    }
+
+    /**
+     * Creates a new error handler with an empty loader queue.
+     */
+    /* package */ SslErrorHandlerImpl() {
+        mLoaderQueue = new LinkedList<LoadListener>();
+        mSslPrefTable = new Bundle();
+
+        // These are used by client facing SslErrorHandlers.
+        mOriginHandler = null;
+        mLoadListener = null;
+    }
+
+    /**
+     * Create a new error handler that will be passed to the client.
+     */
+    private SslErrorHandlerImpl(SslErrorHandler origin, LoadListener listener) {
+        mOriginHandler = origin;
+        mLoadListener = listener;
+    }
+
+    /**
+     * Saves this handler's state into a map.
+     * @return True iff succeeds.
+     */
+    /* package */ synchronized boolean saveState(Bundle outState) {
+        boolean success = (outState != null);
+        if (success) {
+            // TODO?
+            outState.putBundle("ssl-error-handler", mSslPrefTable);
+        }
+
+        return success;
+    }
+
+    /**
+     * Restores this handler's state from a map.
+     * @return True iff succeeds.
+     */
+    /* package */ synchronized boolean restoreState(Bundle inState) {
+        boolean success = (inState != null);
+        if (success) {
+            success = inState.containsKey("ssl-error-handler");
+            if (success) {
+                mSslPrefTable = inState.getBundle("ssl-error-handler");
+            }
+        }
+
+        return success;
+    }
+
+    /**
+     * Clears SSL error preference table.
+     */
+    /* package */ synchronized void clear() {
+        mSslPrefTable.clear();
+    }
+
+    /**
+     * Handles SSL error(s) on the way up to the user.
+     */
+    /* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
+        if (DebugFlags.SSL_ERROR_HANDLER) {
+            Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
+                  "url=" + loader.url());
+        }
+
+        if (!loader.cancelled()) {
+            mLoaderQueue.offer(loader);
+            if (loader == mLoaderQueue.peek()) {
+                fastProcessQueuedSslErrors();
+            }
+        }
+    }
+
+    /**
+     * Check the preference table for a ssl error that has already been shown
+     * to the user.
+     */
+    /* package */ synchronized boolean checkSslPrefTable(LoadListener loader,
+            SslError error) {
+        final String host = loader.host();
+        final int primary = error.getPrimaryError();
+
+        if (DebugFlags.SSL_ERROR_HANDLER) {
+            Assert.assertTrue(host != null && primary != 0);
+        }
+
+        if (mSslPrefTable.containsKey(host)) {
+            if (primary <= mSslPrefTable.getInt(host)) {
+                handleSslErrorResponse(loader, error, true);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Processes queued SSL-error confirmation requests in
+     * a tight loop while there is no need to ask the user.
+     */
+    /* package */void fastProcessQueuedSslErrors() {
+        while (processNextLoader());
+    }
+
+    /**
+     * Processes the next loader in the queue.
+     * @return True iff should proceed to processing the
+     * following loader in the queue
+     */
+    private synchronized boolean processNextLoader() {
+        LoadListener loader = mLoaderQueue.peek();
+        if (loader != null) {
+            // if this loader has been cancelled
+            if (loader.cancelled()) {
+                // go to the following loader in the queue. Make sure this
+                // loader has been removed from the queue.
+                mLoaderQueue.remove(loader);
+                return true;
+            }
+
+            SslError error = loader.sslError();
+
+            if (DebugFlags.SSL_ERROR_HANDLER) {
+                Assert.assertNotNull(error);
+            }
+
+            // checkSslPrefTable will handle the ssl error response if the
+            // answer is available. It does not remove the loader from the
+            // queue.
+            if (checkSslPrefTable(loader, error)) {
+                mLoaderQueue.remove(loader);
+                return true;
+            }
+
+            // if we do not have information on record, ask
+            // the user (display a dialog)
+            CallbackProxy proxy = loader.getFrame().getCallbackProxy();
+            proxy.onReceivedSslError(new SslErrorHandlerImpl(this, loader), error);
+        }
+
+        // the queue must be empty, stop
+        return false;
+    }
+
+    /**
+     * Proceed with the SSL certificate.
+     */
+    public void proceed() {
+        mOriginHandler.sendMessage(
+                mOriginHandler.obtainMessage(
+                        HANDLE_RESPONSE, 1, 0, mLoadListener));
+    }
+
+    /**
+     * Cancel this request and all pending requests for the WebView that had
+     * the error.
+     */
+    public void cancel() {
+        mOriginHandler.sendMessage(
+                mOriginHandler.obtainMessage(
+                        HANDLE_RESPONSE, 0, 0, mLoadListener));
+    }
+
+    /**
+     * Handles SSL error(s) on the way down from the user.
+     */
+    /* package */ synchronized void handleSslErrorResponse(LoadListener loader,
+            SslError error, boolean proceed) {
+        if (DebugFlags.SSL_ERROR_HANDLER) {
+            Assert.assertNotNull(loader);
+            Assert.assertNotNull(error);
+        }
+
+        if (DebugFlags.SSL_ERROR_HANDLER) {
+            Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
+                  + " proceed: " + proceed
+                  + " url:" + loader.url());
+        }
+
+        if (!loader.cancelled()) {
+            if (proceed) {
+                // update the user's SSL error preference table
+                int primary = error.getPrimaryError();
+                String host = loader.host();
+
+                if (DebugFlags.SSL_ERROR_HANDLER) {
+                    Assert.assertTrue(host != null && primary != 0);
+                }
+                boolean hasKey = mSslPrefTable.containsKey(host);
+                if (!hasKey ||
+                    primary > mSslPrefTable.getInt(host)) {
+                    mSslPrefTable.putInt(host, primary);
+                }
+            }
+            loader.handleSslErrorResponse(proceed);
+        }
+    }
+}
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index d83fc42..bb18270 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -353,6 +353,18 @@
         return false;
     }
 
+    void ensureLayout() {
+        if (getLayout() == null) {
+            // Ensure we have a Layout
+            measure(mWidthSpec, mHeightSpec);
+            LayoutParams params = (LayoutParams) getLayoutParams();
+            if (params != null) {
+                layout(params.x, params.y, params.x + params.width,
+                        params.y + params.height);
+            }
+        }
+    }
+
     /**
      *  Determine whether this WebTextView currently represents the node
      *  represented by ptr.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 0959bfa..bbfcc08 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -432,8 +432,10 @@
      * to further, which slows down the refresh rate. Choose 50 to favor the
      * current high speed devices. For Dream like devices, 100 is a better
      * choice. Maybe make this in the buildspec later.
+     * (Update 12/14/2010: changed to 0 since current device should be able to
+     * handle the raw events and Map team voted to have the raw events too.
      */
-    private static final int TOUCH_SENT_INTERVAL = 50;
+    private static final int TOUCH_SENT_INTERVAL = 0;
     private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
 
     /**
@@ -2180,17 +2182,19 @@
         }
     }
     /**
-     * Request the href of an anchor element due to getFocusNodePath returning
-     * "href." If hrefMsg is null, this method returns immediately and does not
-     * dispatch hrefMsg to its target.
+     * Request the anchor or image element URL at the last tapped point.
+     * If hrefMsg is null, this method returns immediately and does not
+     * dispatch hrefMsg to its target. If the tapped point hits an image,
+     * an anchor, or an image in an anchor, the message associates
+     * strings in named keys in its data. The value paired with the key
+     * may be an empty string.
      *
      * @param hrefMsg This message will be dispatched with the result of the
-     *            request as the data member with "url" as key. The result can
-     *            be null.
+     *                request. The message data contains three keys:
+     *                - "url" returns the anchor's href attribute.
+     *                - "title" returns the anchor's text.
+     *                - "src" returns the image's src attribute.
      */
-    // FIXME: API change required to change the name of this function.  We now
-    // look at the cursor node, and not the focus node.  Also, what is
-    // getFocusNodePath?
     public void requestFocusNodeHref(Message hrefMsg) {
         if (hrefMsg == null) {
             return;
@@ -3750,6 +3754,18 @@
             clearTextEntry();
         }
         if (inEditingMode()) {
+            // Since we just called rebuildWebTextView, the layout is not set
+            // properly.  Update it so it can correctly find the word to select.
+            mWebTextView.ensureLayout();
+            // Provide a touch down event to WebTextView, which will allow it
+            // to store the location to use in performLongClick.
+            AbsoluteLayout.LayoutParams params
+                    = (AbsoluteLayout.LayoutParams) mWebTextView.getLayoutParams();
+            MotionEvent fake = MotionEvent.obtain(mLastTouchTime,
+                    mLastTouchTime, MotionEvent.ACTION_DOWN,
+                    mLastTouchX - params.x + mScrollX,
+                    mLastTouchY - params.y + mScrollY, 0);
+            mWebTextView.dispatchTouchEvent(fake);
             return mWebTextView.performLongClick();
         }
         if (mSelectingText) return false; // long click does nothing on selection
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 942fe0b..b8c8913 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -555,6 +555,7 @@
 
     private native String nativeRetrieveHref(int x, int y);
     private native String nativeRetrieveAnchorText(int x, int y);
+    private native String nativeRetrieveImageSource(int x, int y);
 
     private native void nativeTouchUp(int touchGeneration,
             int framePtr, int nodePtr, int x, int y);
@@ -1335,6 +1336,8 @@
                                     nativeRetrieveHref(msg.arg1, msg.arg2));
                             hrefMsg.getData().putString("title",
                                     nativeRetrieveAnchorText(msg.arg1, msg.arg2));
+                            hrefMsg.getData().putString("src",
+                                    nativeRetrieveImageSource(msg.arg1, msg.arg2));
                             hrefMsg.sendToTarget();
                             break;
                         }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index cbd98b9..6309cac 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1464,7 +1464,7 @@
 
         SavedState ss = new SavedState(superState);
 
-        boolean haveChildren = getChildCount() > 0;
+        boolean haveChildren = getChildCount() > 0 && mItemCount > 0;
         long selectedId = getSelectedItemId();
         ss.selectedId = selectedId;
         ss.height = getHeight();
@@ -1479,8 +1479,12 @@
                 // Remember the position of the first child
                 View v = getChildAt(0);
                 ss.viewTop = v.getTop();
-                ss.position = mFirstPosition;
-                ss.firstId = mAdapter.getItemId(mFirstPosition);
+                int firstPos = mFirstPosition;
+                if (firstPos >= mItemCount) {
+                    firstPos = mItemCount - 1;
+                }
+                ss.position = firstPos;
+                ss.firstId = mAdapter.getItemId(firstPos);
             } else {
                 ss.viewTop = 0;
                 ss.firstId = INVALID_POSITION;
diff --git a/core/java/android/widget/DayPicker.java b/core/java/android/widget/DayPicker.java
index e804022..cdf51f7 100644
--- a/core/java/android/widget/DayPicker.java
+++ b/core/java/android/widget/DayPicker.java
@@ -1091,34 +1091,23 @@
          */
         public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num";
 
-        protected int mDefaultHeight = 32;
-
-        protected int mMinHeight = 10;
-
-        protected static final int DEFAULT_SELECTED_DAY = -1;
-
-        protected static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
-
-        protected static final int DEFAULT_NUM_DAYS = 7;
-
         protected static final int DEFAULT_SHOW_WK_NUM = 0;
 
-        protected static final int DEFAULT_FOCUS_MONTH = -1;
+        protected final int mWeekSeperatorWidth;
 
-        protected static final int DAY_SEPARATOR_WIDTH = 1;
+        protected final int mNumberTextSize;
 
-        protected int mNumberTextSize = 14;
-
-        // affects the padding on the sides of this view
-        protected int mPadding = 0;
+        protected final int mWeekDayPadding;
 
         protected final Rect mTempRect = new Rect();
 
         protected final Paint mDrawPaint = new Paint();
 
-        protected Paint mMonthNumDrawPaint = new Paint();
+        protected final Paint mMonthNumDrawPaint = new Paint();
 
-        protected Drawable mSelectedDayLine;
+        protected final Drawable mSelectedDayLine;
+
+        protected final int mSelectedDayLineWidth;
 
         protected final int mSelectionBackgroundColor;
 
@@ -1130,6 +1119,8 @@
 
         protected final int mWeekNumberColor;
 
+        protected final int mFirstDayOfweek;
+
         // Cache the number strings so we don't have to recompute them each time
         protected String[] mDayNumbers;
 
@@ -1153,22 +1144,19 @@
         protected int mWidth;
 
         // The height this view should draw at in pixels, set by height param
-        protected int mHeight = mDefaultHeight;
-
-        // Whether the week number should be shown
-        protected boolean mShowWeekNum = false;
+        protected int mHeight;
 
         // If this view contains the selected day
         protected boolean mHasSelectedDay = false;
 
         // Which day is selected [0-6] or -1 if no day is selected
-        protected int mSelectedDay = DEFAULT_SELECTED_DAY;
+        protected int mSelectedDay = -1;
 
         // How many days to display
-        protected int mNumDays = DEFAULT_NUM_DAYS;
+        protected int mWeekDayCount;
 
         // The number of days + a spot for week number if it is displayed
-        protected int mNumCells = mNumDays;
+        protected int mNumCells;
 
         // The left edge of the selected day
         protected int mSelectedLeft = -1;
@@ -1183,7 +1171,20 @@
             context.getTheme().resolveAttribute(R.attr.dayPickerWeekViewStyle, outTypedValue, true);
             TypedArray attributesArray = context.obtainStyledAttributes(outTypedValue.resourceId,
                     R.styleable.DayPickerWeekView);
-
+            mHeight = attributesArray.getDimensionPixelSize(R.styleable.DayPickerWeekView_height,
+                    26);
+            mNumberTextSize = attributesArray.getDimensionPixelSize(
+                    R.styleable.DayPickerWeekView_textSize, 14);
+            mFirstDayOfweek = attributesArray.getInt(R.styleable.DayPickerWeekView_weekStartDay,
+                    Calendar.SUNDAY);
+            mNumCells = mWeekDayCount = attributesArray.getInt(
+                    R.styleable.DayPickerWeekView_weekDayCount, 7);
+            mShowWeekNumber = attributesArray.getBoolean(R.styleable.DayPickerWeekView_weekDayCount,
+                    true);
+            mWeekSeperatorWidth = attributesArray.getDimensionPixelSize(
+                    R.styleable.DayPickerWeekView_weekSeperatorWidth, 1);
+            mWeekDayPadding = attributesArray.getDimensionPixelSize(
+                    R.styleable.DayPickerWeekView_weekDayPadding, 0);
             mSelectionBackgroundColor = attributesArray.getColor(
                     R.styleable.DayPickerWeekView_selectionBackgroundColor, 0);
             mFocusedMonthDateColor = attributesArray.getColor(
@@ -1196,17 +1197,10 @@
                     R.styleable.DayPickerWeekView_weekNumberColor, 0);
             mSelectedDayLine = attributesArray
                     .getDrawable(R.styleable.DayPickerWeekView_selectedDayLine);
+            mSelectedDayLineWidth = attributesArray.getDimensionPixelSize(
+                    R.styleable.DayPickerWeekView_selectedDayLineWidth, 6);
             attributesArray.recycle();
 
-            if (sScale == 0) {
-                sScale = context.getResources().getDisplayMetrics().density;
-                if (sScale != 1) {
-                    mDefaultHeight *= sScale;
-                    mMinHeight *= sScale;
-                    mNumberTextSize *= sScale;
-                }
-            }
-
             // Sets up any standard paints that will be used
             setPaintProperties();
         }
@@ -1231,26 +1225,23 @@
             // We keep the current value for any params not present
             if (params.containsKey(VIEW_PARAMS_HEIGHT)) {
                 mHeight = ((int[]) params.get(VIEW_PARAMS_HEIGHT))[0];
-                if (mHeight < mMinHeight) {
-                    mHeight = mMinHeight;
-                }
             }
             if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) {
                 mSelectedDay = ((int[]) params.get(VIEW_PARAMS_SELECTED_DAY))[0];
             }
             mHasSelectedDay = mSelectedDay != -1;
             if (params.containsKey(VIEW_PARAMS_NUM_DAYS)) {
-                mNumDays = ((int[]) params.get(VIEW_PARAMS_NUM_DAYS))[0];
+                mWeekDayCount = ((int[]) params.get(VIEW_PARAMS_NUM_DAYS))[0];
             }
             if (params.containsKey(VIEW_PARAMS_SHOW_WK_NUM)) {
                 if (((int[]) params.get(VIEW_PARAMS_SHOW_WK_NUM))[0] != 0) {
-                    mNumCells = mNumDays + 1;
-                    mShowWeekNum = true;
+                    mNumCells = mWeekDayCount + 1;
+                    mShowWeekNumber = true;
                 } else {
-                    mShowWeekNum = false;
+                    mShowWeekNumber = false;
                 }
             } else {
-                mNumCells = mShowWeekNum ? mNumDays + 1 : mNumDays;
+                mNumCells = mShowWeekNumber ? mWeekDayCount + 1 : mWeekDayCount;
             }
             mWeek = ((int[]) params.get(VIEW_PARAMS_WEEK))[0];
             mTempCalendar.clear();
@@ -1259,7 +1250,7 @@
             if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
                 mTempCalendar.setFirstDayOfWeek(((int[]) params.get(VIEW_PARAMS_WEEK_START))[0]);
             } else {
-                mTempCalendar.setFirstDayOfWeek(DEFAULT_WEEK_START);
+                mTempCalendar.setFirstDayOfWeek(Calendar.SUNDAY);
             }
 
             // Allocate space for caching the day numbers and focus values
@@ -1268,7 +1259,7 @@
 
             // If we're showing the week number calculate it based on Monday
             int i = 0;
-            if (mShowWeekNum) {
+            if (mShowWeekNumber) {
                 mDayNumbers[0] = Integer.toString(mTempCalendar.get(Calendar.WEEK_OF_YEAR));
                 i++;
             }
@@ -1282,7 +1273,7 @@
             mMonthOfFirstWeekDay = mTempCalendar.get(Calendar.MONTH);
 
             int focusMonth = params.containsKey(VIEW_PARAMS_FOCUS_MONTH) ? ((int[]) params
-                    .get(VIEW_PARAMS_FOCUS_MONTH))[0] : DEFAULT_FOCUS_MONTH;
+                    .get(VIEW_PARAMS_FOCUS_MONTH))[0] : -1;
 
             for (; i < mNumCells; i++) {
                 mFocusDay[i] = (mTempCalendar.get(Calendar.MONTH) == focusMonth);
@@ -1348,7 +1339,7 @@
          * Returns the number of days this view will display.
          */
         public int getNumDays() {
-            return mNumDays;
+            return mWeekDayCount;
         }
 
         /**
@@ -1358,13 +1349,15 @@
          * @param x The x position of the touch eventy
          */
         public void getDayFromLocation(float x, Calendar outCalendar) {
-            int dayStart = mShowWeekNum ? (mWidth - mPadding * 2) / mNumCells + mPadding : mPadding;
-            if (x < dayStart || x > mWidth - mPadding) {
+            int dayStart = mShowWeekNumber ? (mWidth - mWeekDayPadding * 2) / mNumCells
+                    + mWeekDayPadding : mWeekDayPadding;
+            if (x < dayStart || x > mWidth - mWeekDayPadding) {
                 outCalendar.set(0, 0, 0, 0, 0, 0);
                 return;
             }
             // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
-            int dayPosition = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding));
+            int dayPosition = (int) ((x - dayStart) * mWeekDayCount
+                    / (mWidth - dayStart - mWeekDayPadding));
             outCalendar.setTimeZone(mFirstDay.getTimeZone());
             outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
             outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
@@ -1375,6 +1368,7 @@
             drawBackground(canvas);
             drawWeekNums(canvas);
             drawDaySeparators(canvas);
+            drawSelectedDayLines(canvas);
         }
 
         /**
@@ -1390,15 +1384,15 @@
             }
             mDrawPaint.setColor(mSelectionBackgroundColor);
 
-            mTempRect.top = DAY_SEPARATOR_WIDTH;
+            mTempRect.top = mWeekSeperatorWidth;
             mTempRect.bottom = mHeight;
-            mTempRect.left = mShowWeekNum ? mPadding + (mWidth - mPadding * 2) / mNumCells
-                    : mPadding;
+            mTempRect.left = mShowWeekNumber ? mWeekDayPadding + (mWidth - mWeekDayPadding * 2)
+                    / mNumCells : mWeekDayPadding;
             mTempRect.right = mSelectedLeft - 2;
             canvas.drawRect(mTempRect, mDrawPaint);
 
             mTempRect.left = mSelectedRight + 3;
-            mTempRect.right = mWidth - mPadding;
+            mTempRect.right = mWidth - mWeekDayPadding;
             canvas.drawRect(mTempRect, mDrawPaint);
         }
 
@@ -1410,22 +1404,22 @@
          */
         protected void drawWeekNums(Canvas canvas) {
             float textHeight = mDrawPaint.getTextSize();
-            int y = (int) ((mHeight + textHeight) / 2) - DAY_SEPARATOR_WIDTH;
+            int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorWidth;
             int nDays = mNumCells;
 
             mDrawPaint.setTextAlign(Align.CENTER);
             int i = 0;
             int divisor = 2 * nDays;
-            if (mShowWeekNum) {
+            if (mShowWeekNumber) {
                 mDrawPaint.setColor(mWeekNumberColor);
-                int x = (mWidth - mPadding * 2) / divisor + mPadding;
+                int x = (mWidth - mWeekDayPadding * 2) / divisor + mWeekDayPadding;
                 canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
                 i++;
             }
             for (; i < nDays; i++) {
                 mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
                         : mOtherMonthDateColor);
-                int x = (2 * i + 1) * (mWidth - mPadding * 2) / divisor + mPadding;
+                int x = (2 * i + 1) * (mWidth - mWeekDayPadding * 2) / divisor + mWeekDayPadding;
                 canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
             }
         }
@@ -1434,22 +1428,41 @@
          * Draws a horizontal line for separating the weeks. Override this
          * method if you want custom separators.
          *
-         * @param canvas The canvas to draw on
+         * @param canvas The canvas to draw on.
          */
         protected void drawDaySeparators(Canvas canvas) {
-            mDrawPaint.setColor(mGridLinesColor);
-            mDrawPaint.setStrokeWidth(DAY_SEPARATOR_WIDTH);
-            float x = mShowWeekNum ? mPadding + (mWidth - mPadding * 2) / mNumCells : mPadding;
-            canvas.drawLine(x, 0, mWidth - mPadding, 0, mDrawPaint);
-
-            if (mHasSelectedDay) {
-                mSelectedDayLine.setBounds(mSelectedLeft - 2, DAY_SEPARATOR_WIDTH,
-                        mSelectedLeft + 4, mHeight + 1);
-                mSelectedDayLine.draw(canvas);
-                mSelectedDayLine.setBounds(mSelectedRight - 3, DAY_SEPARATOR_WIDTH,
-                        mSelectedRight + 3, mHeight + 1);
-                mSelectedDayLine.draw(canvas);
+            // If it is the topmost fully visible child do not draw separator line
+            int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+            if (mListView.getChildAt(0).getTop() < 0) {
+                firstFullyVisiblePosition++;
             }
+            if (firstFullyVisiblePosition == mWeek) {
+                return;
+            }
+            mDrawPaint.setColor(mGridLinesColor);
+            mDrawPaint.setStrokeWidth(mWeekSeperatorWidth);
+            float x = mShowWeekNumber ? mWeekDayPadding + (mWidth - mWeekDayPadding * 2) / mNumCells
+                    : mWeekDayPadding;
+            canvas.drawLine(x, 0, mWidth - mWeekDayPadding, 0, mDrawPaint);
+        }
+
+        /**
+         * Draws the selected day lines if this week has a selected day.
+         *
+         * @param canvas The canvas to draw on
+         */
+        protected void drawSelectedDayLines(Canvas canvas) {
+            if (!mHasSelectedDay) {
+                return;
+            }
+            mSelectedDayLine.setBounds(mSelectedLeft - mSelectedDayLineWidth / 2,
+                    mWeekSeperatorWidth,
+                    mSelectedLeft + mSelectedDayLineWidth / 2, mHeight);
+            mSelectedDayLine.draw(canvas);
+            mSelectedDayLine.setBounds(mSelectedRight - mSelectedDayLineWidth / 2,
+                    mWeekSeperatorWidth,
+                    mSelectedRight + mSelectedDayLineWidth / 2, mHeight);
+            mSelectedDayLine.draw(canvas);
         }
 
         @Override
@@ -1467,12 +1480,13 @@
                 if (selectedPosition < 0) {
                     selectedPosition += 7;
                 }
-                if (mShowWeekNum) {
+                if (mShowWeekNumber) {
                     selectedPosition++;
                 }
-                mSelectedLeft = selectedPosition * (mWidth - mPadding * 2) / mNumCells + mPadding;
-                mSelectedRight = (selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells
-                        + mPadding;
+                mSelectedLeft = selectedPosition * (mWidth - mWeekDayPadding * 2) / mNumCells
+                        + mWeekDayPadding;
+                mSelectedRight = (selectedPosition + 1) * (mWidth - mWeekDayPadding * 2) / mNumCells
+                        + mWeekDayPadding;
             }
         }
 
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 4766c53..7b35b51 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1188,16 +1188,12 @@
 
     /**
      * @return The wrapped index <code>selectorIndex</code> value.
-     *         <p>
-     *         Note: The absolute value of the argument is never larger than
-     *         mEnd - mStart.
-     *         </p>
      */
     private int getWrappedSelectorIndex(int selectorIndex) {
         if (selectorIndex > mEnd) {
-            return mStart + selectorIndex - mEnd - 1;
+            return mStart + (selectorIndex - mEnd) % (mEnd - mStart);
         } else if (selectorIndex < mStart) {
-            return mEnd + selectorIndex - mStart + 1;
+            return mEnd - (mStart - selectorIndex) % (mEnd - mStart);
         }
         return selectorIndex;
     }
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index feb0972..11bdd65 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -22,6 +22,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
@@ -52,14 +53,17 @@
      * Default animation parameters
      */
     private static final int DEFAULT_ANIMATION_DURATION = 400;
+    private static final int FADE_IN_ANIMATION_DURATION = 800;
     private static final int MINIMUM_ANIMATION_DURATION = 50;
 
     /**
      * Parameters effecting the perspective visuals
      */
-    private static float PERSPECTIVE_SHIFT_FACTOR = 0.12f;
+    private static final float PERSPECTIVE_SHIFT_FACTOR_Y = 0.1f;
+    private static final float PERSPECTIVE_SHIFT_FACTOR_X = 0.1f;
+
     @SuppressWarnings({"FieldCanBeLocal"})
-    private static float PERSPECTIVE_SCALE_FACTOR = 0.35f;
+    private static final float PERSPECTIVE_SCALE_FACTOR = 0.f;
 
     /**
      * Represent the two possible stack modes, one where items slide up, and the other
@@ -184,7 +188,7 @@
      * Animate the views between different relative indexes within the {@link AdapterViewAnimator}
      */
     void animateViewForTransition(int fromIndex, int toIndex, View view) {
-        if (fromIndex == -1 && toIndex != 0) {
+        if (fromIndex == -1 && toIndex > 0) {
             // Fade item in
             if (view.getAlpha() == 1) {
                 view.setAlpha(0);
@@ -192,7 +196,7 @@
             view.setVisibility(VISIBLE);
 
             ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 1.0f);
-            fadeIn.setDuration(DEFAULT_ANIMATION_DURATION);
+            fadeIn.setDuration(FADE_IN_ANIMATION_DURATION);
             fadeIn.start();
         } else if (fromIndex == 0 && toIndex == 1) {
             // Slide item in
@@ -239,12 +243,46 @@
         }
     }
 
+    private void transformViewAtIndex(int index, View view) {
+        float maxPerspectiveShiftY = getMeasuredHeight() * PERSPECTIVE_SHIFT_FACTOR_Y;
+        float maxPerspectiveShiftX = getMeasuredWidth() * PERSPECTIVE_SHIFT_FACTOR_X;
+
+        index = mMaxNumActiveViews - index - 1;
+        if (index == mMaxNumActiveViews - 1) index--;
+
+        float r = (index * 1.0f) / (mMaxNumActiveViews - 2);
+
+        float scale = 1 - PERSPECTIVE_SCALE_FACTOR * (1 - r);
+        PropertyValuesHolder scalePropX = PropertyValuesHolder.ofFloat("scaleX", scale);
+        PropertyValuesHolder scalePropY = PropertyValuesHolder.ofFloat("scaleY", scale);
+
+        int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
+        float perspectiveTranslationY = -stackDirection * r * maxPerspectiveShiftY;
+        float scaleShiftCorrectionY = stackDirection * (1 - scale) *
+                (getMeasuredHeight() * (1 - PERSPECTIVE_SHIFT_FACTOR_Y) / 2.0f);
+        float transY = perspectiveTranslationY + scaleShiftCorrectionY;
+
+        float perspectiveTranslationX = (1 - r) * maxPerspectiveShiftX;
+        float scaleShiftCorrectionX =  (1 - scale) *
+                (getMeasuredWidth() * (1 - PERSPECTIVE_SHIFT_FACTOR_X) / 2.0f);
+        float transX = perspectiveTranslationX + scaleShiftCorrectionX;
+
+        PropertyValuesHolder translationX = PropertyValuesHolder.ofFloat("translationX", transX);
+        PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY);
+
+        ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(view, scalePropX, scalePropY,
+                translationY, translationX);
+        pa.setDuration(100);
+        pa.start();
+    }
+
     private void setupStackSlider(View v, int mode) {
         mStackSlider.setMode(mode);
         if (v != null) {
             mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
             mHighlight.setRotation(v.getRotation());
             mHighlight.setTranslationY(v.getTranslationY());
+            mHighlight.setTranslationX(v.getTranslationX());
             mHighlight.bringToFront();
             v.bringToFront();
             mStackSlider.setView(v);
@@ -283,32 +321,6 @@
         super.showPrevious();
     }
 
-    private void transformViewAtIndex(int index, View view) {
-        float maxPerpectiveShift = getMeasuredHeight() * PERSPECTIVE_SHIFT_FACTOR;
-
-        index = mMaxNumActiveViews - index - 1;
-        if (index == mMaxNumActiveViews - 1) index--;
-
-        float r = (index * 1.0f) / (mMaxNumActiveViews - 2);
-
-        float scale = 1 - PERSPECTIVE_SCALE_FACTOR * (1 - r);
-        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", scale);
-        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", scale);
-
-        r = (float) Math.pow(r, 2);
-
-        int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
-        float perspectiveTranslation = -stackDirection * r * maxPerpectiveShift;
-        float scaleShiftCorrection = stackDirection * (1 - scale) *
-                (getMeasuredHeight() * (1 - PERSPECTIVE_SHIFT_FACTOR) / 2.0f);
-        float transY = perspectiveTranslation + scaleShiftCorrection;
-
-        PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY);
-        ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(view, scaleX, scaleY, translationY);
-        pa.setDuration(100);
-        pa.start();
-    }
-
     @Override
     void showOnly(int childIndex, boolean animate, boolean onLayout) {
         super.showOnly(childIndex, animate, onLayout);
@@ -910,8 +922,9 @@
 
     private void measureChildren() {
         final int count = getChildCount();
-        final int childWidth = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
-        final int childHeight = Math.round(getMeasuredHeight()*(1-PERSPECTIVE_SHIFT_FACTOR))
+        final int childWidth = Math.round(getMeasuredWidth()*(1-PERSPECTIVE_SHIFT_FACTOR_X))
+                - mPaddingLeft - mPaddingRight;
+        final int childHeight = Math.round(getMeasuredHeight()*(1-PERSPECTIVE_SHIFT_FACTOR_Y))
                 - mPaddingTop - mPaddingBottom;
 
         for (int i = 0; i < count; i++) {
@@ -932,14 +945,14 @@
 
         // We need to deal with the case where our parent hasn't told us how
         // big we should be. In this case we should
-        float factor = 1/(1 - PERSPECTIVE_SHIFT_FACTOR);
+        float factorY = 1/(1 - PERSPECTIVE_SHIFT_FACTOR_Y);
         if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
             heightSpecSize = haveChildRefSize ?
-                    Math.round(mReferenceChildHeight * (1 + factor)) +
+                    Math.round(mReferenceChildHeight * (1 + factorY)) +
                     mPaddingTop + mPaddingBottom : 0;
         } else if (heightSpecMode == MeasureSpec.AT_MOST) {
             if (haveChildRefSize) {
-                int height = Math.round(mReferenceChildHeight * (1 + factor))
+                int height = Math.round(mReferenceChildHeight * (1 + factorY))
                         + mPaddingTop + mPaddingBottom;
                 if (height <= heightSpecSize) {
                     heightSpecSize = height;
@@ -951,9 +964,11 @@
             }
         }
 
+        float factorX = 1/(1 - PERSPECTIVE_SHIFT_FACTOR_X);
         if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
-            widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
-                    mPaddingRight : 0;
+            widthSpecSize = haveChildRefSize ?
+                    Math.round(mReferenceChildWidth * (1 + factorX)) +
+                    mPaddingLeft + mPaddingRight : 0;
         } else if (heightSpecMode == MeasureSpec.AT_MOST) {
             if (haveChildRefSize) {
                 int width = mReferenceChildWidth + mPaddingLeft + mPaddingRight;
@@ -1122,13 +1137,16 @@
             float rotationX = v.getRotationX();
             float rotation = v.getRotation();
             float translationY = v.getTranslationY();
+            float translationX = v.getTranslationX();
             v.setRotationX(0);
             v.setRotation(0);
             v.setTranslationY(0);
+            v.setTranslationX(0);
             v.draw(mCanvas);
             v.setRotationX(rotationX);
             v.setRotation(rotation);
             v.setTranslationY(translationY);
+            v.setTranslationX(translationX);
 
             drawOutline(mCanvas, bitmap);
             return bitmap;
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index f80fbd8..bee8112 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -51,6 +51,7 @@
  * "correct" states.
  */
 public class LockPatternView extends View {
+    private static final String TAG = "LockPatternView";
     // Aspect to use when rendering this view
     private static final int ASPECT_SQUARE = 0; // View will be the minimum of width/height
     private static final int ASPECT_LOCK_WIDTH = 1; // Fixed width; height will be minimum of (w,h)
@@ -293,7 +294,7 @@
         try {
             pattern = getResources().getIntArray(id);
         } catch (Resources.NotFoundException e) {
-            Log.e("LockPatternView", "Vibrate pattern missing, using default", e);
+            Log.e(TAG, "Vibrate pattern missing, using default", e);
         }
         if (pattern == null) {
             return DEFAULT_VIBE_PATTERN;
@@ -444,25 +445,47 @@
         mSquareHeight = height / 3.0f;
     }
 
+    private int resolveMeasured(int measureSpec, int desired)
+    {
+        int result = 0;
+        int specSize = MeasureSpec.getSize(measureSpec);
+        switch (MeasureSpec.getMode(measureSpec)) {
+            case MeasureSpec.UNSPECIFIED:
+                result = desired;
+                break;
+            case MeasureSpec.AT_MOST:
+                result = Math.min(specSize, desired);
+                break;
+            case MeasureSpec.EXACTLY:
+            default:
+                result = specSize;
+        }
+        return result;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int width = MeasureSpec.getSize(widthMeasureSpec);
-        final int height = MeasureSpec.getSize(heightMeasureSpec);
-        int viewWidth = width;
-        int viewHeight = height;
+        final int minimumWidth = 3 * mBitmapCircleDefault.getWidth();
+        final int minimumHeight = 3 * mBitmapCircleDefault.getHeight();
+        int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
+        int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
+
+        int requestedWidth = MeasureSpec.getSize(widthMeasureSpec);
+        int requestedHeight = MeasureSpec.getSize(heightMeasureSpec);
         switch (mAspect) {
             case ASPECT_SQUARE:
-                viewWidth = viewHeight = Math.min(width, height);
+                viewWidth = viewHeight = Math.min(requestedWidth, requestedHeight);
                 break;
             case ASPECT_LOCK_WIDTH:
-                viewWidth = width;
-                viewHeight = Math.min(width, height);
+                viewWidth = requestedWidth;
+                viewHeight = Math.min(requestedWidth, requestedHeight);
                 break;
             case ASPECT_LOCK_HEIGHT:
-                viewWidth = Math.min(width, height);
-                viewHeight = height;
+                viewWidth = Math.min(requestedWidth, requestedHeight);
+                viewHeight = requestedHeight;
                 break;
         }
+        // Log.v(TAG, "LockPatternView dimensions: " + viewWidth + "x" + viewHeight);
         setMeasuredDimension(viewWidth, viewHeight);
     }
 
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
index e9fa06e..4cb3966 100644
--- a/core/java/com/android/internal/widget/WaveView.java
+++ b/core/java/com/android/internal/widget/WaveView.java
@@ -62,6 +62,7 @@
     private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset
     private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion
     private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking
+    private static final long WAVE_DELAY = WAVE_DURATION / WAVE_COUNT; // initial propagation delay
 
     private Vibrator mVibrator;
     private OnTriggerListener mOnTriggerListener;
@@ -70,9 +71,8 @@
     private boolean mFingerDown = false;
     private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it
     private int mSnapRadius = 136; // minimum threshold for drag unlock
-    private int mWaveDelay = 240; // time to delay
     private int mWaveCount = WAVE_COUNT;  // number of waves
-    private long mWaveTimerDelay = mWaveDelay;
+    private long mWaveTimerDelay = WAVE_DELAY;
     private int mCurrentWave = 0;
     private float mLockCenterX; // center of widget as dictated by widget size
     private float mLockCenterY;
@@ -190,7 +190,7 @@
         switch (mLockState) {
             case STATE_RESET_LOCK:
                 if (DBG) Log.v(TAG, "State RESET_LOCK");
-                mWaveTimerDelay = mWaveDelay;
+                mWaveTimerDelay = WAVE_DELAY;
                 for (int i = 0; i < mLightWaves.size(); i++) {
                     //TweenMax.to(mLightWave.get(i), .3, {alpha:0, ease:Quint.easeOut});
                     DrawableHolder holder = mLightWaves.get(i);
@@ -445,7 +445,7 @@
             double distY = mMouseY - mLockCenterY;
             int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
             if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius
-                    && mWaveTimerDelay >= mWaveDelay) {
+                    && mWaveTimerDelay >= WAVE_DELAY) {
                 mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT);
 
                 DrawableHolder wave = mLightWaves.get(mCurrentWave);
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 1e00b71..c81db82 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -248,8 +248,8 @@
     // promise we will never change our pixels (great for sharing and pictures)
     pr->setImmutable();
 
-    // now create the java bitmap
-    jbyteArray buff = ((AndroidPixelRef*) pr)->getStorageObj();
+    JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
+    jbyteArray buff = allocator->getStorageObjAndReset();
     return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1);
 }
 
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index fc358a1..5134c24 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -473,16 +473,20 @@
     }
 }
 
-void AndroidPixelRef::globalRef() {
+void AndroidPixelRef::globalRef(void* localref) {
     if (fOnJavaHeap && sk_atomic_inc(&fGlobalRefCnt) == 0) {
         JNIEnv *env = vm2env(fVM);
+
+        // If JNI ref was passed, it is always used
+        if (localref) fStorageObj = (jbyteArray) localref;
+
         if (fStorageObj == NULL) {
-            SkDebugf("Cannot create a global ref, fStorage obj is NULL");
+            SkDebugf("No valid local ref to create a JNI global ref\n");
             sk_throw();
         }
         if (fHasGlobalRef) {
             // This should never happen
-            SkDebugf("Already holding a global ref");
+            SkDebugf("Already holding a JNI global ref");
             sk_throw();
         }
 
@@ -564,7 +568,8 @@
 
 JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap)
     : fAllocateInJavaHeap(allocateInJavaHeap),
-      fStorageObj(NULL) {
+      fStorageObj(NULL),
+      fAllocCount(0) {
     if (env->GetJavaVM(&fVM) != JNI_OK) {
         SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
         sk_throw();
@@ -577,12 +582,13 @@
     // If allocating in the Java heap, only allow a single object to be
     // allocated for the lifetime of this object.
     if (fStorageObj != NULL) {
-        SkDebugf("ERROR: One-shot allocator has already allocated\n");
-        sk_throw();
+        SkDebugf("WARNING: One-shot allocator has already allocated (alloc count = %d)\n", fAllocCount);
+//        sk_throw();
     }
 
     if (fAllocateInJavaHeap) {
         fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+        fAllocCount += 1;
         return fStorageObj != NULL;
     }
     return GraphicsJNI::mallocPixelRef(env, bitmap, ctable);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 505d24e..a71ce83 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -92,7 +92,16 @@
 
     void setLocalJNIRef(jbyteArray arr);
 
-    virtual void globalRef();
+    /** Used to hold a ref to the pixels when the Java bitmap may be collected.
+     *  If specified, 'localref' is a valid JNI local reference to the byte array
+     *  containing the pixel data.
+     *
+     *  'localref' may only be NULL if setLocalJNIRef() was already called with
+     *  a JNI local ref that is still valid.
+     */
+    virtual void globalRef(void* localref=NULL);
+
+    /** Release a ref that was acquired using globalRef(). */
     virtual void globalUnref();
 
 private:
@@ -136,12 +145,28 @@
     // overrides
     virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
     
-    jbyteArray getStorageObj() { return fStorageObj; };
+    /** Return the Java array object created for the last allocation.
+     *  This returns a local JNI reference which the caller is responsible
+     *  for storing appropriately (usually by passing it to the Bitmap
+     *  constructor).
+     */
+    jbyteArray getStorageObj() { return fStorageObj; }
+
+    /** Same as getStorageObj(), but also resets the allocator so that it
+     *  can allocate again.
+     */
+    jbyteArray getStorageObjAndReset() {
+        jbyteArray result = fStorageObj;
+        fStorageObj = NULL;
+        fAllocCount = 0;
+        return result;
+    };
 
 private:
     JavaVM* fVM;
     bool fAllocateInJavaHeap;
     jbyteArray fStorageObj;
+    int fAllocCount;
 };
 
 class JavaMemoryUsageReporter : public SkVMMemoryReporter {
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 6737501..3b102996 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -123,6 +123,11 @@
 
 static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject clazz)
 {
+    return doBooleanCommand("TERMINATE", "OK");
+}
+
+static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject clazz)
+{
     return (jboolean)(::wifi_stop_supplicant() == 0);
 }
 
@@ -605,7 +610,8 @@
     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded},
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
     { "startSupplicant", "()Z",  (void *)android_net_wifi_startSupplicant },
-    { "stopSupplicant", "()Z",  (void *)android_net_wifi_stopSupplicant },
+    { "stopSupplicant", "()Z", (void*) android_net_wifi_stopSupplicant },
+    { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
     { "connectToSupplicant", "()Z",  (void *)android_net_wifi_connectToSupplicant },
     { "closeSupplicantConnection", "()V",  (void *)android_net_wifi_closeSupplicantConnection },
 
diff --git a/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_dark.9.png b/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_dark.9.png
new file mode 100644
index 0000000..2edae8f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_light.9.png b/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_light.9.png
new file mode 100644
index 0000000..2edae8f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/day_picker_week_view_dayline_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_dark.9.png b/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_dark.9.png
new file mode 100644
index 0000000..a8cfd77
--- /dev/null
+++ b/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_light.9.png b/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_light.9.png
new file mode 100644
index 0000000..a8cfd77
--- /dev/null
+++ b/core/res/res/drawable-mdpi/day_picker_week_view_dayline_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png
new file mode 100644
index 0000000..eb4e0be
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png
new file mode 100644
index 0000000..e56ae5b
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png
new file mode 100644
index 0000000..8c3e363
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png
new file mode 100644
index 0000000..7b3e41b
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png
new file mode 100644
index 0000000..f163742
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png
new file mode 100644
index 0000000..37abb2f
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png
new file mode 100644
index 0000000..c6f6fc2
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png b/core/res/res/drawable-xlarge-mdpi/unlock_ring.png
index 1ac6d54..0363a8b 100644
--- a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png
+++ b/core/res/res/drawable-xlarge-mdpi/unlock_ring.png
Binary files differ
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
index 42636ad..52be82c 100644
--- a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
@@ -96,8 +96,8 @@
         android:gravity="center"
         >
         <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
-            android:layout_width="350dip"
-            android:layout_height="350dip"
+            android:layout_width="354dip"
+            android:layout_height="354dip"
             android:layout_marginTop="90dip"
             android:layout_marginRight="90dip"
           />
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
index aeed79b..e170a76 100644
--- a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
@@ -32,7 +32,7 @@
             android:orientation="vertical"
             android:gravity="center_vertical"
                 >
-        
+
         <include layout="@layout/keyguard_screen_status_port" />
 
         <!-- footer -->
@@ -92,8 +92,8 @@
         android:gravity="center"
         >
         <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
-            android:layout_width="350dip"
-            android:layout_height="350dip"
+            android:layout_width="354dip"
+            android:layout_height="354dip"
             android:layout_marginTop="50dip"
           />
     </LinearLayout>
diff --git a/core/res/res/layout/date_picker_dialog.xml b/core/res/res/layout/date_picker_dialog.xml
index 90de1f5..148e192 100644
--- a/core/res/res/layout/date_picker_dialog.xml
+++ b/core/res/res/layout/date_picker_dialog.xml
@@ -17,12 +17,6 @@
 */
 -->
 
-<!-- Note: We want the DatePicker to take as much space as possible, therefore
-           we set the width and height bigger than the max AlertDialog size
-           determined by our parent WeightedLinearLayout.
-
-           See: alert_dialog.xml
--->
 <DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/datePicker"
     android:layout_gravity="center_horizontal"
diff --git a/core/res/res/layout/day_picker.xml b/core/res/res/layout/day_picker.xml
index 46ab450..a030df8 100644
--- a/core/res/res/layout/day_picker.xml
+++ b/core/res/res/layout/day_picker.xml
@@ -25,7 +25,7 @@
     <TextView android:id="@+android:id/month_name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal" 
+        android:layout_gravity="center_horizontal"
         android:paddingTop="10dip"
         android:paddingBottom="10dip"
         style="@android:style/TextAppearance.Medium" />
@@ -37,7 +37,7 @@
         android:layout_marginBottom="6dip"
         android:layout_marginRight="2dip"
         android:layout_marginLeft="2dip"
-        android:gravity="center">
+        android:gravity="center" >
 
         <TextView android:layout_width="0dip"
             android:layout_height="wrap_content"
@@ -50,7 +50,6 @@
             android:layout_weight="1"
             android:gravity="center"
             style="?android:attr/dayPickerWeekDayViewStyle" />
-
         <TextView android:layout_width="0dip"
             android:layout_height="wrap_content"
             android:layout_weight="1"
@@ -89,12 +88,18 @@
 
     </LinearLayout>
 
+    <ImageView android:layout_width="match_parent"
+        android:layout_height="1dip"
+        android:scaleType="fitXY"
+        android:gravity="fill_horizontal"
+        android:src="?android:attr/dividerHorizontal" />
+
     <ListView android:id="@android:id/list"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:drawSelectorOnTop="false"
-            android:cacheColorHint="@android:color/transparent"
-            android:fastScrollEnabled="false"
-            android:overScrollMode="never" />
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:drawSelectorOnTop="false"
+        android:cacheColorHint="@android:color/transparent"
+        android:fastScrollEnabled="false"
+        android:overScrollMode="never" />
 
 </LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f10e90f..701b8ca 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2976,6 +2976,14 @@
 
     <!-- @hide -->
     <declare-styleable name="DayPickerWeekView">
+        <attr name="height" />
+        <attr name="weekStartDay" format="integer|reference" />
+        <attr name="weekDayCount" format="integer|reference" />
+        <attr name="showWeekNumber" format="boolean|reference" />
+        <attr name="weekSeperatorWidth" format="dimension" />
+        <attr name="textSize" />
+        <attr name="weekDayPadding" format="dimension" />
+        <attr name="selectedDayLineWidth" format="dimension" />
         <attr name="selectionBackgroundColor" format="color|reference" />
         <attr name="focusedMonthDateColor" format="color|reference" />
         <attr name="otherMonthDateColor" format="color|reference" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d780335..e48321c 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1343,9 +1343,9 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_sdcardWrite" product="default">Allows an application to write to the SD card.</string>
 
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
     <string name="permlab_mediaStorageWrite" product="default">modify/delete internal media storage contents</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
     <string name="permdesc_mediaStorageWrite" product="default">Allows an application to modify the contents of the internal media storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index fd51c0b..3e75261 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -475,12 +475,20 @@
 
     <!-- @hide -->
     <style name="Widget.DayPickerWeekView">
+        <item name="android:height">26dip</item>
+        <item name="android:weekStartDay">1</item>
+        <item name="android:weekDayCount">7</item>
+        <item name="android:showWeekNumber">true</item>
+        <item name="android:weekSeperatorWidth">1dip</item>
+        <item name="android:textSize">14dip</item>
+        <item name="android:weekDayPadding">0dip</item>
+        <item name="android:selectedDayLineWidth">6dip</item>
         <item name="android:selectionBackgroundColor">#330099FF</item>
         <item name="android:focusedMonthDateColor">#FFFFFFFF</item>
         <item name="android:otherMonthDateColor">#66FFFFFF</item>
         <item name="android:weekNumberColor">#33FFFFFF</item>
         <item name="android:gridLinesColor">#19FFFFFF</item>
-        <item name="selectedDayLine">@android:drawable/simple_week_dayline_holo_light</item>
+        <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo_dark</item>
     </style>
 
     <!-- @hide -->
@@ -1436,13 +1444,13 @@
     </style>
 
     <!-- @hide -->
-    <style name="Widget.Holo.DayPickerWeekView">
+    <style name="Widget.Holo.DayPickerWeekView" parent="Widget.DayPickerWeekView">
         <item name="android:selectionBackgroundColor">#330099FF</item>
         <item name="android:focusedMonthDateColor">#FFFFFFFF</item>
         <item name="android:otherMonthDateColor">#66FFFFFF</item>
         <item name="android:weekNumberColor">#33FFFFFF</item>
         <item name="android:gridLinesColor">#19FFFFFF</item>
-        <item name="selectedDayLine">@android:drawable/simple_week_dayline_holo_light</item>
+        <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo_dark</item>
     </style>
 
     <style name="Widget.Holo.ImageButton" parent="Widget.ImageButton">
@@ -1811,16 +1819,22 @@
 
     <!-- @hide -->
     <style name="Widget.Holo.Light.DayPickerWeekView" parent="Widget.DayPickerWeekView">
+        <item name="android:selectionBackgroundColor">#7F080030</item>
+        <item name="android:focusedMonthDateColor">#FF000000</item>
+        <item name="android:otherMonthDateColor">#7F08002B</item>
+        <item name="android:weekNumberColor">#7F080021</item>
+        <item name="android:gridLinesColor">#7F08002A</item>
+        <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo_light</item>
     </style>
 
     <!-- @hide -->
-    <style name="Widget.Holo.Light.ImageButton.NumberPickerUpButton" parent="Widget.Holo.Light.ImageButton.NumberPickerUpButton">
-        <item name="android:background">@android:drawable/timepicker_up_btn_holo_light</item>
+    <style name="Widget.Holo.Light.ImageButton.NumberPickerUpButton" parent="Widget.Holo.ImageButton.NumberPickerUpButton">
+        <item name="android:src">@android:drawable/timepicker_up_btn_holo_light</item>
     </style>
 
     <!-- @hide -->
     <style name="Widget.Holo.Light.ImageButton.NumberPickerDownButton" parent="Widget.Holo.ImageButton.NumberPickerDownButton">
-        <item name="android:background">@android:drawable/timepicker_down_btn_holo_light</item>
+        <item name="android:src">@android:drawable/timepicker_down_btn_holo_light</item>
     </style>
 
     <!-- @hide -->
diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd
index f503749..5738bd6 100644
--- a/docs/html/guide/practices/screens_support.jd
+++ b/docs/html/guide/practices/screens_support.jd
@@ -195,10 +195,10 @@
 Illustration of how the Android platform maps actual screen densities and sizes
 to generalized density and size configurations. </p>
 
-<p>Although the platform lets your application provide layouts and resources for
-generalized size-density configurations, you do not necessarily need to do write
-custom code or provide custom resources for each of the nine supported
-configurations. The platform provides robust compatibility features, described
+<p>Although the platform lets your application provide customized resources for
+the various size and density configurations, you do not need to do write
+custom code or provide custom resources for every combination of screen size and density.
+The platform provides robust compatibility features, described
 in the sections below, that can handle most of the work of rendering your
 application on any device screen, provided that you've implemented your
 application UI properly. For more information about how to implement a UI that
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index 79ce669..129462e 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -32,12 +32,16 @@
     for different states (for example, to use a different image when a button is pressed).
     Creates a {@link android.graphics.drawable.StateListDrawable}.</dd>
   <dt><a href="#LevelList">Level List</a></dt>
-    <dd>An XML file that defines a Drawable that manages a number of alternate Drawables, each
+    <dd>An XML file that defines a drawable that manages a number of alternate Drawables, each
 assigned a maximum numerical value. Creates a {@link
 android.graphics.drawable.LevelListDrawable}.</dd>
   <dt><a href="#Transition">Transition Drawable</a></dt>
-    <dd>An XML file that defines a Drawable that can cross-fade between two drawable resources.
+    <dd>An XML file that defines a drawable that can cross-fade between two drawable resources.
 Creates a {@link android.graphics.drawable.TransitionDrawable}.</dd>
+  <dt><a href="#Inset">Inset Drawable</a></dt>
+    <dd>An XML file that defines a drawable that insets another drawable by a specified distance.
+This is useful when a View needs a background drawble that is smaller than the View's actual
+bounds.</dd>
   <dt><a href="#Clip">Clip Drawable</a></dt>
     <dd>An XML file that defines a drawable that clips another Drawable based on this Drawable's
 current level value. Creates a {@link android.graphics.drawable.ClipDrawable}.</dd>
@@ -893,7 +897,7 @@
 <dd>
 <pre class="stx">
 &lt;?xml version="1.0" encoding="utf-8"?>
-&lt;<a href="#transition-element">layer-list</a>
+&lt;<a href="#transition-element">transition</a>
 xmlns:android="http://schemas.android.com/apk/res/android" &gt;
     &lt;<a href="#transition-item-element">item</a>
         android:drawable="@[package:]drawable/<em>drawable_resource</em>"
@@ -994,13 +998,10 @@
 
 
 
-
-
-
 <h2 id="Inset">Inset Drawable</h2>
 
-<p>A drawable defined in XML that insets another drawable by a specified distance. This is used when
-a View needs a background that is smaller than the View's actual bounds.</p>
+<p>A drawable defined in XML that insets another drawable by a specified distance. This is useful
+when a View needs a background that is smaller than the View's actual bounds.</p>
 
 <dl class="xml">
 
diff --git a/docs/html/guide/topics/resources/menu-resource.jd b/docs/html/guide/topics/resources/menu-resource.jd
index 7bcd78a..33c782b 100644
--- a/docs/html/guide/topics/resources/menu-resource.jd
+++ b/docs/html/guide/topics/resources/menu-resource.jd
@@ -39,7 +39,10 @@
           android:title="<em>string</em>"
           android:titleCondensed="<em>string</em>"
           android:icon="@[package:]drawable/<em>drawable_resource_name</em>"
+          android:onClick="<em>method name</em>"
           android:showAsAction=["ifRoom" | "never" | "withText" | "always"]
+          android:actionLayout="@[package:]layout/<em>layout_resource_name</em>"
+          android:actionViewClass="<em>class name</em>"
           android:alphabeticShortcut="<em>string</em>"
           android:numericShortcut="<em>string</em>"
           android:checkable=["true" | "false"]
@@ -74,8 +77,8 @@
       <p class="caps">attributes:</p>
       <dl class="atn-list">
         <dt><code>xmlns:android</code></dt>
-          <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
-          <code>"http://schemas.android.com/apk/res/android"</code>.
+          <dd><em>XML namespace</em>. <strong>Required.</strong> Defines the XML namespace, which
+must be <code>"http://schemas.android.com/apk/res/android"</code>.
       </dl>
     </dd>
 
@@ -89,13 +92,26 @@
 <code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new
 ID.</dd>
         <dt><code>android:title</code></dt>
-          <dd><em>String</em>. The menu title.</dd>
+          <dd><em>String resource</em>. The menu title as a string resource or raw string.</dd>
         <dt><code>android:titleCondensed</code></dt>
-          <dd><em>String</em>. A condensed title, for situations in which the normal title is
-too long.</dd>
+          <dd><em>String resource</em>. A condensed title as a string resource or a raw string. This
+title is used for situations in which the normal title is too long.</dd>
+
         <dt><code>android:icon</code></dt>
           <dd><em>Drawable resource</em>. An image to be used as the menu item icon.</dd>
 
+        <dt><code>android:onClick</code></dt>
+          <dd><em>Method name</em>. The method to call when this menu item is clicked. The
+method must be declared in the activity as public and accept a {@link android.view.MenuItem} as its
+only parameter, which indicates the item clicked. This method takes precedence over the standard
+callback to {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()}. See the
+example at the bottom.
+          <p class="warning"><strong>Warning:</strong> If you obfuscate your code using <a
+href="{@docRoot}guide/developing/tools/proguard.html">ProGuard</a> (or a similar tool),
+be sure to exclude the method you specify in this attribute from renaming, because it can break the
+functionality.</p>
+          <p>Introduced in API Level HONEYCOMB.</p></dd>
+
         <dt><code>android:showAsAction</code></dt>
           <dd><em>Keyword</em>. When and how this item should appear as an action item in the Action
 Bar. A menu item can appear as an action item only when the activity includes an {@link
@@ -118,6 +134,24 @@
           <p>Introduced in API Level HONEYCOMB.</p>
         </dd>
 
+        <dt><code>android:actionViewLayout</code></dt>
+          <dd><em>Layout resource</em>. A layout to use as the action view.
+          <p>See <a href="{@docRoot}guide/topics/ui/actionbar.html">Using the Action Bar</a> for
+more information.</p>
+          <p>Introduced in API Level HONEYCOMB.</p></dd>
+
+        <dt><code>android:actionViewClassName</code></dt>
+          <dd><em>Class name</em>. A fully-qualified class name for the {@link android.view.View}
+to use as the action view.
+          <p>See <a href="{@docRoot}guide/topics/ui/actionbar.html">Using the Action Bar</a> for
+more information.</p>
+          <p class="warning"><strong>Warning:</strong> If you obfuscate your code using <a
+href="{@docRoot}guide/developing/tools/proguard.html">ProGuard</a> (or a similar tool),
+be sure to exclude the class you specify in this attribute from renaming, because it can break the
+functionality.</p>
+          <p>Introduced in API Level HONEYCOMB.</p></dd>
+
+
         <dt><code>android:alphabeticShortcut</code></dt>
           <dd><em>Char</em>. A character for the alphabetic shortcut key.</dd>
         <dt><code>android:numericShortcut</code></dt>
@@ -208,9 +242,11 @@
           android:showAsAction="ifRoom|withText"/>
     &lt;group android:id="@+id/group">
         &lt;item android:id="@+id/group_item1"
+              android:onClick="onGroupItemClick"
               android:title="@string/group_item1"
               android:icon="@drawable/group_item1_icon" />
         &lt;item android:id="@+id/group_item2"
+              android:onClick="onGroupItemClick"
               android:title="G@string/group_item2"
               android:icon="@drawable/group_item2_icon" />
     &lt;/group>
@@ -224,13 +260,20 @@
     &lt;/item>
 &lt;/menu>
 </pre>
-    <p>This application code will inflate the menu from the {@link
-android.app.Activity#onCreateOptionsMenu(Menu)} callback:</p>
+    <p>The following application code inflates the menu from the {@link
+android.app.Activity#onCreateOptionsMenu(Menu)} callback and also declares the on-click
+callback for two of the items:</p>
 <pre>
 public boolean onCreateOptionsMenu(Menu menu) {
-  MenuInflater inflater = getMenuInflater();
-  inflater.inflate(R.menu.example_menu, menu);
-  return true;
+    MenuInflater inflater = getMenuInflater();
+    inflater.inflate(R.menu.example_menu, menu);
+    return true;
+}
+
+public void onGroupItemClick(MenuItem item) {
+    // One of the group items (using the onClick attribute) was clicked
+    // The item parameter passed here indicates which item it is
+    // All other menu item clicks are handled by {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()}
 }
 </pre>
 <p class="note"><strong>Note:</strong> The {@code android:showAsAction} attribute is
diff --git a/docs/html/sdk/adt_download.html b/docs/html/sdk/adt_download.html
new file mode 100644
index 0000000..5ba2ef5
--- /dev/null
+++ b/docs/html/sdk/adt_download.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<meta http-equiv="refresh" content="0;url=http://developer.android.com/sdk/eclipse-adt.html">
+<title>Redirecting...</title>
+</head>
+<body>
+<p>You should be redirected. Please <a
+href="http://developer.android.com/sdk/eclipse-adt.html">click here</a>.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/docs/html/sdk/adt_download.jd b/docs/html/sdk/adt_download.jd
deleted file mode 100644
index 33e480a..0000000
--- a/docs/html/sdk/adt_download.jd
+++ /dev/null
@@ -1,127 +0,0 @@
-page.title=Download the ADT Zip File
-@jd:body
-
-<p>If you are unable to download the ADT plugin through <a
-href="{@docRoot}sdk/eclipse-adt.html#installing">setting up a remote
-update site</a> in Eclipse, you can download the ADT zip file and install it 
-from your computer (archived site) instead. </p>
-
-<p>If you use this approach, in order to update the plugin, you will need to 
-download the latest version from this page, uninstall the old version from 
-Eclipse, then install the new version. For more details on the procedure, 
-see <a 
-href="{@docRoot}sdk/eclipse-adt.html#troubleshooting">Troubleshooting
-ADT Installation</a>.</p>
-
-<table class="download">
-  <tr>
-    <th><nobr>ADT Version</nobr></th>
-    <th>Package</th>
-    <th>Size</th>
-    <th>Md5 Checksum</th>
-    <th>Notes</th>
-  </tr>
-  <tr>
-     <td>8.0.0</td>
-     <td><a href="http://dl-ssl.google.com/android/ADT-8.0.0.zip">ADT-8.0.0.zip</a></td>
-     <td><nobr> bytes</nobr></td>
-     <td></td>
-     <td>Requires SDK Tools, Revision 8 <em><nobr>November 2010</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.9.9</td>
-     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.9.zip">ADT-0.9.9.zip</a></td>
-     <td><nobr>8301681 bytes</nobr></td>
-     <td>7deff0c9b25940a74cea7a0815a3bc36</td>
-     <td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.9.8</td>
-     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.8.zip">ADT-0.9.8.zip</a></td>
-     <td><nobr>8301417 bytes</nobr></td>
-     <td>27e0de800512f13feae46fb554e6ee2f</td>
-     <td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.9.7</td>
-     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.7.zip">ADT-0.9.7.zip</a></td>
-     <td><nobr>8033750 bytes</nobr></td>
-     <td>de2431c8d4786d127ae5bfc95b4605df</td>
-     <td>Requires SDK Tools, Revision 5 <em><nobr>May 2010</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.9.6</td>
-     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.6.zip">ADT-0.9.6.zip</a></td>
-     <td><nobr>7456339 bytes</nobr></td>
-     <td>ea45d271be52b87b5dd1c9fb17536223</td>
-     <td>Requires SDK Tools, Revision 5 <em><nobr>March 2010</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.9.5</td>
-     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.5.zip">ADT-0.9.5.zip</a></td>
-     <td><nobr>3372982 bytes</nobr></td>
-     <td>227ec538359fbe417ccde7f0ad614a96</td>
-     <td>Requires SDK Tools, Revision 4 <em><nobr>December 2009</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.9.4</td>
-     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.4.zip">ADT-0.9.4.zip</a></td>
-     <td><nobr>3367536 bytes</nobr></td>
-     <td>4cdecd72b3e28022d8a55891f13e7d43</td>
-     <td>Requires SDK Tools, Revision 3 <em><nobr>October 2009</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.9.3</td>
-     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.3.zip">ADT-0.9.3.zip</a></td>
-     <td><nobr>3252487 bytes</nobr></td>
-     <td>c296488ac35772667c0f49e822156979</td>
-     <td>Required for users of Android 1.6 SDK only . Updated from 0.9.1. <em><nobr>September 2009</nobr></em></td>
-  </tr>
-</table>
-
-
-<h4>Obsolete Versions of ADT</h4>
-
-<p>The table below lists older versions of the ADT Plugin that are no longer
-supported. If you are developing applications that are intended to be deployable
-to Android-powered devices, make sure that you upgrade to the most current SDK
-release available and use the most current version of the ADT Plugin, as listed
-in the section above.</p>
-
-<p>If you are not sure what version of ADT is installed in your Eclipse
-environment, open Eclipse and from the main menu select <strong>Help</strong>
-&gt; <strong>About Eclipse</strong> &gt; <strong>Features Details</strong>.
-Locate "com.android.ide.eclipse.adt" in the Feature ID column and look at its
-version number.</p>
-
-<table>
-  <tr>
-    <th><nobr>ADT Version</nobr></th>
-    <th>Notes</th>
-  </tr>
-  <tr>
-     <td>0.9.1</td>
-     <td>Required for users of Android 1.5 SDK. Updated from 0.9.0. <em><nobr>6 May 2009</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.8.0</td>
-     <td>Required for users of Android 1.0/1.1 SDKs. <em><nobr>23 Sep 2008</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.7.1</td>
-     <td>Required for users of the Android 0.9 SDK beta. <em><nobr>18 Aug 2008</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.4.0</td>
-     <td>Required for users of the Android M5 Early Look SDK. <em><nobr>12 Feb 2008</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.3.3</td>
-     <td>Required for users of the Android M3-RC37 Early Look SDK. <em><nobr>14 Dec 2007</nobr></em></td>
-  </tr>
-  <tr>
-     <td>0.3.1</td>
-     <td>Initial Release. Required for users of the Android M3-RC20/22 SDKs.<em><nobr>21 Nov 2007</nobr></em></td>
-  </tr>
-</table>
-</p>
diff --git a/docs/html/sdk/android-2.3.jd b/docs/html/sdk/android-2.3.jd
index 33affcb..a0b7c99 100644
--- a/docs/html/sdk/android-2.3.jd
+++ b/docs/html/sdk/android-2.3.jd
@@ -469,7 +469,7 @@
 indicates that a specific process is running something that is considered to be
 actively perceptible to the user. An example would be an application performing
 background music playback.</li>
-<li>The {@link android.app.Activity#setPersistent(boolean)} method to mark an
+<li>The Activity.setPersistent(boolean) method to mark an
 Activity as persistent is now deprecated and the implementation is a no-op.</li>
 </ul>
 </li>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index cc0260a..0144eb4 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -1,9 +1,9 @@
 page.title=ADT Plugin for Eclipse
 sdk.preview=0
-adt.zip.version=0.9.9
-adt.zip.download=ADT-0.9.9.zip
-adt.zip.bytes=8301681
-adt.zip.checksum=7deff0c9b25940a74cea7a0815a3bc36
+adt.zip.version=8.0.1
+adt.zip.download=ADT-8.0.1.zip
+adt.zip.bytes=8724909
+adt.zip.checksum=0e62185279083ddc01f18098ce7ba2d1
 
 @jd:body
 
@@ -98,7 +98,37 @@
 
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
-        <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+        <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px"
+width="9px" />
+ADT 8.0.1</a> <em>(December 2010)</em>
+  <div class="toggleme">
+
+<dl>
+
+<dt>Dependencies:</dt>
+
+<p><p>ADT 8.0.1 is designed for use with SDK Tools r8. If you haven't
+already installed SDK Tools r8 into your SDK, use the Android SDK and AVD Manager to do
+so.</p></dd>
+
+<dt>General notes:</dt>
+<dd>
+<ul>
+  <li>This is a quick follow-up to ADT 8.0.0 to fix some bugs.</li>
+  <li>Fixes an issue in which projects failed to compile, citing a dex error.</li>
+  <li>Better ProGuard error reporting when exporting applications for release.</li>
+</ul>
+<p>Also see the recent release notes for 8.0.0, below.</p>
+</dd>
+</dl>
+ </div>
+</div>
+
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
+width="9px" />
 ADT 8.0.0</a> <em>(December 2010)</em>
   <div class="toggleme">
 
@@ -322,7 +352,7 @@
 
 <dd><p>ADT 0.9.5 requires features provided in SDK Tools r4 or higher. If you install
 ADT 0.9.5, which is highly recommended, you should use the Android SDK and AVD
-Manager to download thye latest SDK Tools into your SDK. For more information,
+Manager to download the latest SDK Tools into your SDK. For more information,
 see <a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.</p>
 </dd>
 
diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd
index a359dad..401dfe3 100644
--- a/docs/html/sdk/requirements.jd
+++ b/docs/html/sdk/requirements.jd
@@ -51,8 +51,7 @@
     <ul>
       <li><a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">JDK 5 or JDK
 6</a> (JRE alone is not sufficient)</li>
-      <li><a href="http://ant.apache.org/">Apache Ant</a> 1.6.5 or later for
-Linux and Mac, 1.7 or later for Windows</li>
+      <li><a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later</li>
       <li><strong>Not</strong> compatible with Gnu Compiler for Java (gcj)</li>
     </ul>
   </li>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 3366c5c..9c7ad89 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -114,7 +114,7 @@
       <span style="display:none" class="zh-TW"></span>
       </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 8.0.0
+      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 8.0.1
       <span style="display:none" class="de"></span>
       <span style="display:none" class="es"></span>
       <span style="display:none" class="fr"></span>
diff --git a/docs/html/sdk/tools-notes.jd b/docs/html/sdk/tools-notes.jd
index 9316fae..f8d7071 100644
--- a/docs/html/sdk/tools-notes.jd
+++ b/docs/html/sdk/tools-notes.jd
@@ -74,6 +74,9 @@
 designed for use with ADT 8.0.0 and later. After installing SDK Tools r8, we
 highly recommend updating your ADT Plugin to 8.0.0.</p>
 
+<p>If you are developing outside Eclipse, you must have <a href="http://ant.apache.org/">Apache
+Ant</a> 1.8 or later.</p>
+
 <p>Also note that SDK Tools r8 requires a new SDK component called
 <em>Platform-tools</em>. The new Platform-tools component lets all SDK platforms
 (Android 2.1, Android 2.2, and so on) use the same (latest) version of build
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 30475bd..1bddbae 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -452,13 +452,9 @@
         return new Allocation(id, rs, t, usage);
     }
 
-    static public Allocation createFromBitmap(RenderScript rs, Bitmap b,
-                                              Element dstFmt, boolean genMips) {
-        MipmapControl mc = MipmapControl.MIPMAP_NONE;
-        if (genMips) {
-            mc = MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
-        }
-        return createFromBitmap(rs, b, mc, USAGE_GRAPHICS_TEXTURE);
+    static public Allocation createFromBitmap(RenderScript rs, Bitmap b) {
+        return createFromBitmap(rs, b, MipmapControl.MIPMAP_NONE,
+                                USAGE_GRAPHICS_TEXTURE);
     }
 
     static public Allocation createCubemapFromBitmap(RenderScript rs, Bitmap b,
@@ -500,14 +496,9 @@
     }
 
     static public Allocation createCubemapFromBitmap(RenderScript rs, Bitmap b,
-                                                     Element dstFmt,
-                                                     boolean genMips,
                                                      CubemapLayout layout) {
-        MipmapControl mc = MipmapControl.MIPMAP_NONE;
-        if (genMips) {
-            mc = MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
-        }
-        return createCubemapFromBitmap(rs, b, mc, layout, USAGE_GRAPHICS_TEXTURE);
+        return createCubemapFromBitmap(rs, b, MipmapControl.MIPMAP_NONE,
+                                       layout, USAGE_GRAPHICS_TEXTURE);
     }
 
     static public Allocation createFromBitmapResource(RenderScript rs,
@@ -525,6 +516,15 @@
 
     static public Allocation createFromBitmapResource(RenderScript rs,
                                                       Resources res,
+                                                      int id) {
+        return createFromBitmapResource(rs, res, id,
+                                        MipmapControl.MIPMAP_NONE,
+                                        USAGE_GRAPHICS_TEXTURE);
+    }
+
+/*
+    static public Allocation createFromBitmapResource(RenderScript rs,
+                                                      Resources res,
                                                       int id,
                                                       Element dstFmt,
                                                       boolean genMips) {
@@ -534,7 +534,7 @@
         }
         return createFromBitmapResource(rs, res, id, mc, USAGE_GRAPHICS_TEXTURE);
     }
-
+*/
     static public Allocation createFromString(RenderScript rs,
                                               String str,
                                               int usage) {
diff --git a/graphics/java/android/renderscript/Sampler.java b/graphics/java/android/renderscript/Sampler.java
index 9fbc09a..45a3949 100644
--- a/graphics/java/android/renderscript/Sampler.java
+++ b/graphics/java/android/renderscript/Sampler.java
@@ -40,6 +40,7 @@
         NEAREST (0),
         LINEAR (1),
         LINEAR_MIP_LINEAR (2),
+        LINEAR_MIP_NEAREST (5),
         WRAP (3),
         CLAMP (4);
 
@@ -201,7 +202,8 @@
         public void setMin(Value v) {
             if (v == Value.NEAREST ||
                 v == Value.LINEAR ||
-                v == Value.LINEAR_MIP_LINEAR) {
+                v == Value.LINEAR_MIP_LINEAR ||
+                v == Value.LINEAR_MIP_NEAREST) {
                 mMin = v;
             } else {
                 throw new IllegalArgumentException("Invalid value");
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index e7f1d6d..c0963a6 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -40,7 +40,8 @@
 enum player_type {
     PV_PLAYER = 1,
     SONIVOX_PLAYER = 2,
-    STAGEFRIGHT_PLAYER = 4,
+    STAGEFRIGHT_PLAYER = 3,
+    NU_PLAYER = 4,
     // Test players are available only in the 'test' and 'eng' builds.
     // The shared library with the test player is passed passed as an
     // argument to the 'test:' url in the setDataSource call.
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
new file mode 100644
index 0000000..940470d
--- /dev/null
+++ b/include/media/stagefright/ACodec.h
@@ -0,0 +1,152 @@
+#ifndef A_CODEC_H_
+
+#define A_CODEC_H_
+
+#include <stdint.h>
+#include <android/native_window.h>
+#include <media/IOMX.h>
+#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+
+namespace android {
+
+struct ABuffer;
+struct MemoryDealer;
+
+struct ACodec : public AHierarchicalStateMachine {
+    enum {
+        kWhatFillThisBuffer    = 'fill',
+        kWhatDrainThisBuffer   = 'drai',
+        kWhatEOS               = 'eos ',
+        kWhatShutdownCompleted = 'scom',
+        kWhatFlushCompleted    = 'fcom',
+    };
+
+    ACodec();
+
+    void setNotificationMessage(const sp<AMessage> &msg);
+    void initiateSetup(const sp<AMessage> &msg);
+    void signalFlush();
+    void signalResume();
+    void initiateShutdown();
+
+protected:
+    virtual ~ACodec();
+
+private:
+    struct BaseState;
+    struct UninitializedState;
+    struct LoadedToIdleState;
+    struct IdleToExecutingState;
+    struct ExecutingState;
+    struct OutputPortSettingsChangedState;
+    struct ExecutingToIdleState;
+    struct IdleToLoadedState;
+    struct ErrorState;
+    struct FlushingState;
+
+    enum {
+        kWhatSetup                   = 'setu',
+        kWhatOMXMessage              = 'omx ',
+        kWhatInputBufferFilled       = 'inpF',
+        kWhatOutputBufferDrained     = 'outD',
+        kWhatShutdown                = 'shut',
+        kWhatFlush                   = 'flus',
+        kWhatResume                  = 'resm',
+        kWhatDrainDeferredMessages   = 'drai',
+    };
+
+    enum {
+        kPortIndexInput  = 0,
+        kPortIndexOutput = 1
+    };
+
+    struct BufferInfo {
+        enum Status {
+            OWNED_BY_US,
+            OWNED_BY_COMPONENT,
+            OWNED_BY_UPSTREAM,
+            OWNED_BY_DOWNSTREAM,
+            OWNED_BY_NATIVE_WINDOW,
+        };
+
+        IOMX::buffer_id mBufferID;
+        Status mStatus;
+
+        sp<ABuffer> mData;
+        sp<GraphicBuffer> mGraphicBuffer;
+    };
+
+    sp<AMessage> mNotify;
+
+    sp<UninitializedState> mUninitializedState;
+    sp<LoadedToIdleState> mLoadedToIdleState;
+    sp<IdleToExecutingState> mIdleToExecutingState;
+    sp<ExecutingState> mExecutingState;
+    sp<OutputPortSettingsChangedState> mOutputPortSettingsChangedState;
+    sp<ExecutingToIdleState> mExecutingToIdleState;
+    sp<IdleToLoadedState> mIdleToLoadedState;
+    sp<ErrorState> mErrorState;
+    sp<FlushingState> mFlushingState;
+
+    AString mComponentName;
+    sp<IOMX> mOMX;
+    IOMX::node_id mNode;
+    sp<MemoryDealer> mDealer[2];
+
+    sp<ANativeWindow> mNativeWindow;
+
+    Vector<BufferInfo> mBuffers[2];
+    bool mPortEOS[2];
+
+    List<sp<AMessage> > mDeferredQueue;
+
+    status_t allocateBuffersOnPort(OMX_U32 portIndex);
+    status_t freeBuffersOnPort(OMX_U32 portIndex);
+    status_t freeBuffer(OMX_U32 portIndex, size_t i);
+
+    status_t allocateOutputBuffersFromNativeWindow();
+    status_t cancelBufferToNativeWindow(BufferInfo *info);
+    status_t freeOutputBuffersOwnedByNativeWindow();
+    BufferInfo *dequeueBufferFromNativeWindow();
+
+    BufferInfo *findBufferByID(
+            uint32_t portIndex, IOMX::buffer_id bufferID,
+            ssize_t *index = NULL);
+
+    void setComponentRole(bool isEncoder, const char *mime);
+    void configureCodec(const char *mime, const sp<AMessage> &msg);
+
+    status_t setVideoPortFormatType(
+            OMX_U32 portIndex,
+            OMX_VIDEO_CODINGTYPE compressionFormat,
+            OMX_COLOR_FORMATTYPE colorFormat);
+
+    status_t setSupportedOutputFormat();
+
+    status_t setupVideoDecoder(
+            const char *mime, int32_t width, int32_t height);
+
+    status_t setVideoFormatOnPort(
+            OMX_U32 portIndex,
+            int32_t width, int32_t height,
+            OMX_VIDEO_CODINGTYPE compressionFormat);
+
+    status_t setupAACDecoder(int32_t numChannels, int32_t sampleRate);
+    status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
+
+    status_t initNativeWindow();
+
+    // Returns true iff all buffers on the given port have status OWNED_BY_US.
+    bool allYourBuffersAreBelongToUs(OMX_U32 portIndex);
+
+    bool allYourBuffersAreBelongToUs();
+
+    void deferMessage(const sp<AMessage> &msg);
+    void processDeferredMessages();
+
+    DISALLOW_EVIL_CONSTRUCTORS(ACodec);
+};
+
+}  // namespace android
+
+#endif  // A_CODEC_H_
diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
index 69021d8c..eb5e494 100644
--- a/include/media/stagefright/foundation/ADebug.h
+++ b/include/media/stagefright/foundation/ADebug.h
@@ -32,6 +32,7 @@
 #define CHECK(condition)                                \
     LOG_ALWAYS_FATAL_IF(                                \
             !(condition),                               \
+            "%s",                                       \
             __FILE__ ":" LITERAL_TO_STRING(__LINE__)    \
             " CHECK(" #condition ") failed.")
 
@@ -58,10 +59,12 @@
     do {                                                                \
         AString ___res = Compare_##suffix(x, y);                        \
         if (!___res.empty()) {                                          \
-            LOG_ALWAYS_FATAL(                                           \
-                    __FILE__ ":" LITERAL_TO_STRING(__LINE__)            \
-                    " CHECK_" #suffix "( " #x "," #y ") failed: %s",    \
-                    ___res.c_str());                                    \
+            AString ___full =                                           \
+                __FILE__ ":" LITERAL_TO_STRING(__LINE__)                \
+                    " CHECK_" #suffix "( " #x "," #y ") failed: ";      \
+            ___full.append(___res);                                     \
+                                                                        \
+            LOG_ALWAYS_FATAL("%s", ___full.c_str());                    \
         }                                                               \
     } while (false)
 
diff --git a/include/media/stagefright/foundation/AHierarchicalStateMachine.h b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
new file mode 100644
index 0000000..b5786fb
--- /dev/null
+++ b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
@@ -0,0 +1,49 @@
+#ifndef A_HIERARCHICAL_STATE_MACHINE_H_
+
+#define A_HIERARCHICAL_STATE_MACHINE_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct AState : public RefBase {
+    AState(const sp<AState> &parentState = NULL);
+
+    sp<AState> parentState();
+
+protected:
+    virtual ~AState();
+
+    virtual void stateEntered();
+    virtual void stateExited();
+
+    virtual bool onMessageReceived(const sp<AMessage> &msg) = 0;
+
+private:
+    friend struct AHierarchicalStateMachine;
+
+    sp<AState> mParentState;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AState);
+};
+
+struct AHierarchicalStateMachine : public AHandler {
+    AHierarchicalStateMachine();
+
+protected:
+    virtual ~AHierarchicalStateMachine();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+    // Only to be called in response to a message.
+    void changeState(const sp<AState> &state);
+
+private:
+    sp<AState> mState;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AHierarchicalStateMachine);
+};
+
+}  // namespace android
+
+#endif  // A_HIERARCHICAL_STATE_MACHINE_H_
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 2fbdddc..941f6b9 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -40,6 +40,8 @@
     void setTarget(ALooper::handler_id target);
     ALooper::handler_id target() const;
 
+    void clear();
+
     void setInt32(const char *name, int32_t value);
     void setInt64(const char *name, int64_t value);
     void setSize(const char *name, size_t value);
@@ -106,7 +108,6 @@
     Item mItems[kMaxNumItems];
     size_t mNumItems;
 
-    void clear();
     Item *allocateItem(const char *name);
     void freeItem(Item *item);
     const Item *findItem(const char *name, Type type) const;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e99f73a..4cca0c8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -839,6 +839,7 @@
     mColorA = mColorR = mColorG = mColorB = 0.0f;
     mTextureUnit = 0;
     mTrackDirtyRegions = true;
+    mTexCoordsSlot = -1;
 }
 
 void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
@@ -847,7 +848,11 @@
 }
 
 void OpenGLRenderer::setupDrawColor(int color) {
-    mColorA = ((color >> 24) & 0xFF) / 255.0f;
+    setupDrawColor(color, (color >> 24) & 0xFF);
+}
+
+void OpenGLRenderer::setupDrawColor(int color, int alpha) {
+    mColorA = alpha / 255.0f;
     const float a = mColorA / 255.0f;
     mColorR = a * ((color >> 16) & 0xFF);
     mColorG = a * ((color >>  8) & 0xFF);
@@ -926,6 +931,10 @@
     }
 }
 
+void OpenGLRenderer::setupDrawModelViewIdentity() {
+    mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform);
+}
+
 void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
         bool ignoreTransform, bool ignoreModelView) {
     if (!ignoreModelView) {
@@ -967,6 +976,12 @@
     }
 }
 
+void OpenGLRenderer::setupDrawShaderIdentityUniforms() {
+    if (mShader) {
+        mShader->setupProgram(mCaches.currentProgram, mIdentity, *mSnapshot, &mTextureUnit);
+    }
+}
+
 void OpenGLRenderer::setupDrawColorFilterUniforms() {
     if (mColorFilter) {
         mColorFilter->setupProgram(mCaches.currentProgram);
@@ -995,7 +1010,9 @@
     }
     glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
             gMeshStride, vertices);
-    glVertexAttribPointer(mTexCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
+    if (mTexCoordsSlot > 0) {
+        glVertexAttribPointer(mTexCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
+    }
 }
 
 void OpenGLRenderer::finishDrawTexture() {
@@ -1167,38 +1184,10 @@
     // it draws an unscaled 1px wide line
     const bool isHairLine = paint->getStrokeWidth() == 0.0f;
 
-    setupDraw();
-
     int alpha;
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    uint32_t color = paint->getColor();
-    const GLfloat a = alpha / 255.0f;
-    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
-    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
-    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
-
-    // Used only with AA lines
-    GLuint textureUnit = 0;
-
-    // Describe the required shaders
-    ProgramDescription description;
-    const bool setColor = description.setColor(r, g, b, a);
-
-    if (mShader) {
-        mShader->describe(description, mCaches.extensions);
-    }
-    if (mColorFilter) {
-        mColorFilter->describe(description, mCaches.extensions);
-    }
-
-    // Setup the blending mode
-    chooseBlending(a < 1.0f || (mShader && mShader->blend()), mode, description);
-
-    // We're not drawing with VBOs here
-    mCaches.unbindMeshBuffer();
-
     int verticesCount = count >> 2;
     if (!isHairLine) {
         // TODO: AA needs more vertices
@@ -1211,23 +1200,17 @@
     TextureVertex lines[verticesCount];
     TextureVertex* vertex = &lines[0];
 
-    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gMeshStride, vertex);
-
-    // Build and use the appropriate shader
-    useProgram(mCaches.programCache.get(description));
-    mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform);
-
-    if (!mShader || (mShader && setColor)) {
-        mCaches.currentProgram->setColor(r, g, b, a);
-    }
-
-    if (mShader) {
-        mShader->setupProgram(mCaches.currentProgram, mIdentity, *mSnapshot, &textureUnit);
-    }
-    if (mColorFilter) {
-        mColorFilter->setupProgram(mCaches.currentProgram);
-    }
+    setupDraw();
+    setupDrawColor(paint->getColor(), alpha);
+    setupDrawColorFilter();
+    setupDrawShader();
+    setupDrawBlending(mode);
+    setupDrawProgram();
+    setupDrawModelViewIdentity();
+    setupDrawColorUniforms();
+    setupDrawColorFilterUniforms();
+    setupDrawShaderIdentityUniforms();
+    setupDrawMesh(vertex);
 
     if (!isHairLine) {
         // TODO: Handle the AA case
@@ -1274,6 +1257,7 @@
             TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
             TextureVertex::set(vertex++, points[i + 2], points[i + 3], 0.0f, 0.0f);
         }
+
         glLineWidth(1.0f);
         glDrawArrays(GL_LINES, 0, verticesCount);
     }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index c3c6ad6..8f93f5b 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -362,6 +362,7 @@
      */
     void setupDrawWithTexture(bool isAlpha8 = false);
     void setupDrawColor(int color);
+    void setupDrawColor(int color, int alpha);
     void setupDrawColor(float r, float g, float b, float a);
     void setupDrawAlpha8Color(int color, int alpha);
     void setupDrawAlpha8Color(float r, float g, float b, float a);
@@ -373,17 +374,19 @@
             bool swapSrcDst = false);
     void setupDrawProgram();
     void setupDrawDirtyRegionsDisabled();
+    void setupDrawModelViewIdentity();
     void setupDrawModelView(float left, float top, float right, float bottom,
             bool ignoreTransform = false, bool ignoreModelView = false);
     void setupDrawModelViewTranslate(float left, float top, float right, float bottom,
             bool ignoreTransform = false);
     void setupDrawColorUniforms();
     void setupDrawPureColorUniforms();
+    void setupDrawShaderIdentityUniforms();
     void setupDrawShaderUniforms(bool ignoreTransform = false);
     void setupDrawColorFilterUniforms();
     void setupDrawSimpleMesh();
     void setupDrawTexture(GLuint texture);
-    void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0);
+    void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
     void finishDrawTexture();
 
     /**
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 43d4291..3ad453f 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -171,7 +171,8 @@
     RS_SAMPLER_LINEAR,
     RS_SAMPLER_LINEAR_MIP_LINEAR,
     RS_SAMPLER_WRAP,
-    RS_SAMPLER_CLAMP
+    RS_SAMPLER_CLAMP,
+    RS_SAMPLER_LINEAR_MIP_NEAREST,
 };
 
 enum RsTextureTarget {
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
index f61cf25..6cb50b8 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
@@ -128,9 +128,9 @@
     }
 
     private void loadImage() {
-        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot, Element.RGB_565(mRS), true);
-        mGridImage.uploadToTexture(0);
-
+        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
+                                                         Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                                         Allocation.USAGE_GRAPHICS_TEXTURE);
         mScript.set_gTGrid(mGridImage);
     }
 
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
index 22b3fff..747463a 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
@@ -124,9 +124,9 @@
     }
 
     private void loadImage() {
-        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot, Element.RGB_565(mRS), true);
-        mGridImage.uploadToTexture(0);
-
+        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
+                                                         Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                                         Allocation.USAGE_GRAPHICS_TEXTURE);
         mScript.set_gTGrid(mGridImage);
     }
 
diff --git a/libs/rs/java/Samples/AndroidManifest.xml b/libs/rs/java/Samples/AndroidManifest.xml
index 6f35e2a..9646a77 100644
--- a/libs/rs/java/Samples/AndroidManifest.xml
+++ b/libs/rs/java/Samples/AndroidManifest.xml
@@ -4,7 +4,7 @@
     <application android:label="Samples"
     android:icon="@drawable/test_pattern">
         <activity android:name="RsList"
-                  android:label="RsList"                  
+                  android:label="RsList"
                   android:theme="@android:style/Theme.Black.NoTitleBar">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -13,7 +13,7 @@
         </activity>
         
         <activity android:name="RsRenderStates"
-                  android:label="RsStates"                  
+                  android:label="RsStates"
                   android:theme="@android:style/Theme.Black.NoTitleBar">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -22,7 +22,7 @@
         </activity>
 
         <activity android:name="RsBench"
-                  android:label="RsBenchmark"                  
+                  android:label="RsBenchmark"
                   android:theme="@android:style/Theme.Black.NoTitleBar">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/libs/rs/java/Samples/res/raw/shader2movev.glsl b/libs/rs/java/Samples/res/raw/shader2movev.glsl
index 68712e6..a2c807e 100644
--- a/libs/rs/java/Samples/res/raw/shader2movev.glsl
+++ b/libs/rs/java/Samples/res/raw/shader2movev.glsl
@@ -14,7 +14,6 @@
 
    mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
    vec3 worldNorm = model3 * (ATTRIB_normal + oldPos - objPos.xyz);
-   //vec3 worldNorm = model3 * ATTRIB_normal;
 
    varWorldPos = worldPos.xyz;
    varWorldNormal = worldNorm;
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsBench.java b/libs/rs/java/Samples/src/com/android/samples/RsBench.java
index 5b9af6f..a29dddc 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsBench.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsBench.java
@@ -54,7 +54,7 @@
     @Override
     protected void onResume() {
         // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
+        // to take appropriate action when the activity loses focus
         super.onResume();
         mView.resume();
     }
@@ -62,7 +62,7 @@
     @Override
     protected void onPause() {
         // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
+        // to take appropriate action when the activity loses focus
         super.onPause();
         mView.pause();
     }
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java b/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java
index 212e7a8..ddb05b3 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java
@@ -23,6 +23,7 @@
 import android.graphics.BitmapFactory;
 import android.renderscript.*;
 import android.renderscript.Allocation.CubemapLayout;
+import android.renderscript.Allocation.MipmapControl;
 import android.renderscript.Program.TextureType;
 import android.renderscript.ProgramStore.DepthFunc;
 import android.renderscript.Sampler.Value;
@@ -215,7 +216,7 @@
         ProgramVertex.ShaderBuilder pvbCustom = new ProgramVertex.ShaderBuilder(mRS);
         // Specify the resource that contains the shader string
         pvbCustom.setShader(mRes, R.raw.shaderv);
-        // Use a script field to spcify the input layout
+        // Use a script field to specify the input layout
         pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
         // Define the constant input layout
         pvbCustom.addConstant(mVSConst.getAllocation().getType());
@@ -226,7 +227,7 @@
         ProgramFragment.ShaderBuilder pfbCustom = new ProgramFragment.ShaderBuilder(mRS);
         // Specify the resource that contains the shader string
         pfbCustom.setShader(mRes, R.raw.shaderf);
-        //Tell the builder how many textures we have
+        // Tell the builder how many textures we have
         pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
         // Define the constant input layout
         pfbCustom.addConstant(mFSConst.getAllocation().getType());
@@ -284,17 +285,16 @@
     }
 
     private Allocation loadTextureRGB(int id) {
-        final Allocation allocation = Allocation.createFromBitmapResource(mRS, mRes,
-                id, Element.RGB_565(mRS), true);
-        allocation.uploadToTexture(0);
-        return allocation;
+        return Allocation.createFromBitmapResource(mRS, mRes, id,
+                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                Allocation.USAGE_GRAPHICS_TEXTURE);
     }
 
     private Allocation loadTextureARGB(int id) {
         Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB);
-        final Allocation allocation = Allocation.createFromBitmap(mRS, b, Element.RGBA_8888(mRS), true);
-        allocation.uploadToTexture(0);
-        return allocation;
+        return Allocation.createFromBitmap(mRS, b,
+                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                Allocation.USAGE_GRAPHICS_TEXTURE);
     }
 
     private void loadImages() {
@@ -303,9 +303,8 @@
         mTexTransparent = loadTextureARGB(R.drawable.leaf);
         mTexChecker = loadTextureRGB(R.drawable.checker);
         Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.cubemap_test);
-        mTexCube = Allocation.createCubemapFromBitmap(mRS, b, Element.RGB_565(mRS), false,
+        mTexCube = Allocation.createCubemapFromBitmap(mRS, b,
                                                       Allocation.CubemapLayout.VERTICAL_FACE_LIST);
-        mTexCube.uploadToTexture(0);
 
         mScript.set_gTexTorus(mTexTorus);
         mScript.set_gTexOpaque(mTexOpaque);
@@ -316,16 +315,22 @@
 
     private void initFonts() {
         // Sans font by family name
-        mFontSans = Font.createFromFamily(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
+        mFontSans = Font.createFromFamily(mRS, mRes, "sans-serif",
+                                          Font.Style.NORMAL, 8);
         // Create font by file name
         mFontSerif = Font.create(mRS, mRes, "DroidSerif-Regular.ttf", 8);
         // Create fonts by family and style
-        mFontSerifBold = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD, 8);
-        mFontSerifItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.ITALIC, 8);
-        mFontSerifBoldItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
-        mFontMono = Font.createFromFamily(mRS, mRes, "mono", Font.Style.NORMAL, 8);
+        mFontSerifBold = Font.createFromFamily(mRS, mRes, "serif",
+                                               Font.Style.BOLD, 8);
+        mFontSerifItalic = Font.createFromFamily(mRS, mRes, "serif",
+                                                 Font.Style.ITALIC, 8);
+        mFontSerifBoldItalic = Font.createFromFamily(mRS, mRes, "serif",
+                                                     Font.Style.BOLD_ITALIC, 8);
+        mFontMono = Font.createFromFamily(mRS, mRes, "mono",
+                                          Font.Style.NORMAL, 8);
 
-        mTextAlloc = Allocation.createFromString(mRS, "String from allocation", Allocation.USAGE_SCRIPT);
+        mTextAlloc = Allocation.createFromString(mRS, "String from allocation",
+                                                 Allocation.USAGE_SCRIPT);
 
         mScript.set_gFontSans(mFontSans);
         mScript.set_gFontSerif(mFontSerif);
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsBenchView.java b/libs/rs/java/Samples/src/com/android/samples/RsBenchView.java
index 4283a42..0a56668 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsBenchView.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsBenchView.java
@@ -43,7 +43,6 @@
 
     public RsBenchView(Context context) {
         super(context);
-        //setFocusable(true);
     }
 
     private RenderScriptGL mRS;
@@ -71,17 +70,13 @@
     }
 
     @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event)
-    {
-        // break point at here
-        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
         return super.onKeyDown(keyCode, event);
     }
 
 
     @Override
-    public boolean onTouchEvent(MotionEvent ev)
-    {
+    public boolean onTouchEvent(MotionEvent ev) {
         boolean ret = false;
         int act = ev.getAction();
         if (act == ev.ACTION_DOWN) {
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
index 6258c9b..75e8d99 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -274,17 +274,16 @@
     }
 
     private Allocation loadTextureRGB(int id) {
-        final Allocation allocation = Allocation.createFromBitmapResource(mRS, mRes,
-                id, Element.RGB_565(mRS), true);
-        allocation.uploadToTexture(0);
-        return allocation;
+        return Allocation.createFromBitmapResource(mRS, mRes, id,
+                                                   Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                                   Allocation.USAGE_GRAPHICS_TEXTURE);
     }
 
     private Allocation loadTextureARGB(int id) {
         Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB);
-        final Allocation allocation = Allocation.createFromBitmap(mRS, b, Element.RGBA_8888(mRS), true);
-        allocation.uploadToTexture(0);
-        return allocation;
+        return Allocation.createFromBitmap(mRS, b,
+                                           Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                           Allocation.USAGE_GRAPHICS_TEXTURE);
     }
 
     private void loadImages() {
@@ -293,9 +292,8 @@
         mTexTransparent = loadTextureARGB(R.drawable.leaf);
         mTexChecker = loadTextureRGB(R.drawable.checker);
         Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.cubemap_test);
-        mTexCube = Allocation.createCubemapFromBitmap(mRS, b, Element.RGB_565(mRS), false,
+        mTexCube = Allocation.createCubemapFromBitmap(mRS, b,
                                                       Allocation.CubemapLayout.VERTICAL_FACE_LIST);
-        mTexCube.uploadToTexture(0);
 
         mScript.set_gTexTorus(mTexTorus);
         mScript.set_gTexOpaque(mTexOpaque);
diff --git a/libs/rs/java/Samples/src/com/android/samples/rsbench.rs b/libs/rs/java/Samples/src/com/android/samples/rsbench.rs
index 87f2f29..905f34b 100644
--- a/libs/rs/java/Samples/src/com/android/samples/rsbench.rs
+++ b/libs/rs/java/Samples/src/com/android/samples/rsbench.rs
@@ -19,7 +19,7 @@
 #include "rs_graphics.rsh"
 #include "shader_def.rsh"
 
-const int gMaxModes = 23;
+const int gMaxModes = 26;
 
 rs_program_vertex gProgVertex;
 rs_program_fragment gProgFragmentColor;
@@ -94,7 +94,14 @@
                              0.5f, 0.6f, 0.7f, 1.0f,
 };
 
-void displayFontSamples(int fillNum) {
+static void displayFontSamples(int fillNum) {
+
+    rs_font fonts[5];
+    rsSetObject(&fonts[0], gFontSans);
+    rsSetObject(&fonts[1], gFontSerif);
+    rsSetObject(&fonts[2], gFontSerifBold);
+    rsSetObject(&fonts[3], gFontSerifBoldItalic);
+    rsSetObject(&fonts[4], gFontSans);
 
     uint width = rsgGetWidth();
     uint height = rsgGetHeight();
@@ -107,9 +114,8 @@
     int yPos = top;
 
     int xOffset = 0, yOffset = 0;
-    rsgBindFont(gFontSans); //rsgBindFont(gFontSerif); rsgBindFont(gFontSerifBold); rsgBindFont(gFontSerifBoldItalic); rsgBindFont(gFontSans);
-
     for(int fillI = 0; fillI < fillNum; fillI ++) {
+        rsgBindFont(fonts[fillI]);
         xOffset = textOffsets[fillI * 2];
         yOffset = textOffsets[fillI * 2 + 1];
         float *colPtr = textColors + fillI * 4;
@@ -122,18 +128,22 @@
             }
         }
     }
+
+    for (int i = 0; i < 5; i ++) {
+        rsClearObject(&fonts[i]);
+    }
 }
 
-void bindProgramVertexOrtho() {
+static void bindProgramVertexOrtho() {
     // Default vertex sahder
     rsgBindProgramVertex(gProgVertex);
-    // Setup the projectioni matrix
+    // Setup the projection matrix
     rs_matrix4x4 proj;
     rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -500, 500);
     rsgProgramVertexLoadProjectionMatrix(&proj);
 }
 
-void displaySingletexFill(bool blend, int quadCount) {
+static void displaySingletexFill(bool blend, int quadCount) {
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
     rsMatrixLoadIdentity(&matrix);
@@ -159,7 +169,7 @@
     }
 }
 
-void displayBlendingSamples() {
+static void displayBlendingSamples() {
     int i;
 
     bindProgramVertexOrtho();
@@ -205,7 +215,7 @@
 
 }
 
-void displayMeshSamples(int meshNum) {
+static void displayMeshSamples(int meshNum) {
 
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
@@ -227,7 +237,7 @@
     }
 }
 
-void displayTextureSamplers() {
+static void displayTextureSamplers() {
 
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
@@ -282,27 +292,34 @@
     rsgDrawText("Filtering: miplinear wrap", 310, 590);
 }
 
-float gTorusRotation = 0;
-static void drawToruses(int numMeshes) {
-    rs_matrix4x4 matrix;
+static float gTorusRotation = 0;
+static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) {
+    if (buffer == 0) {
+        rsgProgramVertexLoadModelMatrix(matrix);
+    } else {
+        rsAllocationMarkDirty(rsGetAllocation(buffer));
+    }
+}
+
+static void drawToruses(int numMeshes, rs_matrix4x4 *matrix, void *buffer) {
 
     if (numMeshes == 1) {
-        rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, -7.5f);
-        rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsgProgramVertexLoadModelMatrix(&matrix);
+        rsMatrixLoadTranslate(matrix, 0.0f, 0.0f, -7.5f);
+        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+        updateModelMatrix(matrix, buffer);
         rsgDrawMesh(gTorusMesh);
         return;
     }
 
     if (numMeshes == 2) {
-        rsMatrixLoadTranslate(&matrix, -1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsgProgramVertexLoadModelMatrix(&matrix);
+        rsMatrixLoadTranslate(matrix, -1.6f, 0.0f, -7.5f);
+        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+        updateModelMatrix(matrix, buffer);
         rsgDrawMesh(gTorusMesh);
 
-        rsMatrixLoadTranslate(&matrix, 1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsgProgramVertexLoadModelMatrix(&matrix);
+        rsMatrixLoadTranslate(matrix, 1.6f, 0.0f, -7.5f);
+        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+        updateModelMatrix(matrix, buffer);
         rsgDrawMesh(gTorusMesh);
         return;
     }
@@ -315,9 +332,9 @@
     for (int h = 0; h < 4; h ++) {
         for (int v = 0; v < 2; v ++) {
             // Position our model on the screen
-            rsMatrixLoadTranslate(&matrix, startX + dist * h, startY + dist * v, startZ);
-            rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-            rsgProgramVertexLoadModelMatrix(&matrix);
+            rsMatrixLoadTranslate(matrix, startX + dist * h, startY + dist * v, startZ);
+            rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+            updateModelMatrix(matrix, buffer);
             rsgDrawMesh(gTorusMesh);
         }
     }
@@ -325,10 +342,10 @@
 
 
 // Quick hack to get some geometry numbers
-void displaySimpleGeoSamples(bool useTexture, int numMeshes) {
+static void displaySimpleGeoSamples(bool useTexture, int numMeshes) {
     rsgBindProgramVertex(gProgVertex);
     rsgBindProgramRaster(gCullBack);
-    // Setup the projectioni matrix with 60 degree field of view
+    // Setup the projection matrix with 30 degree field of view
     rs_matrix4x4 proj;
     float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
     rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
@@ -345,19 +362,20 @@
     rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
     rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
 
-    // Aplly a rotation to our mesh
+    // Apply a rotation to our mesh
     gTorusRotation += 50.0f * gDt;
     if (gTorusRotation > 360.0f) {
         gTorusRotation -= 360.0f;
     }
 
-    drawToruses(numMeshes);
+    rs_matrix4x4 matrix;
+    drawToruses(numMeshes, &matrix, 0);
 }
 
 float gLight0Rotation = 0;
 float gLight1Rotation = 0;
 
-void setupCustomShaderLights() {
+static void setupCustomShaderLights() {
     float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f};
     float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f};
     float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f};
@@ -393,7 +411,7 @@
     gVSConstants->light1_CosinePower = 25.0f;
     rsAllocationMarkDirty(rsGetAllocation(gVSConstants));
 
-    // Update fragmetn shader constants
+    // Update fragment shader constants
     // Set light 0 colors
     gFSConstants->light0_DiffuseColor = light0DiffCol;
     gFSConstants->light0_SpecularColor = light0SpecCol;
@@ -419,17 +437,17 @@
     rsAllocationMarkDirty(rsGetAllocation(gFSConstPixel));
 }
 
-void displayCustomShaderSamples(int numMeshes) {
+static void displayCustomShaderSamples(int numMeshes) {
 
     // Update vertex shader constants
     // Load model matrix
-    // Aplly a rotation to our mesh
+    // Apply a rotation to our mesh
     gTorusRotation += 50.0f * gDt;
     if (gTorusRotation > 360.0f) {
         gTorusRotation -= 360.0f;
     }
 
-    // Setup the projectioni matrix
+    // Setup the projection matrix
     float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
     rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
     setupCustomShaderLights();
@@ -445,70 +463,31 @@
     // Use back face culling
     rsgBindProgramRaster(gCullBack);
 
-    rs_matrix4x4 matrix;
-
-    if (numMeshes == 1) {
-        rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -7.5f);
-        rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-        rsAllocationMarkDirty(rsGetAllocation(gVSConstants));
-
-        rsgDrawMesh(gTorusMesh);
-        return;
-    }
-
-    if (numMeshes == 2) {
-        rsMatrixLoadTranslate(&gVSConstants->model, -1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-        rsAllocationMarkDirty(rsGetAllocation(gVSConstants));
-        rsgDrawMesh(gTorusMesh);
-
-        rsMatrixLoadTranslate(&gVSConstants->model, 1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-        rsAllocationMarkDirty(rsGetAllocation(gVSConstants));
-        rsgDrawMesh(gTorusMesh);
-        return;
-    }
-
-    float startX = -5.0f;
-    float startY = -1.5f;
-    float startZ = -15.0f;
-    float dist = 3.2f;
-
-    for (int h = 0; h < 4; h ++) {
-        for (int v = 0; v < 2; v ++) {
-            // Position our model on the screen
-            rsMatrixLoadTranslate(&gVSConstants->model, startX + dist * h, startY + dist * v, startZ);
-            rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-            rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-            rsAllocationMarkDirty(rsGetAllocation(gVSConstants));
-            rsgDrawMesh(gTorusMesh);
-        }
-    }
+    drawToruses(numMeshes, &gVSConstants->model, gVSConstants);
 }
 
-void displayPixelLightSamples(int numMeshes) {
+static void displayPixelLightSamples(int numMeshes, bool heavyVertex) {
 
     // Update vertex shader constants
     // Load model matrix
-    // Aplly a rotation to our mesh
-    gTorusRotation += 20.0f * gDt;
+    // Apply a rotation to our mesh
+    gTorusRotation += 30.0f * gDt;
     if (gTorusRotation > 360.0f) {
         gTorusRotation -= 360.0f;
     }
 
-    //gTorusRotation = 45.0f;
-
     gVSConstPixel->time = rsUptimeMillis()*0.005;
 
-    // Setup the projectioni matrix
+    // Setup the projection matrix
     float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
     rsMatrixLoadPerspective(&gVSConstPixel->proj, 30.0f, aspect, 0.1f, 100.0f);
     setupCustomShaderLights();
 
-    rsgBindProgramVertex(gProgVertexPixelLight);
+    if (heavyVertex) {
+        rsgBindProgramVertex(gProgVertexPixelLightMove);
+    } else {
+        rsgBindProgramVertex(gProgVertexPixelLight);
+    }
 
     // Fragment shader with texture
     rsgBindProgramStore(gProgStoreBlendNoneDepth);
@@ -519,51 +498,10 @@
     // Use back face culling
     rsgBindProgramRaster(gCullBack);
 
-    rs_matrix4x4 matrix;
-
-    if (numMeshes == 1) {
-        rsMatrixLoadTranslate(&gVSConstPixel->model, 0.0f, 0.0f, -7.5f);
-        rsMatrixRotate(&gVSConstPixel->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsMatrixRotate(&gVSConstPixel->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-        rsAllocationMarkDirty(rsGetAllocation(gVSConstPixel));
-
-        rsgDrawMesh(gTorusMesh);
-        return;
-    }
-
-    if (numMeshes == 2) {
-        rsMatrixLoadTranslate(&gVSConstPixel->model, -1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(&gVSConstPixel->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsMatrixRotate(&gVSConstPixel->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-        rsAllocationMarkDirty(rsGetAllocation(gVSConstPixel));
-        rsgDrawMesh(gTorusMesh);
-
-        rsMatrixLoadTranslate(&gVSConstPixel->model, 1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(&gVSConstPixel->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        rsMatrixRotate(&gVSConstPixel->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-        rsAllocationMarkDirty(rsGetAllocation(gVSConstPixel));
-        rsgDrawMesh(gTorusMesh);
-        return;
-    }
-
-    float startX = -5.0f;
-    float startY = -1.5f;
-    float startZ = -15.0f;
-    float dist = 3.2f;
-
-    for (int h = 0; h < 4; h ++) {
-        for (int v = 0; v < 2; v ++) {
-            // Position our model on the screen
-            rsMatrixLoadTranslate(&gVSConstPixel->model, startX + dist * h, startY + dist * v, startZ);
-            rsMatrixRotate(&gVSConstPixel->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-            rsMatrixRotate(&gVSConstPixel->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-            rsAllocationMarkDirty(rsGetAllocation(gVSConstPixel));
-            rsgDrawMesh(gTorusMesh);
-        }
-    }
+    drawToruses(numMeshes, &gVSConstPixel->model, gVSConstPixel);
 }
 
-void displayMultitextureSample(bool blend, int quadCount) {
+static void displayMultitextureSample(bool blend, int quadCount) {
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
     rsMatrixLoadIdentity(&matrix);
@@ -593,9 +531,9 @@
     }
 }
 
-float gAnisoTime = 0.0f;
-uint anisoMode = 0;
-void displayAnisoSample() {
+static float gAnisoTime = 0.0f;
+static uint anisoMode = 0;
+static void displayAnisoSample() {
 
     gAnisoTime += gDt;
 
@@ -672,6 +610,8 @@
         displayTextureSamplers();
         displayMultitextureSample(true, 5);
         displayAnisoSample();
+        displayPixelLightSamples(1, false);
+        displayPixelLightSamples(1, true);
         countdown --;
         rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f);
 
@@ -699,7 +639,7 @@
 static int totalFramesRendered = 0;
 static int benchMode = 0;
 
-#define testTime 10.0f
+#define testTime 5.0f
 static float curTestTime = testTime;
 
 static const char *testNames[] = {
@@ -727,12 +667,9 @@
     "Finished 25.6k geo heavy fragment",
     "Finished 51.2k geo heavy fragment",
     "Finished 204.8k geo raster load heavy fragment",
-    "Finished simpleGeo",
-    "Finished simpleGeo",
-    "Finished simpleGeo",
-    "Finished simpleGeo",
-    "Finished simpleGeo",
-    "Finished simpleGeo",
+    "Finished 25.6k geo heavy fragment, heavy vertex",
+    "Finished 51.2k geo heavy fragment, heavy vertex",
+    "Finished 204.8k geo raster load heavy fragment, heavy vertex",
 };
 
 int root(int launchID) {
@@ -746,9 +683,6 @@
         return 1;
     }
 
-    /*displayPixelLightSamples(1);
-    return 1;*/
-
     curTestTime -= gDt;
     if(curTestTime < 0.0f) {
         float fps = (float)(frameCount) / (testTime - curTestTime);
@@ -829,14 +763,24 @@
         displayMultitextureSample(true, 5);
         break;
     case 21:
-        displayPixelLightSamples(1);
+        displayPixelLightSamples(1, false);
         break;
     case 22:
-        displayPixelLightSamples(2);
+        displayPixelLightSamples(2, false);
         break;
     case 23:
-        displayPixelLightSamples(8);
+        displayPixelLightSamples(8, false);
         break;
+    case 24:
+        displayPixelLightSamples(1, true);
+        break;
+    case 25:
+        displayPixelLightSamples(2, true);
+        break;
+    case 26:
+        displayPixelLightSamples(8, true);
+        break;
+
     }
 
     frameCount ++;
diff --git a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
index 39b0834..a973167 100644
--- a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
+++ b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -81,7 +81,7 @@
 void init() {
 }
 
-void displayFontSamples() {
+static void displayFontSamples() {
     rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
     int yPos = 100;
     rsgBindFont(gFontSans);
@@ -148,7 +148,7 @@
 
 }
 
-void bindProgramVertexOrtho() {
+static void bindProgramVertexOrtho() {
     // Default vertex sahder
     rsgBindProgramVertex(gProgVertex);
     // Setup the projectioni matrix
@@ -157,7 +157,7 @@
     rsgProgramVertexLoadProjectionMatrix(&proj);
 }
 
-void displayShaderSamples() {
+static void displayShaderSamples() {
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
     rsMatrixLoadIdentity(&matrix);
@@ -206,7 +206,7 @@
     rsgDrawText("Flat color shader", 100, 450);
 }
 
-void displayBlendingSamples() {
+static void displayBlendingSamples() {
     int i;
 
     bindProgramVertexOrtho();
@@ -252,7 +252,7 @@
 
 }
 
-void displayMeshSamples() {
+static void displayMeshSamples() {
 
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
@@ -272,7 +272,7 @@
     rsgDrawText("User gen 10 by 10 grid mesh", 10, 250);
 }
 
-void displayTextureSamplers() {
+static void displayTextureSamplers() {
 
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
@@ -327,9 +327,9 @@
     rsgDrawText("Filtering: miplinear wrap", 310, 590);
 }
 
-float gTorusRotation = 0;
+static float gTorusRotation = 0;
 
-void displayCullingSamples() {
+static void displayCullingSamples() {
     rsgBindProgramVertex(gProgVertex);
     // Setup the projectioni matrix with 60 degree field of view
     rs_matrix4x4 proj;
@@ -370,10 +370,10 @@
     rsgDrawText("Displaying mesh front/back face culling", 10, rsgGetHeight() - 10);
 }
 
-float gLight0Rotation = 0;
-float gLight1Rotation = 0;
+static float gLight0Rotation = 0;
+static float gLight1Rotation = 0;
 
-void setupCustomShaderLights() {
+static void setupCustomShaderLights() {
     float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f};
     float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f};
     float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f};
@@ -436,7 +436,7 @@
     rsAllocationMarkDirty(rsGetAllocation(gFSConstants2));
 }
 
-void displayCustomShaderSamples() {
+static void displayCustomShaderSamples() {
 
     // Update vertex shader constants
     // Load model matrix
@@ -472,7 +472,7 @@
     rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
 }
 
-void displayCustomShaderSamples2() {
+static void displayCustomShaderSamples2() {
 
     // Update vertex shader constants
     // Load model matrix
@@ -509,7 +509,7 @@
     rsgDrawText("Custom shader sample with array uniforms", 10, rsgGetHeight() - 10);
 }
 
-void displayCubemapShaderSample() {
+static void displayCubemapShaderSample() {
     // Update vertex shader constants
     // Load model matrix
     // Aplly a rotation to our mesh
@@ -545,7 +545,7 @@
     rsgDrawText("Cubemap shader sample", 10, rsgGetHeight() - 10);
 }
 
-void displayMultitextureSample() {
+static void displayMultitextureSample() {
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
     rsMatrixLoadIdentity(&matrix);
@@ -573,9 +573,9 @@
     rsgDrawText("Custom shader with multitexturing", 10, 280);
 }
 
-float gAnisoTime = 0.0f;
-uint anisoMode = 0;
-void displayAnisoSample() {
+static float gAnisoTime = 0.0f;
+static uint anisoMode = 0;
+static void displayAnisoSample() {
 
     gAnisoTime += gDt;
 
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 78b570a..77e8032 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -31,10 +31,13 @@
 using namespace android;
 using namespace android::renderscript;
 
-Allocation::Allocation(Context *rsc, const Type *type, uint32_t usages) : ObjectBase(rsc) {
+Allocation::Allocation(Context *rsc, const Type *type, uint32_t usages,
+                       RsAllocationMipmapControl mc)
+    : ObjectBase(rsc) {
     init(rsc, type);
 
     mUsageFlags = usages;
+    mMipmapControl = mc;
 
     allocScriptMemory();
     if (mType->getElement()->getHasReferences()) {
@@ -191,13 +194,6 @@
         uploadCubeTexture(isFirstUpload);
     }
 
-    if (mMipmapControl == RS_ALLOCATION_MIPMAP_ON_SYNC_TO_TEXTURE) {
-#ifndef ANDROID_RS_BUILD_FOR_HOST
-        glGenerateMipmap(target);
-#endif //ANDROID_RS_BUILD_FOR_HOST
-    }
-
-
     if (!(mUsageFlags & RS_ALLOCATION_USAGE_SCRIPT)) {
         freeScriptMemory();
     }
@@ -227,6 +223,12 @@
                             format, type, p);
         }
     }
+
+    if (mMipmapControl == RS_ALLOCATION_MIPMAP_ON_SYNC_TO_TEXTURE) {
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+        glGenerateMipmap(target);
+#endif //ANDROID_RS_BUILD_FOR_HOST
+    }
 }
 
 void Allocation::uploadCubeTexture(bool isFirstUpload) {
@@ -266,6 +268,12 @@
             }
         }
     }
+
+    if (mMipmapControl == RS_ALLOCATION_MIPMAP_ON_SYNC_TO_TEXTURE) {
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+        glGenerateMipmap(target);
+#endif //ANDROID_RS_BUILD_FOR_HOST
+    }
 }
 
 void Allocation::deferedUploadToBufferObject(const Context *rsc) {
@@ -790,7 +798,7 @@
                                       RsAllocationMipmapControl mips,
                                       uint32_t usages) {
     Context *rsc = static_cast<Context *>(con);
-    Allocation * alloc = new Allocation(rsc, static_cast<Type *>(vtype), usages);
+    Allocation * alloc = new Allocation(rsc, static_cast<Type *>(vtype), usages, mips);
     alloc->incUserRef();
     return alloc;
 }
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 4a5f3da..44dce0d 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -29,7 +29,8 @@
     // The graphics equilivent of malloc.  The allocation contains a structure of elements.
 
 public:
-    Allocation(Context *rsc, const Type *, uint32_t usages);
+    Allocation(Context *rsc, const Type *, uint32_t usages,
+               RsAllocationMipmapControl mc = RS_ALLOCATION_MIPMAP_NONE);
 
     virtual ~Allocation();
 
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 3f9a9d6..2fa1f0a 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -463,7 +463,7 @@
 
     // This will dirty the texture and the shader so next time
     // we draw it will upload the data
-    mTextTexture->deferedUploadToTexture(mRSC);
+    mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT);
     mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get());
 
     // Some debug code
@@ -529,7 +529,7 @@
 
     Allocation *cacheAlloc = new Allocation(mRSC, texType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE);
     mTextTexture.set(cacheAlloc);
-    mTextTexture->deferedUploadToTexture(mRSC);
+    mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT);
 
     // Split up our cache texture into lines of certain widths
     int32_t nextLine = 0;
diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp
index 54282a8..e2757df 100644
--- a/libs/rs/rsSampler.cpp
+++ b/libs/rs/rsSampler.cpp
@@ -61,6 +61,7 @@
         GL_LINEAR_MIPMAP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR,
         GL_REPEAT, //RS_SAMPLER_WRAP,
         GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
+        GL_LINEAR_MIPMAP_NEAREST, //RS_SAMPLER_LINEAR_MIP_NEAREST
     };
 
     GLenum transNP[] = {
@@ -69,6 +70,7 @@
         GL_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR,
         GL_CLAMP_TO_EDGE, //RS_SAMPLER_WRAP,
         GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
+        GL_LINEAR, //RS_SAMPLER_LINEAR_MIP_NEAREST,
     };
 
     // This tells us the correct texture type
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index 0f84e4b..fb5980a 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -275,6 +275,18 @@
     pf->setConstantColor(rsc, r, g, b, a);
 }
 
+static void SC_allocationSyncAll(RsAllocation va) {
+    CHECK_OBJ(va);
+    GET_TLS();
+    static_cast<Allocation *>(va)->syncAll(rsc, RS_ALLOCATION_USAGE_SCRIPT);
+}
+
+static void SC_allocationSyncAll2(RsAllocation va, RsAllocationUsageType source) {
+    CHECK_OBJ(va);
+    GET_TLS();
+    static_cast<Allocation *>(va)->syncAll(rsc, source);
+}
+
 static void SC_uploadToTexture2(RsAllocation va, uint32_t baseMipLevel) {
     CHECK_OBJ(va);
     GET_TLS();
@@ -425,6 +437,8 @@
     { "_Z11rsgGetWidthv", (void *)&SC_getWidth, false },
     { "_Z12rsgGetHeightv", (void *)&SC_getHeight, false },
 
+    { "_Z20rsgAllocationSyncAll13rs_allocation", (void *)&SC_allocationSyncAll, false },
+
     { "_Z18rsgUploadToTexture13rs_allocationj", (void *)&SC_uploadToTexture2, false },
     { "_Z18rsgUploadToTexture13rs_allocation", (void *)&SC_uploadToTexture, false },
     { "_Z23rsgUploadToBufferObject13rs_allocation", (void *)&SC_uploadToBufferObject, false },
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
index 54e6328..3e708aa 100644
--- a/libs/rs/scriptc/rs_graphics.rsh
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -35,6 +35,9 @@
     rsgGetHeight(void);
 
 extern void __attribute__((overloadable))
+    rsgAllocationSyncAll(rs_allocation);
+
+extern void __attribute__((overloadable))
     rsgUploadToTexture(rs_allocation);
 extern void __attribute__((overloadable))
     rsgUploadToTexture(rs_allocation, uint mipLevel);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 6d47c44..1d9ef65 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -2012,7 +2012,8 @@
                 int microphone = intent.getIntExtra("microphone", 0);
 
                 if (microphone != 0) {
-                    boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+                    boolean isConnected =
+                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
                     if (state == 0 && isConnected) {
                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
                                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
@@ -2022,10 +2023,12 @@
                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
                                 AudioSystem.DEVICE_STATE_AVAILABLE,
                                 "");
-                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
+                        mConnectedDevices.put(
+                                new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
                     }
                 } else {
-                    boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+                    boolean isConnected =
+                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
                     if (state == 0 && isConnected) {
                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
                                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
@@ -2035,13 +2038,15 @@
                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
                                 AudioSystem.DEVICE_STATE_AVAILABLE,
                                 "");
-                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
+                        mConnectedDevices.put(
+                                new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
                     }
                 }
             } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
                 int state = intent.getIntExtra("state", 0);
                 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
-                boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
+                boolean isConnected =
+                    mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
                 if (state == 0 && isConnected) {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
@@ -2049,13 +2054,28 @@
                 } else if (state == 1 && !isConnected)  {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
                                                          AudioSystem.DEVICE_STATE_AVAILABLE, "");
-                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
+                    mConnectedDevices.put(
+                            new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
                 }
-            } else if ( (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) ||
-                        (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) ) {
+            } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
+                int state = intent.getIntExtra("state", 0);
+                Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
+                boolean isConnected =
+                    mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
+                if (state == 0 && isConnected) {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
+                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
+                } else if (state == 1 && !isConnected)  {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
+                                                         AudioSystem.DEVICE_STATE_AVAILABLE, "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
+                }
+            } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
                 int state = intent.getIntExtra("state", 0);
                 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
-                boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+                boolean isConnected =
+                    mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
                 if (state == 0 && isConnected) {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
@@ -2063,7 +2083,8 @@
                 } else if (state == 1 && !isConnected)  {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
                                                          AudioSystem.DEVICE_STATE_AVAILABLE, "");
-                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
+                    mConnectedDevices.put(
+                            new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
                 }
             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                 boolean broadcast = false;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 5904bfe..9662817 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -215,7 +215,7 @@
     // its pixels, it is important that the pixels (along with SkBitmap) be
     // available after creating the Bitmap is returned to Java app.
     jobject jSrcBitmap = env->NewObject(fields.bitmapClazz,
-            fields.bitmapConstructor, (int) bitmap, true, NULL, -1);
+            fields.bitmapConstructor, (int) bitmap, NULL, true, NULL, -1);
 
     LOGV("Return a new bitmap constructed with the rotation matrix");
     return env->CallStaticObjectMethod(
@@ -321,7 +321,7 @@
         return;
     }
 
-    fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(IZ[BI)V");
+    fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(I[BZ[BI)V");
     if (fields.bitmapConstructor == NULL) {
         jniThrowException(env, "java/lang/RuntimeException", "Can't find Bitmap constructor");
         return;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index a9d537f..f7f0d95 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -35,7 +35,8 @@
 	libsurfaceflinger_client
 
 LOCAL_STATIC_LIBRARIES := \
-        libstagefright_rtsp
+        libstagefright_rtsp                     \
+        libstagefright_nuplayer                 \
 
 ifneq ($(TARGET_SIMULATOR),true)
 LOCAL_SHARED_LIBRARIES += libdl
@@ -47,9 +48,11 @@
 	$(TOP)/frameworks/base/include/media/stagefright/openmax \
 	$(TOP)/frameworks/base/media/libstagefright/include             \
 	$(TOP)/frameworks/base/media/libstagefright/rtsp                \
-        $(TOP)/external/tremolo/Tremolo
+        $(TOP)/external/tremolo/Tremolo \
 
 LOCAL_MODULE:= libmediaplayerservice
 
 include $(BUILD_SHARED_LIBRARY)
 
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 63d09d6..6f011ce 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -58,6 +58,7 @@
 #include "MidiFile.h"
 #include "TestPlayerStub.h"
 #include "StagefrightPlayer.h"
+#include "nuplayer/NuPlayerDriver.h"
 
 #include <OMX.h>
 
@@ -759,6 +760,10 @@
             LOGV(" create StagefrightPlayer");
             p = new StagefrightPlayer;
             break;
+        case NU_PLAYER:
+            LOGV(" create NuPlayer");
+            p = new NuPlayerDriver;
+            break;
         case TEST_PLAYER:
             LOGV("Create Test Player stub");
             p = new TestPlayerStub();
@@ -887,7 +892,7 @@
 status_t MediaPlayerService::Client::setDataSource(
         const sp<IStreamSource> &source) {
     // create the right type of player
-    sp<MediaPlayerBase> p = createPlayer(STAGEFRIGHT_PLAYER);
+    sp<MediaPlayerBase> p = createPlayer(NU_PLAYER);
 
     if (p == NULL) {
         return NO_INIT;
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
new file mode 100644
index 0000000..c4f3764
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                       \
+        NuPlayer.cpp                    \
+        NuPlayerDecoder.cpp             \
+        NuPlayerDriver.cpp              \
+        NuPlayerRenderer.cpp            \
+        NuPlayerStreamListener.cpp      \
+        DecoderWrapper.cpp              \
+
+LOCAL_C_INCLUDES := \
+        $(TOP)/frameworks/base/include/media/stagefright/openmax        \
+	$(TOP)/frameworks/base/media/libstagefright/include             \
+        $(TOP)/frameworks/base/media/libstagefright/mpeg2ts             \
+
+LOCAL_MODULE:= libstagefright_nuplayer
+
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
new file mode 100644
index 0000000..89a5e69
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DecoderWrapper"
+#include <utils/Log.h>
+
+#include "DecoderWrapper.h"
+
+#include "AACDecoder.h"
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct DecoderWrapper::WrapperSource : public MediaSource {
+    WrapperSource(
+            const sp<MetaData> &meta,
+            const sp<AMessage> &notify);
+
+    virtual status_t start(MetaData *params);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+    void queueBuffer(const sp<ABuffer> &buffer);
+    void queueEOS(status_t finalResult);
+    void clear();
+
+protected:
+    virtual ~WrapperSource();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+
+    sp<MetaData> mMeta;
+    sp<AMessage> mNotify;
+
+    List<sp<ABuffer> > mQueue;
+    status_t mFinalResult;
+
+    DISALLOW_EVIL_CONSTRUCTORS(WrapperSource);
+};
+
+DecoderWrapper::WrapperSource::WrapperSource(
+        const sp<MetaData> &meta, const sp<AMessage> &notify)
+    : mMeta(meta),
+      mNotify(notify),
+      mFinalResult(OK) {
+}
+
+DecoderWrapper::WrapperSource::~WrapperSource() {
+}
+
+status_t DecoderWrapper::WrapperSource::start(MetaData *params) {
+    return OK;
+}
+
+status_t DecoderWrapper::WrapperSource::stop() {
+    return OK;
+}
+
+sp<MetaData> DecoderWrapper::WrapperSource::getFormat() {
+    return mMeta;
+}
+
+status_t DecoderWrapper::WrapperSource::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    Mutex::Autolock autoLock(mLock);
+
+    bool requestedBuffer = false;
+
+    while (mQueue.empty() && mFinalResult == OK) {
+        if (!requestedBuffer) {
+            mNotify->dup()->post();
+            requestedBuffer = true;
+        }
+
+        mCondition.wait(mLock);
+    }
+
+    if (mQueue.empty()) {
+        return mFinalResult;
+    }
+
+    sp<ABuffer> src = *mQueue.begin();
+    mQueue.erase(mQueue.begin());
+
+    MediaBuffer *dst = new MediaBuffer(src->size());
+    memcpy(dst->data(), src->data(), src->size());
+
+    int64_t timeUs;
+    CHECK(src->meta()->findInt64("timeUs", &timeUs));
+
+    dst->meta_data()->setInt64(kKeyTime, timeUs);
+
+    *out = dst;
+
+    return OK;
+}
+
+void DecoderWrapper::WrapperSource::queueBuffer(const sp<ABuffer> &buffer) {
+    Mutex::Autolock autoLock(mLock);
+    mQueue.push_back(buffer);
+    mCondition.broadcast();
+}
+
+void DecoderWrapper::WrapperSource::queueEOS(status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    Mutex::Autolock autoLock(mLock);
+    mFinalResult = finalResult;
+    mCondition.broadcast();
+}
+
+void DecoderWrapper::WrapperSource::clear() {
+    Mutex::Autolock autoLock(mLock);
+    mQueue.clear();
+    mFinalResult = OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct DecoderWrapper::WrapperReader : public AHandler {
+    WrapperReader(
+            const sp<MediaSource> &decoder,
+            const sp<AMessage> &notify);
+
+    void start();
+    void readMore(bool flush = false);
+
+protected:
+    virtual ~WrapperReader();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatRead
+    };
+
+    sp<MediaSource> mDecoder;
+    sp<AMessage> mNotify;
+    bool mEOS;
+
+    DISALLOW_EVIL_CONSTRUCTORS(WrapperReader);
+};
+
+DecoderWrapper::WrapperReader::WrapperReader(
+        const sp<MediaSource> &decoder, const sp<AMessage> &notify)
+    : mDecoder(decoder),
+      mNotify(notify),
+      mEOS(false) {
+}
+
+DecoderWrapper::WrapperReader::~WrapperReader() {
+}
+
+void DecoderWrapper::WrapperReader::start() {
+    CHECK_EQ(mDecoder->start(), (status_t)OK);
+    readMore();
+}
+
+void DecoderWrapper::WrapperReader::readMore(bool flush) {
+    if (!flush && mEOS) {
+        return;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatRead, id());
+    msg->setInt32("flush", static_cast<int32_t>(flush));
+    msg->post();
+}
+
+void DecoderWrapper::WrapperReader::onMessageReceived(
+        const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRead:
+        {
+            int32_t flush;
+            CHECK(msg->findInt32("flush", &flush));
+
+            MediaSource::ReadOptions options;
+            if (flush) {
+                // Dummy seek
+                options.setSeekTo(0);
+                mEOS = false;
+            }
+
+            CHECK(!mEOS);
+
+            MediaBuffer *src;
+            status_t err = mDecoder->read(&src, &options);
+
+            sp<AMessage> notify = mNotify->dup();
+
+            sp<AMessage> realNotify;
+            CHECK(notify->findMessage("real-notify", &realNotify));
+
+            if (err == OK) {
+                realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+
+                sp<ABuffer> dst = new ABuffer(src->range_length());
+                memcpy(dst->data(),
+                       (const uint8_t *)src->data() + src->range_offset(),
+                       src->range_length());
+
+                int64_t timeUs;
+                CHECK(src->meta_data()->findInt64(kKeyTime, &timeUs));
+                src->release();
+                src = NULL;
+
+                dst->meta()->setInt64("timeUs", timeUs);
+
+                realNotify->setObject("buffer", dst);
+            } else {
+                realNotify->setInt32("what", ACodec::kWhatEOS);
+                mEOS = true;
+            }
+
+            notify->post();
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+DecoderWrapper::DecoderWrapper()
+    : mNumOutstandingInputBuffers(0),
+      mNumOutstandingOutputBuffers(0),
+      mNumPendingDecodes(0),
+      mFlushing(false) {
+}
+
+DecoderWrapper::~DecoderWrapper() {
+}
+
+void DecoderWrapper::setNotificationMessage(const sp<AMessage> &msg) {
+    mNotify = msg;
+}
+
+void DecoderWrapper::initiateSetup(const sp<AMessage> &msg) {
+    msg->setWhat(kWhatSetup);
+    msg->setTarget(id());
+    msg->post();
+}
+
+void DecoderWrapper::initiateShutdown() {
+    (new AMessage(kWhatShutdown, id()))->post();
+}
+
+void DecoderWrapper::signalFlush() {
+    (new AMessage(kWhatFlush, id()))->post();
+}
+
+void DecoderWrapper::signalResume() {
+    (new AMessage(kWhatResume, id()))->post();
+}
+
+void DecoderWrapper::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSetup:
+            onSetup(msg);
+            break;
+
+        case kWhatInputDataRequested:
+        {
+            postFillBuffer();
+            ++mNumOutstandingInputBuffers;
+            break;
+        }
+
+        case kWhatInputBufferFilled:
+        {
+            CHECK_GT(mNumOutstandingInputBuffers, 0);
+            --mNumOutstandingInputBuffers;
+
+            if (mFlushing) {
+                mSource->queueEOS(INFO_DISCONTINUITY);
+
+                completeFlushIfPossible();
+                break;
+            }
+
+            sp<RefBase> obj;
+            if (!msg->findObject("buffer", &obj)) {
+                int32_t err = OK;
+                CHECK(msg->findInt32("err", &err));
+
+                mSource->queueEOS(err);
+                break;
+            }
+
+            sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+            mSource->queueBuffer(buffer);
+            break;
+        }
+
+        case kWhatFillBufferDone:
+        {
+            CHECK_GT(mNumPendingDecodes, 0);
+            --mNumPendingDecodes;
+
+            if (mFlushing) {
+                completeFlushIfPossible();
+                break;
+            }
+
+            sp<AMessage> notify;
+            CHECK(msg->findMessage("real-notify", &notify));
+
+            sp<AMessage> reply =
+                new AMessage(kWhatOutputBufferDrained, id());
+
+            notify->setMessage("reply", reply);
+            notify->post();
+
+            ++mNumOutstandingOutputBuffers;
+            break;
+        }
+
+        case kWhatOutputBufferDrained:
+        {
+            CHECK_GT(mNumOutstandingOutputBuffers, 0);
+            --mNumOutstandingOutputBuffers;
+
+            if (mFlushing) {
+                completeFlushIfPossible();
+                break;
+            }
+
+            ++mNumPendingDecodes;
+            mReader->readMore();
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            onFlush();
+            break;
+        }
+
+        case kWhatResume:
+        {
+            onResume();
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void DecoderWrapper::onSetup(const sp<AMessage> &msg) {
+    AString mime;
+    CHECK(msg->findString("mime", &mime));
+
+    CHECK(!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC));
+
+    int32_t numChannels, sampleRate;
+    CHECK(msg->findInt32("channel-count", &numChannels));
+    CHECK(msg->findInt32("sample-rate", &sampleRate));
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("esds", &obj));
+    sp<ABuffer> esds = static_cast<ABuffer *>(obj.get());
+
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, mime.c_str());
+    meta->setInt32(kKeySampleRate, sampleRate);
+    meta->setInt32(kKeyChannelCount, numChannels);
+    meta->setData(kKeyESDS, 0, esds->data(), esds->size());
+
+    mSource = new WrapperSource(
+            meta, new AMessage(kWhatInputDataRequested, id()));
+
+    sp<MediaSource> decoder = new AACDecoder(mSource);
+
+    mReaderLooper = new ALooper;
+    mReaderLooper->setName("DecoderWrapper looper");
+
+    mReaderLooper->start(
+            false, /* runOnCallingThread */
+            false, /* canCallJava */
+            PRIORITY_AUDIO);
+
+    sp<AMessage> notify = new AMessage(kWhatFillBufferDone, id());
+    notify->setMessage("real-notify", mNotify);
+
+    mReader = new WrapperReader(decoder, notify);
+    mReaderLooper->registerHandler(mReader);
+
+    mReader->start();
+    ++mNumPendingDecodes;
+}
+
+void DecoderWrapper::postFillBuffer() {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatFillThisBuffer);
+    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
+    notify->setMessage("reply", reply);
+    notify->post();
+}
+
+void DecoderWrapper::onFlush() {
+    CHECK(!mFlushing);
+    mFlushing = true;
+
+    completeFlushIfPossible();
+}
+
+void DecoderWrapper::completeFlushIfPossible() {
+    CHECK(mFlushing);
+
+    if (mNumOutstandingInputBuffers > 0
+            || mNumOutstandingOutputBuffers > 0
+            || mNumPendingDecodes > 0) {
+        return;
+    }
+
+    mFlushing = false;
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatFlushCompleted);
+    notify->post();
+}
+
+void DecoderWrapper::onResume() {
+    CHECK(!mFlushing);
+
+    ++mNumPendingDecodes;
+
+    mSource->clear();
+    mReader->readMore(true /* flush */);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.h b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
new file mode 100644
index 0000000..883b356
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DECODER_WRAPPER_H_
+
+#define DECODER_WRAPPER_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct MediaSource;
+
+struct DecoderWrapper : public AHandler {
+    DecoderWrapper();
+
+    void setNotificationMessage(const sp<AMessage> &msg);
+    void initiateSetup(const sp<AMessage> &msg);
+    void initiateShutdown();
+    void signalFlush();
+    void signalResume();
+
+protected:
+    virtual ~DecoderWrapper();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    struct WrapperSource;
+    struct WrapperReader;
+
+    enum {
+        kWhatSetup,
+        kWhatInputBufferFilled,
+        kWhatOutputBufferDrained,
+        kWhatShutdown,
+        kWhatFillBufferDone,
+        kWhatInputDataRequested,
+        kWhatFlush,
+        kWhatResume,
+    };
+
+    sp<AMessage> mNotify;
+
+    sp<WrapperSource> mSource;
+
+    sp<ALooper> mReaderLooper;
+    sp<WrapperReader> mReader;
+
+    int32_t mNumOutstandingInputBuffers;
+    int32_t mNumOutstandingOutputBuffers;
+    int32_t mNumPendingDecodes;
+    bool mFlushing;
+
+    void onSetup(const sp<AMessage> &msg);
+    void onFlush();
+    void onResume();
+
+    void postFillBuffer();
+    void completeFlushIfPossible();
+
+    DISALLOW_EVIL_CONSTRUCTORS(DecoderWrapper);
+};
+
+}  // namespace android
+
+#endif  // DECODER_WRAPPER_H_
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
new file mode 100644
index 0000000..403029a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayer"
+#include <utils/Log.h>
+
+#include "NuPlayer.h"
+#include "NuPlayerDecoder.h"
+#include "NuPlayerRenderer.h"
+#include "NuPlayerStreamListener.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+NuPlayer::NuPlayer()
+    : mEOS(false),
+      mAudioEOS(false),
+      mVideoEOS(false),
+      mFlushingAudio(NONE),
+      mFlushingVideo(NONE) {
+}
+
+NuPlayer::~NuPlayer() {
+}
+
+void NuPlayer::setListener(const wp<MediaPlayerBase> &listener) {
+    mListener = listener;
+}
+
+void NuPlayer::setDataSource(const sp<IStreamSource> &source) {
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+
+    source->incStrong(this);
+    msg->setPointer("source", source.get());  // XXX unsafe.
+
+    msg->post();
+}
+
+void NuPlayer::setVideoSurface(const sp<Surface> &surface) {
+    sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, id());
+    msg->setObject("surface", surface);
+    msg->post();
+}
+
+void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
+    sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id());
+    msg->setObject("sink", sink);
+    msg->post();
+}
+
+void NuPlayer::start() {
+    (new AMessage(kWhatStart, id()))->post();
+}
+
+void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSetDataSource:
+        {
+            LOGI("kWhatSetDataSource");
+
+            CHECK(mSource == NULL);
+
+            void *ptr;
+            CHECK(msg->findPointer("source", &ptr));
+
+            mSource = static_cast<IStreamSource *>(ptr);
+            mSource->decStrong(this);
+
+            mStreamListener = new NuPlayerStreamListener(mSource, id());
+            mTSParser = new ATSParser;
+            break;
+        }
+
+        case kWhatSetVideoSurface:
+        {
+            LOGI("kWhatSetVideoSurface");
+
+            sp<RefBase> obj;
+            CHECK(msg->findObject("surface", &obj));
+
+            mSurface = static_cast<Surface *>(obj.get());
+            break;
+        }
+
+        case kWhatSetAudioSink:
+        {
+            LOGI("kWhatSetAudioSink");
+
+            sp<RefBase> obj;
+            CHECK(msg->findObject("sink", &obj));
+
+            mAudioSink = static_cast<MediaPlayerBase::AudioSink *>(obj.get());
+            break;
+        }
+
+        case kWhatStart:
+        {
+            mStreamListener->start();
+
+            mRenderer = new Renderer(
+                    mAudioSink,
+                    new AMessage(kWhatRendererNotify, id()));
+
+            looper()->registerHandler(mRenderer);
+
+            (new AMessage(kWhatScanSources, id()))->post();
+            break;
+        }
+
+        case kWhatScanSources:
+        {
+            instantiateDecoder(false, &mVideoDecoder);
+
+            if (mAudioSink != NULL) {
+                instantiateDecoder(true, &mAudioDecoder);
+            }
+
+            if (mEOS) {
+                break;
+            }
+
+            feedMoreTSData();
+
+            if (mAudioDecoder == NULL || mVideoDecoder == NULL) {
+                msg->post(100000ll);
+            }
+            break;
+        }
+
+        case kWhatVideoNotify:
+        case kWhatAudioNotify:
+        {
+            bool audio = msg->what() == kWhatAudioNotify;
+
+            sp<AMessage> codecRequest;
+            CHECK(msg->findMessage("codec-request", &codecRequest));
+
+            int32_t what;
+            CHECK(codecRequest->findInt32("what", &what));
+
+            if (what == ACodec::kWhatFillThisBuffer) {
+                status_t err = feedDecoderInputData(
+                        audio, codecRequest);
+
+                if (err == -EWOULDBLOCK && !mEOS) {
+                    feedMoreTSData();
+                    msg->post();
+                }
+            } else if (what == ACodec::kWhatEOS) {
+                mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
+            } else if (what == ACodec::kWhatFlushCompleted) {
+                if (audio) {
+                    CHECK_EQ((int)mFlushingAudio, (int)FLUSHING_DECODER);
+                    mFlushingAudio = FLUSHED;
+                } else {
+                    CHECK_EQ((int)mFlushingVideo, (int)FLUSHING_DECODER);
+                    mFlushingVideo = FLUSHED;
+                }
+
+                LOGI("decoder %s flush completed", audio ? "audio" : "video");
+
+                if (mFlushingAudio == FLUSHED && mFlushingVideo == FLUSHED) {
+                    LOGI("both audio and video are flushed now.");
+
+                    mRenderer->signalTimeDiscontinuity();
+
+                    if (mAudioDecoder != NULL) {
+                        mAudioDecoder->signalResume();
+                    }
+
+                    if (mVideoDecoder != NULL) {
+                        mVideoDecoder->signalResume();
+                    }
+
+                    mFlushingAudio = NONE;
+                    mFlushingVideo = NONE;
+                }
+            } else {
+                CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
+
+                renderBuffer(audio, codecRequest);
+            }
+
+            break;
+        }
+
+        case kWhatRendererNotify:
+        {
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            if (what == Renderer::kWhatEOS) {
+                int32_t audio;
+                CHECK(msg->findInt32("audio", &audio));
+
+                if (audio) {
+                    mAudioEOS = true;
+                } else {
+                    mVideoEOS = true;
+                }
+
+                LOGI("reached %s EOS", audio ? "audio" : "video");
+
+                if ((mAudioEOS || mAudioDecoder == NULL)
+                        && (mVideoEOS || mVideoDecoder == NULL)) {
+                    notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
+                }
+            } else {
+                CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
+
+                int32_t audio;
+                CHECK(msg->findInt32("audio", &audio));
+
+                LOGI("renderer %s flush completed.", audio ? "audio" : "video");
+            }
+            break;
+        }
+
+        case kWhatMoreDataQueued:
+        {
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void NuPlayer::feedMoreTSData() {
+    CHECK(!mEOS);
+
+    for (int32_t i = 0; i < 10; ++i) {
+        char buffer[188];
+        ssize_t n = mStreamListener->read(buffer, sizeof(buffer));
+
+        if (n == 0) {
+            LOGI("input data EOS reached.");
+            mTSParser->signalEOS(ERROR_END_OF_STREAM);
+            mEOS = true;
+            break;
+        } else if (n == INFO_DISCONTINUITY) {
+            mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+        } else if (n < 0) {
+            CHECK_EQ(n, -EWOULDBLOCK);
+            break;
+        } else {
+            if (buffer[0] == 0x00) {
+                // XXX legacy
+                mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+            } else {
+                mTSParser->feedTSPacket(buffer, sizeof(buffer));
+            }
+        }
+    }
+}
+
+status_t NuPlayer::dequeueNextAccessUnit(
+        ATSParser::SourceType *type, sp<ABuffer> *accessUnit) {
+    accessUnit->clear();
+
+    status_t audioErr = -EWOULDBLOCK;
+    int64_t audioTimeUs;
+
+    sp<AnotherPacketSource> audioSource =
+        static_cast<AnotherPacketSource *>(
+                mTSParser->getSource(ATSParser::MPEG2ADTS_AUDIO).get());
+
+    if (audioSource != NULL) {
+        audioErr = audioSource->nextBufferTime(&audioTimeUs);
+    }
+
+    status_t videoErr = -EWOULDBLOCK;
+    int64_t videoTimeUs;
+
+    sp<AnotherPacketSource> videoSource =
+        static_cast<AnotherPacketSource *>(
+                mTSParser->getSource(ATSParser::AVC_VIDEO).get());
+
+    if (videoSource != NULL) {
+        videoErr = videoSource->nextBufferTime(&videoTimeUs);
+    }
+
+    if (audioErr == -EWOULDBLOCK || videoErr == -EWOULDBLOCK) {
+        return -EWOULDBLOCK;
+    }
+
+    if (audioErr != OK && videoErr != OK) {
+        return audioErr;
+    }
+
+    if (videoErr != OK || (audioErr == OK && audioTimeUs < videoTimeUs)) {
+        *type = ATSParser::MPEG2ADTS_AUDIO;
+        return audioSource->dequeueAccessUnit(accessUnit);
+    } else {
+        *type = ATSParser::AVC_VIDEO;
+        return videoSource->dequeueAccessUnit(accessUnit);
+    }
+}
+
+status_t NuPlayer::dequeueAccessUnit(
+        ATSParser::SourceType type, sp<ABuffer> *accessUnit) {
+    sp<AnotherPacketSource> source =
+        static_cast<AnotherPacketSource *>(mTSParser->getSource(type).get());
+
+    if (source == NULL) {
+        return -EWOULDBLOCK;
+    }
+
+    status_t finalResult;
+    if (!source->hasBufferAvailable(&finalResult)) {
+        return finalResult == OK ? -EWOULDBLOCK : finalResult;
+    }
+
+    return source->dequeueAccessUnit(accessUnit);
+}
+
+status_t NuPlayer::instantiateDecoder(
+        bool audio, sp<Decoder> *decoder) {
+    if (*decoder != NULL) {
+        return OK;
+    }
+
+    ATSParser::SourceType type =
+        audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO;
+
+    sp<AnotherPacketSource> source =
+        static_cast<AnotherPacketSource *>(
+                mTSParser->getSource(type).get());
+
+    if (source == NULL) {
+        return -EWOULDBLOCK;
+    }
+
+    sp<AMessage> notify =
+        new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
+                     id());
+
+    *decoder = new Decoder(notify, audio ? NULL : mSurface);
+    looper()->registerHandler(*decoder);
+
+    const sp<MetaData> &meta = source->getFormat();
+    (*decoder)->configure(meta);
+
+    if (audio) {
+        int32_t sampleRate;
+        int32_t channelCount;
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+        CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
+
+        channelCount = 2;  // XXX
+
+        CHECK_EQ(mAudioSink->open(sampleRate, channelCount), (status_t)OK);
+        mAudioSink->start();
+    }
+
+    return OK;
+}
+
+status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
+    sp<AMessage> reply;
+    CHECK(msg->findMessage("reply", &reply));
+
+    if ((audio && mFlushingAudio == FLUSHING_DECODER)
+            || (!audio && mFlushingVideo == FLUSHING_DECODER)) {
+        reply->setInt32("err", INFO_DISCONTINUITY);
+        reply->post();
+        return OK;
+    }
+
+    sp<ABuffer> accessUnit;
+    status_t err = dequeueAccessUnit(
+            audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO,
+            &accessUnit);
+
+    if (err == -EWOULDBLOCK) {
+        return err;
+    } else if (err != OK) {
+        if (err == INFO_DISCONTINUITY) {
+            LOGI("%s discontinuity", audio ? "audio" : "video");
+            (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
+            mRenderer->flush(audio);
+
+            if (audio) {
+                CHECK(mFlushingAudio == NONE
+                        || mFlushingAudio == AWAITING_DISCONTINUITY);
+                mFlushingAudio = FLUSHING_DECODER;
+                if (mFlushingVideo == NONE) {
+                    mFlushingVideo = (mVideoDecoder != NULL)
+                        ? AWAITING_DISCONTINUITY
+                        : FLUSHED;
+                }
+            } else {
+                CHECK(mFlushingVideo == NONE
+                        || mFlushingVideo == AWAITING_DISCONTINUITY);
+                mFlushingVideo = FLUSHING_DECODER;
+                if (mFlushingAudio == NONE) {
+                    mFlushingAudio = (mAudioDecoder != NULL)
+                        ? AWAITING_DISCONTINUITY
+                        : FLUSHED;
+                }
+            }
+        }
+
+        reply->setInt32("err", err);
+        reply->post();
+        return OK;
+    }
+
+    LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
+
+#if 0
+    int64_t mediaTimeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
+    LOGI("feeding %s input buffer at media time %.2f secs",
+         audio ? "audio" : "video",
+         mediaTimeUs / 1E6);
+#endif
+
+    reply->setObject("buffer", accessUnit);
+    reply->post();
+
+    return OK;
+}
+
+void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) {
+    LOGV("renderBuffer %s", audio ? "audio" : "video");
+
+    sp<AMessage> reply;
+    CHECK(msg->findMessage("reply", &reply));
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+
+    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+    mRenderer->queueBuffer(audio, buffer, reply);
+}
+
+void NuPlayer::notifyListener(int msg, int ext1, int ext2) {
+    if (mListener == NULL) {
+        return;
+    }
+
+    sp<MediaPlayerBase> listener = mListener.promote();
+
+    if (listener == NULL) {
+        return;
+    }
+
+    listener->sendEvent(msg, ext1, ext2);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
new file mode 100644
index 0000000..9a5a6c4
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NU_PLAYER_H_
+
+#define NU_PLAYER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/AHandler.h>
+
+#include "ATSParser.h"
+#include "AnotherPacketSource.h"
+
+namespace android {
+
+struct ACodec;
+struct MetaData;
+
+struct NuPlayer : public AHandler {
+    NuPlayer();
+
+    void setListener(const wp<MediaPlayerBase> &listener);
+
+    void setDataSource(const sp<IStreamSource> &source);
+    void setVideoSurface(const sp<Surface> &surface);
+    void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
+    void start();
+
+protected:
+    virtual ~NuPlayer();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    struct Renderer;
+    struct Decoder;
+    struct NuPlayerStreamListener;
+
+    enum {
+        kWhatSetDataSource,
+        kWhatSetVideoSurface,
+        kWhatSetAudioSink,
+        kWhatMoreDataQueued,
+        kWhatStart,
+        kWhatScanSources,
+        kWhatVideoNotify,
+        kWhatAudioNotify,
+        kWhatRendererNotify,
+    };
+
+    wp<MediaPlayerBase> mListener;
+    sp<IStreamSource> mSource;
+    sp<Surface> mSurface;
+    sp<MediaPlayerBase::AudioSink> mAudioSink;
+    sp<NuPlayerStreamListener> mStreamListener;
+    sp<ATSParser> mTSParser;
+    sp<Decoder> mVideoDecoder;
+    sp<Decoder> mAudioDecoder;
+    sp<Renderer> mRenderer;
+
+    bool mEOS;
+    bool mAudioEOS;
+    bool mVideoEOS;
+
+    enum FlushStatus {
+        NONE,
+        AWAITING_DISCONTINUITY,
+        FLUSHING_DECODER,
+        FLUSHED
+    };
+
+    FlushStatus mFlushingAudio;
+    FlushStatus mFlushingVideo;
+
+    status_t instantiateDecoder(
+            bool audio, sp<Decoder> *decoder);
+
+    status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
+    void renderBuffer(bool audio, const sp<AMessage> &msg);
+
+    status_t dequeueNextAccessUnit(
+            ATSParser::SourceType *type, sp<ABuffer> *accessUnit);
+
+    status_t dequeueAccessUnit(
+            ATSParser::SourceType type, sp<ABuffer> *accessUnit);
+
+    void feedMoreTSData();
+    void notifyListener(int msg, int ext1, int ext2);
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
+};
+
+}  // namespace android
+
+#endif  // NU_PLAYER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
new file mode 100644
index 0000000..d1ed222
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerDecoder"
+#include <utils/Log.h>
+
+#include "NuPlayerDecoder.h"
+
+#include "DecoderWrapper.h"
+#include "ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+NuPlayer::Decoder::Decoder(
+        const sp<AMessage> &notify, const sp<Surface> &surface)
+    : mNotify(notify),
+      mSurface(surface) {
+}
+
+NuPlayer::Decoder::~Decoder() {
+}
+
+void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
+    CHECK(mCodec == NULL);
+    CHECK(mWrapper == NULL);
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    sp<AMessage> notifyMsg =
+        new AMessage(kWhatCodecNotify, id());
+
+    sp<AMessage> format = makeFormat(meta);
+
+    if (mSurface != NULL) {
+        format->setObject("surface", mSurface);
+    }
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        mWrapper = new DecoderWrapper;
+        looper()->registerHandler(mWrapper);
+
+        mWrapper->setNotificationMessage(notifyMsg);
+        mWrapper->initiateSetup(format);
+    } else {
+        mCodec = new ACodec;
+        looper()->registerHandler(mCodec);
+
+        mCodec->setNotificationMessage(notifyMsg);
+        mCodec->initiateSetup(format);
+    }
+}
+
+void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatCodecNotify:
+        {
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            if (what == ACodec::kWhatFillThisBuffer) {
+                onFillThisBuffer(msg);
+            } else {
+                sp<AMessage> notify = mNotify->dup();
+                notify->setMessage("codec-request", msg);
+                notify->post();
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
+    CHECK(mCSD.isEmpty());
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    sp<AMessage> msg = new AMessage;
+    msg->setString("mime", mime);
+
+    if (!strncasecmp("video/", mime, 6)) {
+        int32_t width, height;
+        CHECK(meta->findInt32(kKeyWidth, &width));
+        CHECK(meta->findInt32(kKeyHeight, &height));
+
+        msg->setInt32("width", width);
+        msg->setInt32("height", height);
+    } else {
+        CHECK(!strncasecmp("audio/", mime, 6));
+
+        int32_t numChannels, sampleRate;
+        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+        msg->setInt32("channel-count", numChannels);
+        msg->setInt32("sample-rate", sampleRate);
+    }
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+        // Parse the AVCDecoderConfigurationRecord
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size >= 7);
+        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+        uint8_t profile = ptr[1];
+        uint8_t level = ptr[3];
+
+        // There is decodable content out there that fails the following
+        // assertion, let's be lenient for now...
+        // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
+
+        size_t lengthSize = 1 + (ptr[4] & 3);
+
+        // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+        // violates it...
+        // CHECK((ptr[5] >> 5) == 7);  // reserved
+
+        size_t numSeqParameterSets = ptr[5] & 31;
+
+        ptr += 6;
+        size -= 6;
+
+        sp<ABuffer> buffer = new ABuffer(1024);
+        buffer->setRange(0, 0);
+
+        for (size_t i = 0; i < numSeqParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+            buffer->setRange(0, buffer->size() + 4 + length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        buffer->meta()->setInt32("csd", true);
+        mCSD.push(buffer);
+
+        buffer = new ABuffer(1024);
+        buffer->setRange(0, 0);
+
+        CHECK(size >= 1);
+        size_t numPictureParameterSets = *ptr;
+        ++ptr;
+        --size;
+
+        for (size_t i = 0; i < numPictureParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+            buffer->setRange(0, buffer->size() + 4 + length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        buffer->meta()->setInt32("csd", true);
+        mCSD.push(buffer);
+
+        msg->setObject("csd", buffer);
+    } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+#if 0
+        ESDS esds((const char *)data, size);
+        CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+        const void *codec_specific_data;
+        size_t codec_specific_data_size;
+        esds.getCodecSpecificInfo(
+                &codec_specific_data, &codec_specific_data_size);
+
+        sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+        memcpy(buffer->data(), codec_specific_data,
+               codec_specific_data_size);
+
+        buffer->meta()->setInt32("csd", true);
+        mCSD.push(buffer);
+#else
+        sp<ABuffer> buffer = new ABuffer(size);
+        memcpy(buffer->data(), data, size);
+
+        msg->setObject("esds", buffer);
+#endif
+    }
+
+    int32_t maxInputSize;
+    if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+        msg->setInt32("max-input-size", maxInputSize);
+    }
+
+    mCSDIndex = 0;
+
+    return msg;
+}
+
+void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) {
+    sp<AMessage> reply;
+    CHECK(msg->findMessage("reply", &reply));
+
+#if 0
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+    sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+#else
+    sp<ABuffer> outBuffer;
+#endif
+
+    if (mCSDIndex < mCSD.size()) {
+        outBuffer = mCSD.editItemAt(mCSDIndex++);
+        outBuffer->meta()->setInt64("timeUs", 0);
+
+        reply->setObject("buffer", outBuffer);
+        reply->post();
+        return;
+    }
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setMessage("codec-request", msg);
+    notify->post();
+}
+
+void NuPlayer::Decoder::signalFlush() {
+    if (mCodec != NULL) {
+        mCodec->signalFlush();
+    } else {
+        CHECK(mWrapper != NULL);
+        mWrapper->signalFlush();
+    }
+}
+
+void NuPlayer::Decoder::signalResume() {
+    if (mCodec != NULL) {
+        mCodec->signalResume();
+    } else {
+        CHECK(mWrapper != NULL);
+        mWrapper->signalResume();
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
new file mode 100644
index 0000000..77800be
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NUPLAYER_DECODER_H_
+
+#define NUPLAYER_DECODER_H_
+
+#include "NuPlayer.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct DecoderWrapper;
+
+struct NuPlayer::Decoder : public AHandler {
+    Decoder(const sp<AMessage> &notify, const sp<Surface> &surface = NULL);
+
+    void configure(const sp<MetaData> &meta);
+    void signalFlush();
+    void signalResume();
+
+protected:
+    virtual ~Decoder();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatCodecNotify,
+    };
+
+    sp<AMessage> mNotify;
+    sp<Surface> mSurface;
+
+    sp<ACodec> mCodec;
+    sp<DecoderWrapper> mWrapper;
+
+    Vector<sp<ABuffer> > mCSD;
+    size_t mCSDIndex;
+
+    sp<AMessage> makeFormat(const sp<MetaData> &meta);
+
+    void onFillThisBuffer(const sp<AMessage> &msg);
+
+    DISALLOW_EVIL_CONSTRUCTORS(Decoder);
+};
+
+}  // namespace android
+
+#endif  // NUPLAYER_DECODER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
new file mode 100644
index 0000000..b79251a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerDriver"
+#include <utils/Log.h>
+
+#include "NuPlayerDriver.h"
+
+#include "NuPlayer.h"
+
+#include <media/stagefright/foundation/ALooper.h>
+
+namespace android {
+
+NuPlayerDriver::NuPlayerDriver()
+    : mLooper(new ALooper) {
+    mLooper->setName("NuPlayerDriver Looper");
+
+    mLooper->start(
+            false, /* runOnCallingThread */
+            true,  /* canCallJava */
+            PRIORITY_AUDIO);
+
+    mPlayer = new NuPlayer;
+    mLooper->registerHandler(mPlayer);
+
+    mPlayer->setListener(this);
+}
+
+NuPlayerDriver::~NuPlayerDriver() {
+    mLooper->stop();
+}
+
+status_t NuPlayerDriver::initCheck() {
+    return OK;
+}
+
+status_t NuPlayerDriver::setDataSource(
+        const char *url, const KeyedVector<String8, String8> *headers) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
+    mPlayer->setDataSource(source);
+
+    return OK;
+}
+
+status_t NuPlayerDriver::setVideoSurface(const sp<Surface> &surface) {
+    mPlayer->setVideoSurface(surface);
+
+    return OK;
+}
+
+status_t NuPlayerDriver::prepare() {
+    return OK;
+}
+
+status_t NuPlayerDriver::prepareAsync() {
+    return OK;
+}
+
+status_t NuPlayerDriver::start() {
+    mPlayer->start();
+
+    return OK;
+}
+
+status_t NuPlayerDriver::stop() {
+    return OK;
+}
+
+status_t NuPlayerDriver::pause() {
+    return OK;
+}
+
+bool NuPlayerDriver::isPlaying() {
+    return false;
+}
+
+status_t NuPlayerDriver::seekTo(int msec) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getCurrentPosition(int *msec) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getDuration(int *msec) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::reset() {
+    return OK;
+}
+
+status_t NuPlayerDriver::setLooping(int loop) {
+    return INVALID_OPERATION;
+}
+
+player_type NuPlayerDriver::playerType() {
+    return NU_PLAYER;
+}
+
+status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
+    return INVALID_OPERATION;
+}
+
+void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
+    mPlayer->setAudioSink(audioSink);
+}
+
+status_t NuPlayerDriver::getMetadata(
+        const media::Metadata::Filter& ids, Parcel *records) {
+    return INVALID_OPERATION;
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
new file mode 100644
index 0000000..245f1dd
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/MediaPlayerInterface.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ALooper;
+struct NuPlayer;
+
+struct NuPlayerDriver : public MediaPlayerInterface {
+    NuPlayerDriver();
+
+    virtual status_t initCheck();
+
+    virtual status_t setDataSource(
+            const char *url, const KeyedVector<String8, String8> *headers);
+
+    virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+
+    virtual status_t setDataSource(const sp<IStreamSource> &source);
+
+    virtual status_t setVideoSurface(const sp<Surface> &surface);
+    virtual status_t prepare();
+    virtual status_t prepareAsync();
+    virtual status_t start();
+    virtual status_t stop();
+    virtual status_t pause();
+    virtual bool isPlaying();
+    virtual status_t seekTo(int msec);
+    virtual status_t getCurrentPosition(int *msec);
+    virtual status_t getDuration(int *msec);
+    virtual status_t reset();
+    virtual status_t setLooping(int loop);
+    virtual player_type playerType();
+    virtual status_t invoke(const Parcel &request, Parcel *reply);
+    virtual void setAudioSink(const sp<AudioSink> &audioSink);
+
+    virtual status_t getMetadata(
+            const media::Metadata::Filter& ids, Parcel *records);
+
+protected:
+    virtual ~NuPlayerDriver();
+
+private:
+    sp<ALooper> mLooper;
+    sp<NuPlayer> mPlayer;
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuPlayerDriver);
+};
+
+}  // namespace android
+
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
new file mode 100644
index 0000000..855bc0ae
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerRenderer"
+#include <utils/Log.h>
+
+#include "NuPlayerRenderer.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+NuPlayer::Renderer::Renderer(
+        const sp<MediaPlayerBase::AudioSink> &sink,
+        const sp<AMessage> &notify)
+    : mAudioSink(sink),
+      mNotify(notify),
+      mNumFramesWritten(0),
+      mDrainAudioQueuePending(false),
+      mDrainVideoQueuePending(false),
+      mAudioQueueGeneration(0),
+      mVideoQueueGeneration(0),
+      mAnchorTimeMediaUs(-1),
+      mAnchorTimeRealUs(-1),
+      mFlushingAudio(false),
+      mFlushingVideo(false),
+      mSyncQueues(true) {
+}
+
+NuPlayer::Renderer::~Renderer() {
+}
+
+void NuPlayer::Renderer::queueBuffer(
+        bool audio,
+        const sp<ABuffer> &buffer,
+        const sp<AMessage> &notifyConsumed) {
+    sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
+    msg->setInt32("audio", static_cast<int32_t>(audio));
+    msg->setObject("buffer", buffer);
+    msg->setMessage("notifyConsumed", notifyConsumed);
+    msg->post();
+}
+
+void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    sp<AMessage> msg = new AMessage(kWhatQueueEOS, id());
+    msg->setInt32("audio", static_cast<int32_t>(audio));
+    msg->setInt32("finalResult", finalResult);
+    msg->post();
+}
+
+void NuPlayer::Renderer::flush(bool audio) {
+    {
+        Mutex::Autolock autoLock(mFlushLock);
+        if (audio) {
+            CHECK(!mFlushingAudio);
+            mFlushingAudio = true;
+        } else {
+            CHECK(!mFlushingVideo);
+            mFlushingVideo = true;
+        }
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatFlush, id());
+    msg->setInt32("audio", static_cast<int32_t>(audio));
+    msg->post();
+}
+
+void NuPlayer::Renderer::signalTimeDiscontinuity() {
+    CHECK(mAudioQueue.empty());
+    CHECK(mVideoQueue.empty());
+    mAnchorTimeMediaUs = -1;
+    mAnchorTimeRealUs = -1;
+    mSyncQueues = true;
+}
+
+void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatDrainAudioQueue:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mAudioQueueGeneration) {
+                break;
+            }
+
+            mDrainAudioQueuePending = false;
+
+            onDrainAudioQueue();
+
+            postDrainAudioQueue();
+            break;
+        }
+
+        case kWhatDrainVideoQueue:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mVideoQueueGeneration) {
+                break;
+            }
+
+            mDrainVideoQueuePending = false;
+
+            onDrainVideoQueue();
+
+            postDrainVideoQueue();
+            break;
+        }
+
+        case kWhatQueueBuffer:
+        {
+            onQueueBuffer(msg);
+            break;
+        }
+
+        case kWhatQueueEOS:
+        {
+            onQueueEOS(msg);
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            onFlush(msg);
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void NuPlayer::Renderer::postDrainAudioQueue() {
+    if (mDrainAudioQueuePending || mSyncQueues) {
+        return;
+    }
+
+    if (mAudioQueue.empty()) {
+        return;
+    }
+
+    mDrainAudioQueuePending = true;
+    sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
+    msg->setInt32("generation", mAudioQueueGeneration);
+    msg->post(10000);
+}
+
+void NuPlayer::Renderer::onDrainAudioQueue() {
+    uint32_t numFramesPlayed;
+    CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+
+    ssize_t numFramesAvailableToWrite =
+        mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
+
+    CHECK_GE(numFramesAvailableToWrite, 0);
+
+    size_t numBytesAvailableToWrite =
+        numFramesAvailableToWrite * mAudioSink->frameSize();
+
+    while (numBytesAvailableToWrite > 0) {
+        if (mAudioQueue.empty()) {
+            break;
+        }
+
+        QueueEntry *entry = &*mAudioQueue.begin();
+
+        if (entry->mBuffer == NULL) {
+            // EOS
+
+            notifyEOS(true /* audio */);
+
+            mAudioQueue.erase(mAudioQueue.begin());
+            entry = NULL;
+            return;
+        }
+
+        if (entry->mOffset == 0) {
+            int64_t mediaTimeUs;
+            CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+            LOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
+
+            mAnchorTimeMediaUs = mediaTimeUs;
+
+            uint32_t numFramesPlayed;
+            CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+
+            uint32_t numFramesPendingPlayout =
+                mNumFramesWritten - numFramesPlayed;
+
+            int64_t realTimeOffsetUs =
+                (mAudioSink->latency() / 2  /* XXX */
+                    + numFramesPendingPlayout
+                        * mAudioSink->msecsPerFrame()) * 1000ll;
+
+            // LOGI("realTimeOffsetUs = %lld us", realTimeOffsetUs);
+
+            mAnchorTimeRealUs =
+                ALooper::GetNowUs() + realTimeOffsetUs;
+        }
+
+        size_t copy = entry->mBuffer->size() - entry->mOffset;
+        if (copy > numBytesAvailableToWrite) {
+            copy = numBytesAvailableToWrite;
+        }
+
+        CHECK_EQ(mAudioSink->write(
+                    entry->mBuffer->data() + entry->mOffset, copy),
+                 (ssize_t)copy);
+
+        entry->mOffset += copy;
+        if (entry->mOffset == entry->mBuffer->size()) {
+            entry->mNotifyConsumed->post();
+            mAudioQueue.erase(mAudioQueue.begin());
+            entry = NULL;
+        }
+
+        numBytesAvailableToWrite -= copy;
+        mNumFramesWritten += copy / mAudioSink->frameSize();
+    }
+}
+
+void NuPlayer::Renderer::postDrainVideoQueue() {
+    if (mDrainVideoQueuePending || mSyncQueues) {
+        return;
+    }
+
+    if (mVideoQueue.empty()) {
+        return;
+    }
+
+    QueueEntry &entry = *mVideoQueue.begin();
+
+    sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
+    msg->setInt32("generation", mVideoQueueGeneration);
+
+    int64_t delayUs;
+
+    if (entry.mBuffer == NULL) {
+        // EOS doesn't carry a timestamp.
+        delayUs = 0;
+    } else {
+        int64_t mediaTimeUs;
+        CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+        if (mAnchorTimeMediaUs < 0) {
+            delayUs = 0;
+
+            if (mAudioSink == NULL) {
+                mAnchorTimeMediaUs = mediaTimeUs;
+                mAnchorTimeRealUs = ALooper::GetNowUs();
+            }
+        } else {
+            int64_t realTimeUs =
+                (mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs;
+
+            delayUs = realTimeUs - ALooper::GetNowUs();
+        }
+    }
+
+    msg->post(delayUs);
+
+    mDrainVideoQueuePending = true;
+}
+
+void NuPlayer::Renderer::onDrainVideoQueue() {
+    if (mVideoQueue.empty()) {
+        return;
+    }
+
+    QueueEntry *entry = &*mVideoQueue.begin();
+
+    if (entry->mBuffer == NULL) {
+        // EOS
+
+        notifyEOS(false /* audio */);
+
+        mVideoQueue.erase(mVideoQueue.begin());
+        entry = NULL;
+        return;
+    }
+
+#if 0
+    int64_t mediaTimeUs;
+    CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+    LOGI("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
+#endif
+
+    entry->mNotifyConsumed->setInt32("render", true);
+    entry->mNotifyConsumed->post();
+    mVideoQueue.erase(mVideoQueue.begin());
+    entry = NULL;
+}
+
+void NuPlayer::Renderer::notifyEOS(bool audio) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatEOS);
+    notify->setInt32("audio", static_cast<int32_t>(audio));
+    notify->post();
+}
+
+void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
+    int32_t audio;
+    CHECK(msg->findInt32("audio", &audio));
+
+    if (dropBufferWhileFlushing(audio, msg)) {
+        return;
+    }
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+    sp<AMessage> notifyConsumed;
+    CHECK(msg->findMessage("notifyConsumed", &notifyConsumed));
+
+    QueueEntry entry;
+    entry.mBuffer = buffer;
+    entry.mNotifyConsumed = notifyConsumed;
+    entry.mOffset = 0;
+    entry.mFinalResult = OK;
+
+    if (audio) {
+        mAudioQueue.push_back(entry);
+        postDrainAudioQueue();
+    } else {
+        mVideoQueue.push_back(entry);
+        postDrainVideoQueue();
+    }
+
+    if (mSyncQueues && !mAudioQueue.empty() && !mVideoQueue.empty()) {
+        int64_t firstAudioTimeUs;
+        int64_t firstVideoTimeUs;
+        CHECK((*mAudioQueue.begin()).mBuffer->meta()
+                ->findInt64("timeUs", &firstAudioTimeUs));
+        CHECK((*mVideoQueue.begin()).mBuffer->meta()
+                ->findInt64("timeUs", &firstVideoTimeUs));
+
+        int64_t diff = firstVideoTimeUs - firstAudioTimeUs;
+
+        LOGV("queueDiff = %.2f secs", diff / 1E6);
+
+        if (diff > 100000ll) {
+            // Audio data starts More than 0.1 secs before video.
+            // Drop some audio.
+
+            (*mAudioQueue.begin()).mNotifyConsumed->post();
+            mAudioQueue.erase(mAudioQueue.begin());
+            return;
+        }
+
+        syncQueuesDone();
+    }
+}
+
+void NuPlayer::Renderer::syncQueuesDone() {
+    if (!mSyncQueues) {
+        return;
+    }
+
+    mSyncQueues = false;
+
+    if (!mAudioQueue.empty()) {
+        postDrainAudioQueue();
+    }
+
+    if (!mVideoQueue.empty()) {
+        postDrainVideoQueue();
+    }
+}
+
+void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) {
+    int32_t audio;
+    CHECK(msg->findInt32("audio", &audio));
+
+    if (dropBufferWhileFlushing(audio, msg)) {
+        return;
+    }
+
+    int32_t finalResult;
+    CHECK(msg->findInt32("finalResult", &finalResult));
+
+    QueueEntry entry;
+    entry.mOffset = 0;
+    entry.mFinalResult = finalResult;
+
+    if (audio) {
+        mAudioQueue.push_back(entry);
+        postDrainAudioQueue();
+    } else {
+        mVideoQueue.push_back(entry);
+        postDrainVideoQueue();
+    }
+}
+
+void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
+    int32_t audio;
+    CHECK(msg->findInt32("audio", &audio));
+
+    // If we're currently syncing the queues, i.e. dropping audio while
+    // aligning the first audio/video buffer times and only one of the
+    // two queues has data, we may starve that queue by not requesting
+    // more buffers from the decoder. If the other source then encounters
+    // a discontinuity that leads to flushing, we'll never find the
+    // corresponding discontinuity on the other queue.
+    // Therefore we'll stop syncing the queues if at least one of them
+    // is flushed.
+    syncQueuesDone();
+
+    if (audio) {
+        flushQueue(&mAudioQueue);
+
+        Mutex::Autolock autoLock(mFlushLock);
+        mFlushingAudio = false;
+
+        mDrainAudioQueuePending = false;
+        ++mAudioQueueGeneration;
+    } else {
+        flushQueue(&mVideoQueue);
+
+        Mutex::Autolock autoLock(mFlushLock);
+        mFlushingVideo = false;
+
+        mDrainVideoQueuePending = false;
+        ++mVideoQueueGeneration;
+    }
+
+    notifyFlushComplete(audio);
+}
+
+void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) {
+    while (!queue->empty()) {
+        QueueEntry *entry = &*queue->begin();
+
+        if (entry->mBuffer != NULL) {
+            entry->mNotifyConsumed->post();
+        }
+
+        queue->erase(queue->begin());
+        entry = NULL;
+    }
+}
+
+void NuPlayer::Renderer::notifyFlushComplete(bool audio) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatFlushComplete);
+    notify->setInt32("audio", static_cast<int32_t>(audio));
+    notify->post();
+}
+
+bool NuPlayer::Renderer::dropBufferWhileFlushing(
+        bool audio, const sp<AMessage> &msg) {
+    bool flushing = false;
+
+    {
+        Mutex::Autolock autoLock(mFlushLock);
+        if (audio) {
+            flushing = mFlushingAudio;
+        } else {
+            flushing = mFlushingVideo;
+        }
+    }
+
+    if (!flushing) {
+        return false;
+    }
+
+    sp<AMessage> notifyConsumed;
+    if (msg->findMessage("notifyConsumed", &notifyConsumed)) {
+        notifyConsumed->post();
+    }
+
+    return true;
+}
+
+}  // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
new file mode 100644
index 0000000..834ddc5
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NUPLAYER_RENDERER_H_
+
+#define NUPLAYER_RENDERER_H_
+
+#include "NuPlayer.h"
+
+namespace android {
+
+struct NuPlayer::Renderer : public AHandler {
+    Renderer(const sp<MediaPlayerBase::AudioSink> &sink,
+             const sp<AMessage> &notify);
+
+    void queueBuffer(
+            bool audio,
+            const sp<ABuffer> &buffer,
+            const sp<AMessage> &notifyConsumed);
+
+    void queueEOS(bool audio, status_t finalResult);
+
+    void flush(bool audio);
+
+    void signalTimeDiscontinuity();
+
+    enum {
+        kWhatEOS,
+        kWhatFlushComplete,
+    };
+
+protected:
+    virtual ~Renderer();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatDrainAudioQueue,
+        kWhatDrainVideoQueue,
+        kWhatQueueBuffer,
+        kWhatQueueEOS,
+        kWhatFlush,
+    };
+
+    struct QueueEntry {
+        sp<ABuffer> mBuffer;
+        sp<AMessage> mNotifyConsumed;
+        size_t mOffset;
+        status_t mFinalResult;
+    };
+
+    sp<MediaPlayerBase::AudioSink> mAudioSink;
+    sp<AMessage> mNotify;
+    List<QueueEntry> mAudioQueue;
+    List<QueueEntry> mVideoQueue;
+    uint32_t mNumFramesWritten;
+
+    bool mDrainAudioQueuePending;
+    bool mDrainVideoQueuePending;
+    int32_t mAudioQueueGeneration;
+    int32_t mVideoQueueGeneration;
+
+    int64_t mAnchorTimeMediaUs;
+    int64_t mAnchorTimeRealUs;
+
+    Mutex mFlushLock;  // protects the following 2 member vars.
+    bool mFlushingAudio;
+    bool mFlushingVideo;
+
+    bool mSyncQueues;
+
+    void onDrainAudioQueue();
+    void postDrainAudioQueue();
+
+    void onDrainVideoQueue();
+    void postDrainVideoQueue();
+
+    void onQueueBuffer(const sp<AMessage> &msg);
+    void onQueueEOS(const sp<AMessage> &msg);
+    void onFlush(const sp<AMessage> &msg);
+
+    void notifyEOS(bool audio);
+    void notifyFlushComplete(bool audio);
+
+    void flushQueue(List<QueueEntry> *queue);
+    bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);
+    void syncQueuesDone();
+
+    DISALLOW_EVIL_CONSTRUCTORS(Renderer);
+};
+
+}  // namespace android
+
+#endif  // NUPLAYER_RENDERER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
new file mode 100644
index 0000000..92642a8
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerStreamListener"
+#include <utils/Log.h>
+
+#include "NuPlayerStreamListener.h"
+
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener(
+        const sp<IStreamSource> &source,
+        ALooper::handler_id id)
+    : mSource(source),
+      mTargetID(id),
+      mEOS(false),
+      mSendDataNotification(true) {
+    mSource->setListener(this);
+
+    mMemoryDealer = new MemoryDealer(kNumBuffers * kBufferSize);
+    for (size_t i = 0; i < kNumBuffers; ++i) {
+        sp<IMemory> mem = mMemoryDealer->allocate(kBufferSize);
+        CHECK(mem != NULL);
+
+        mBuffers.push(mem);
+    }
+    mSource->setBuffers(mBuffers);
+}
+
+void NuPlayer::NuPlayerStreamListener::start() {
+    for (size_t i = 0; i < kNumBuffers; ++i) {
+        mSource->onBufferAvailable(i);
+    }
+}
+
+void NuPlayer::NuPlayerStreamListener::queueBuffer(size_t index, size_t size) {
+    QueueEntry entry;
+    entry.mIsCommand = false;
+    entry.mIndex = index;
+    entry.mSize = size;
+    entry.mOffset = 0;
+
+    Mutex::Autolock autoLock(mLock);
+    mQueue.push_back(entry);
+
+    if (mSendDataNotification) {
+        mSendDataNotification = false;
+        (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+    }
+}
+
+void NuPlayer::NuPlayerStreamListener::issueCommand(
+        Command cmd, bool synchronous, const sp<AMessage> &extra) {
+    CHECK(!synchronous);
+
+    QueueEntry entry;
+    entry.mIsCommand = true;
+    entry.mCommand = cmd;
+    entry.mExtra = extra;
+
+    Mutex::Autolock autoLock(mLock);
+    mQueue.push_back(entry);
+
+    if (mSendDataNotification) {
+        mSendDataNotification = false;
+        (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+    }
+}
+
+ssize_t NuPlayer::NuPlayerStreamListener::read(void *data, size_t size) {
+    CHECK_GT(size, 0u);
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (mEOS) {
+        return 0;
+    }
+
+    if (mQueue.empty()) {
+        mSendDataNotification = true;
+
+        return -EWOULDBLOCK;
+    }
+
+    QueueEntry *entry = &*mQueue.begin();
+
+    if (entry->mIsCommand) {
+        switch (entry->mCommand) {
+            case EOS:
+            {
+                mQueue.erase(mQueue.begin());
+                entry = NULL;
+
+                mEOS = true;
+                return 0;
+            }
+
+            case DISCONTINUITY:
+            {
+                mQueue.erase(mQueue.begin());
+                entry = NULL;
+
+                return INFO_DISCONTINUITY;
+            }
+
+            default:
+                TRESPASS();
+                break;
+        }
+    }
+
+    size_t copy = entry->mSize;
+    if (copy > size) {
+        copy = size;
+    }
+
+    memcpy(data,
+           (const uint8_t *)mBuffers.editItemAt(entry->mIndex)->pointer()
+            + entry->mOffset,
+           copy);
+
+    entry->mOffset += copy;
+    entry->mSize -= copy;
+
+    if (entry->mSize == 0) {
+        mSource->onBufferAvailable(entry->mIndex);
+        mQueue.erase(mQueue.begin());
+        entry = NULL;
+    }
+
+    return copy;
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
new file mode 100644
index 0000000..f88e945
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NUPLAYER_STREAM_LISTENER_H_
+
+#define NUPLAYER_STREAM_LISTENER_H_
+
+#include "NuPlayer.h"
+
+#include <media/IStreamSource.h>
+
+namespace android {
+
+struct MemoryDealer;
+
+struct NuPlayer::NuPlayerStreamListener : public BnStreamListener {
+    NuPlayerStreamListener(
+            const sp<IStreamSource> &source,
+            ALooper::handler_id targetID);
+
+    virtual void queueBuffer(size_t index, size_t size);
+
+    virtual void issueCommand(
+            Command cmd, bool synchronous, const sp<AMessage> &extra);
+
+    void start();
+    ssize_t read(void *data, size_t size);
+
+private:
+    enum {
+        kNumBuffers = 16,
+        kBufferSize = 188 * 20
+    };
+
+    struct QueueEntry {
+        bool mIsCommand;
+
+        size_t mIndex;
+        size_t mSize;
+        size_t mOffset;
+
+        Command mCommand;
+        sp<AMessage> mExtra;
+    };
+
+    Mutex mLock;
+
+    sp<IStreamSource> mSource;
+    ALooper::handler_id mTargetID;
+    sp<MemoryDealer> mMemoryDealer;
+    Vector<sp<IMemory> > mBuffers;
+    List<QueueEntry> mQueue;
+    bool mEOS;
+    bool mSendDataNotification;
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuPlayerStreamListener);
+};
+
+}  // namespace android
+
+#endif // NUPLAYER_STREAM_LISTENER_H_
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
new file mode 100644
index 0000000..77276ab
--- /dev/null
+++ b/media/libstagefright/ACodec.cpp
@@ -0,0 +1,2097 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ACodec"
+
+#include <media/stagefright/ACodec.h>
+
+#include <binder/MemoryDealer.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/OMXClient.h>
+
+#include <surfaceflinger/Surface.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+struct CodecObserver : public BnOMXObserver {
+    CodecObserver() {}
+
+    void setNotificationMessage(const sp<AMessage> &msg) {
+        mNotify = msg;
+    }
+
+    // from IOMXObserver
+    virtual void onMessage(const omx_message &omx_msg) {
+        sp<AMessage> msg = mNotify->dup();
+
+        msg->setInt32("type", omx_msg.type);
+        msg->setPointer("node", omx_msg.node);
+
+        switch (omx_msg.type) {
+            case omx_message::EVENT:
+            {
+                msg->setInt32("event", omx_msg.u.event_data.event);
+                msg->setInt32("data1", omx_msg.u.event_data.data1);
+                msg->setInt32("data2", omx_msg.u.event_data.data2);
+                break;
+            }
+
+            case omx_message::EMPTY_BUFFER_DONE:
+            {
+                msg->setPointer("buffer", omx_msg.u.buffer_data.buffer);
+                break;
+            }
+
+            case omx_message::FILL_BUFFER_DONE:
+            {
+                msg->setPointer(
+                        "buffer", omx_msg.u.extended_buffer_data.buffer);
+                msg->setInt32(
+                        "range_offset",
+                        omx_msg.u.extended_buffer_data.range_offset);
+                msg->setInt32(
+                        "range_length",
+                        omx_msg.u.extended_buffer_data.range_length);
+                msg->setInt32(
+                        "flags",
+                        omx_msg.u.extended_buffer_data.flags);
+                msg->setInt64(
+                        "timestamp",
+                        omx_msg.u.extended_buffer_data.timestamp);
+                msg->setPointer(
+                        "platform_private",
+                        omx_msg.u.extended_buffer_data.platform_private);
+                msg->setPointer(
+                        "data_ptr",
+                        omx_msg.u.extended_buffer_data.data_ptr);
+                break;
+            }
+
+            default:
+                TRESPASS();
+                break;
+        }
+
+        msg->post();
+    }
+
+protected:
+    virtual ~CodecObserver() {}
+
+private:
+    sp<AMessage> mNotify;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CodecObserver);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::BaseState : public AState {
+    BaseState(ACodec *codec, const sp<AState> &parentState = NULL);
+
+protected:
+    enum PortMode {
+        KEEP_BUFFERS,
+        RESUBMIT_BUFFERS,
+        FREE_BUFFERS,
+    };
+
+    ACodec *mCodec;
+
+    virtual PortMode getPortMode(OMX_U32 portIndex);
+
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+    virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+    virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+    void postFillThisBuffer(BufferInfo *info);
+
+private:
+    bool onOMXMessage(const sp<AMessage> &msg);
+
+    bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID);
+
+    bool onOMXFillBufferDone(
+            IOMX::buffer_id bufferID,
+            size_t rangeOffset, size_t rangeLength,
+            OMX_U32 flags,
+            int64_t timeUs,
+            void *platformPrivate,
+            void *dataPtr);
+
+    void getMoreInputDataIfPossible();
+
+    DISALLOW_EVIL_CONSTRUCTORS(BaseState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::UninitializedState : public ACodec::BaseState {
+    UninitializedState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    void onSetup(const sp<AMessage> &msg);
+
+    DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::LoadedToIdleState : public ACodec::BaseState {
+    LoadedToIdleState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+    virtual void stateEntered();
+
+private:
+    status_t allocateBuffers();
+
+    DISALLOW_EVIL_CONSTRUCTORS(LoadedToIdleState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::IdleToExecutingState : public ACodec::BaseState {
+    IdleToExecutingState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+    virtual void stateEntered();
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(IdleToExecutingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ExecutingState : public ACodec::BaseState {
+    ExecutingState(ACodec *codec);
+
+    void submitOutputBuffers();
+
+    // Submit output buffers to the decoder, submit input buffers to client
+    // to fill with data.
+    void resume();
+
+protected:
+    virtual PortMode getPortMode(OMX_U32 portIndex);
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(ExecutingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::OutputPortSettingsChangedState : public ACodec::BaseState {
+    OutputPortSettingsChangedState(ACodec *codec);
+
+protected:
+    virtual PortMode getPortMode(OMX_U32 portIndex);
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(OutputPortSettingsChangedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ExecutingToIdleState : public ACodec::BaseState {
+    ExecutingToIdleState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+    virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+    virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+private:
+    void changeStateIfWeOwnAllBuffers();
+
+    DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::IdleToLoadedState : public ACodec::BaseState {
+    IdleToLoadedState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(IdleToLoadedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ErrorState : public ACodec::BaseState {
+    ErrorState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(ErrorState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::FlushingState : public ACodec::BaseState {
+    FlushingState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+    virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+    virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+private:
+    bool mFlushComplete[2];
+
+    void changeStateIfWeOwnAllBuffers();
+
+    DISALLOW_EVIL_CONSTRUCTORS(FlushingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ACodec()
+    : mNode(NULL) {
+    mUninitializedState = new UninitializedState(this);
+    mLoadedToIdleState = new LoadedToIdleState(this);
+    mIdleToExecutingState = new IdleToExecutingState(this);
+    mExecutingState = new ExecutingState(this);
+
+    mOutputPortSettingsChangedState =
+        new OutputPortSettingsChangedState(this);
+
+    mExecutingToIdleState = new ExecutingToIdleState(this);
+    mIdleToLoadedState = new IdleToLoadedState(this);
+    mErrorState = new ErrorState(this);
+    mFlushingState = new FlushingState(this);
+
+    mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
+
+    changeState(mUninitializedState);
+}
+
+ACodec::~ACodec() {
+}
+
+void ACodec::setNotificationMessage(const sp<AMessage> &msg) {
+    mNotify = msg;
+}
+
+void ACodec::initiateSetup(const sp<AMessage> &msg) {
+    msg->setWhat(kWhatSetup);
+    msg->setTarget(id());
+    msg->post();
+}
+
+void ACodec::signalFlush() {
+    (new AMessage(kWhatFlush, id()))->post();
+}
+
+void ACodec::signalResume() {
+    (new AMessage(kWhatResume, id()))->post();
+}
+
+void ACodec::initiateShutdown() {
+    (new AMessage(kWhatShutdown, id()))->post();
+}
+
+status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
+    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+    CHECK(mDealer[portIndex] == NULL);
+    CHECK(mBuffers[portIndex].isEmpty());
+
+    if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
+        return allocateOutputBuffersFromNativeWindow();
+    }
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = portIndex;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    LOGV("[%s] Allocating %lu buffers of size %lu on %s port",
+            mComponentName.c_str(),
+            def.nBufferCountActual, def.nBufferSize,
+            portIndex == kPortIndexInput ? "input" : "output");
+
+    size_t totalSize = def.nBufferCountActual * def.nBufferSize;
+    mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");
+
+    for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+        sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
+        CHECK(mem.get() != NULL);
+
+        IOMX::buffer_id buffer;
+#if 0
+        err = mOMX->allocateBufferWithBackup(mNode, portIndex, mem, &buffer);
+#else
+        err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+#endif
+
+        if (err != OK) {
+            return err;
+        }
+
+        BufferInfo info;
+        info.mBufferID = buffer;
+        info.mStatus = BufferInfo::OWNED_BY_US;
+        info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
+        mBuffers[portIndex].push(info);
+    }
+
+    return OK;
+}
+
+status_t ACodec::allocateOutputBuffersFromNativeWindow() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = native_window_set_buffers_geometry(
+            mNativeWindow.get(),
+            def.format.video.nFrameWidth,
+            def.format.video.nFrameHeight,
+            def.format.video.eColorFormat);
+
+    if (err != 0) {
+        LOGE("native_window_set_buffers_geometry failed: %s (%d)",
+                strerror(-err), -err);
+        return err;
+    }
+
+    // Increase the buffer count by one to allow for the ANativeWindow to hold
+    // on to one of the buffers.
+    def.nBufferCountActual++;
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    // Set up the native window.
+    // XXX TODO: Get the gralloc usage flags from the OMX plugin!
+    err = native_window_set_usage(
+            mNativeWindow.get(),
+            GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+
+    if (err != 0) {
+        LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    err = native_window_set_buffer_count(
+            mNativeWindow.get(), def.nBufferCountActual);
+
+    if (err != 0) {
+        LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
+                -err);
+        return err;
+    }
+
+    // XXX TODO: Do something so the ANativeWindow knows that we'll need to get
+    // the same set of buffers.
+
+    LOGV("[%s] Allocating %lu buffers from a native window of size %lu on "
+         "output port",
+         mComponentName.c_str(), def.nBufferCountActual, def.nBufferSize);
+
+    // Dequeue buffers and send them to OMX
+    OMX_U32 i;
+    for (i = 0; i < def.nBufferCountActual; i++) {
+        android_native_buffer_t *buf;
+        err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
+        if (err != 0) {
+            LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
+            break;
+        }
+
+        sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
+        IOMX::buffer_id bufferId;
+        err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
+                &bufferId);
+        if (err != 0) {
+            break;
+        }
+
+        LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
+             mComponentName.c_str(),
+             bufferId, graphicBuffer.get());
+
+        BufferInfo info;
+        info.mBufferID = bufferId;
+        info.mStatus = BufferInfo::OWNED_BY_US;
+        info.mData = new ABuffer(0);
+        info.mGraphicBuffer = graphicBuffer;
+        mBuffers[kPortIndexOutput].push(info);
+    }
+
+    OMX_U32 cancelStart;
+    OMX_U32 cancelEnd;
+
+    if (err != 0) {
+        // If an error occurred while dequeuing we need to cancel any buffers
+        // that were dequeued.
+        cancelStart = 0;
+        cancelEnd = i;
+    } else {
+        // Return the last two buffers to the native window.
+        // XXX TODO: The number of buffers the native window owns should
+        // probably be queried from it when we put the native window in
+        // fixed buffer pool mode (which needs to be implemented).
+        // Currently it's hard-coded to 2.
+        cancelStart = def.nBufferCountActual - 2;
+        cancelEnd = def.nBufferCountActual;
+    }
+
+    for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
+        BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+        cancelBufferToNativeWindow(info);
+    }
+
+    return err;
+}
+
+status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) {
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+
+    LOGV("[%s] Calling cancelBuffer on buffer %p",
+         mComponentName.c_str(), info->mBufferID);
+
+    int err = mNativeWindow->cancelBuffer(
+        mNativeWindow.get(), info->mGraphicBuffer.get());
+
+    CHECK_EQ(err, 0);
+
+    info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
+
+    return OK;
+}
+
+ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
+    android_native_buffer_t *buf;
+    CHECK_EQ(mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf), 0);
+
+    for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
+        BufferInfo *info =
+            &mBuffers[kPortIndexOutput].editItemAt(i);
+
+        if (info->mGraphicBuffer->handle == buf->handle) {
+            CHECK_EQ((int)info->mStatus,
+                     (int)BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+            info->mStatus = BufferInfo::OWNED_BY_US;
+
+            return info;
+        }
+    }
+
+    TRESPASS();
+
+    return NULL;
+}
+
+status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) {
+    for (size_t i = mBuffers[portIndex].size(); i-- > 0;) {
+        CHECK_EQ((status_t)OK, freeBuffer(portIndex, i));
+    }
+
+    mDealer[portIndex].clear();
+
+    return OK;
+}
+
+status_t ACodec::freeOutputBuffersOwnedByNativeWindow() {
+    for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
+        BufferInfo *info =
+            &mBuffers[kPortIndexOutput].editItemAt(i);
+
+        if (info->mStatus ==
+                BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+            CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i));
+        }
+    }
+
+    return OK;
+}
+
+status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
+    BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+    CHECK(info->mStatus == BufferInfo::OWNED_BY_US
+            || info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+    if (portIndex == kPortIndexOutput && mNativeWindow != NULL
+            && info->mStatus == BufferInfo::OWNED_BY_US) {
+        CHECK_EQ((status_t)OK, cancelBufferToNativeWindow(info));
+    }
+
+    CHECK_EQ(mOMX->freeBuffer(
+                mNode, portIndex, info->mBufferID),
+             (status_t)OK);
+
+    mBuffers[portIndex].removeAt(i);
+
+    return OK;
+}
+
+ACodec::BufferInfo *ACodec::findBufferByID(
+        uint32_t portIndex, IOMX::buffer_id bufferID,
+        ssize_t *index) {
+    for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+        BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+        if (info->mBufferID == bufferID) {
+            if (index != NULL) {
+                *index = i;
+            }
+            return info;
+        }
+    }
+
+    TRESPASS();
+
+    return NULL;
+}
+
+void ACodec::setComponentRole(
+        bool isEncoder, const char *mime) {
+    struct MimeToRole {
+        const char *mime;
+        const char *decoderRole;
+        const char *encoderRole;
+    };
+
+    static const MimeToRole kMimeToRole[] = {
+        { MEDIA_MIMETYPE_AUDIO_MPEG,
+            "audio_decoder.mp3", "audio_encoder.mp3" },
+        { MEDIA_MIMETYPE_AUDIO_AMR_NB,
+            "audio_decoder.amrnb", "audio_encoder.amrnb" },
+        { MEDIA_MIMETYPE_AUDIO_AMR_WB,
+            "audio_decoder.amrwb", "audio_encoder.amrwb" },
+        { MEDIA_MIMETYPE_AUDIO_AAC,
+            "audio_decoder.aac", "audio_encoder.aac" },
+        { MEDIA_MIMETYPE_VIDEO_AVC,
+            "video_decoder.avc", "video_encoder.avc" },
+        { MEDIA_MIMETYPE_VIDEO_MPEG4,
+            "video_decoder.mpeg4", "video_encoder.mpeg4" },
+        { MEDIA_MIMETYPE_VIDEO_H263,
+            "video_decoder.h263", "video_encoder.h263" },
+    };
+
+    static const size_t kNumMimeToRole =
+        sizeof(kMimeToRole) / sizeof(kMimeToRole[0]);
+
+    size_t i;
+    for (i = 0; i < kNumMimeToRole; ++i) {
+        if (!strcasecmp(mime, kMimeToRole[i].mime)) {
+            break;
+        }
+    }
+
+    if (i == kNumMimeToRole) {
+        return;
+    }
+
+    const char *role =
+        isEncoder ? kMimeToRole[i].encoderRole
+                  : kMimeToRole[i].decoderRole;
+
+    if (role != NULL) {
+        OMX_PARAM_COMPONENTROLETYPE roleParams;
+        InitOMXParams(&roleParams);
+
+        strncpy((char *)roleParams.cRole,
+                role, OMX_MAX_STRINGNAME_SIZE - 1);
+
+        roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+        status_t err = mOMX->setParameter(
+                mNode, OMX_IndexParamStandardComponentRole,
+                &roleParams, sizeof(roleParams));
+
+        if (err != OK) {
+            LOGW("[%s] Failed to set standard component role '%s'.",
+                 mComponentName.c_str(), role);
+        }
+    }
+}
+
+void ACodec::configureCodec(
+        const char *mime, const sp<AMessage> &msg) {
+    setComponentRole(false /* isEncoder */, mime);
+
+    if (!strncasecmp(mime, "video/", 6)) {
+        int32_t width, height;
+        CHECK(msg->findInt32("width", &width));
+        CHECK(msg->findInt32("height", &height));
+
+        CHECK_EQ(setupVideoDecoder(mime, width, height),
+                 (status_t)OK);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        int32_t numChannels, sampleRate;
+        CHECK(msg->findInt32("channel-count", &numChannels));
+        CHECK(msg->findInt32("sample-rate", &sampleRate));
+
+        CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+    } else {
+        TRESPASS();
+    }
+
+    int32_t maxInputSize;
+    if (msg->findInt32("max-input-size", &maxInputSize)) {
+        CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize),
+                 (status_t)OK);
+    } else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) {
+        CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192),  // XXX
+                 (status_t)OK);
+    }
+}
+
+status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = portIndex;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (def.nBufferSize >= size) {
+        return OK;
+    }
+
+    def.nBufferSize = size;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    CHECK(def.nBufferSize >= size);
+
+    return OK;
+}
+
+status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
+    OMX_AUDIO_PARAM_AACPROFILETYPE profile;
+    InitOMXParams(&profile);
+    profile.nPortIndex = kPortIndexInput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+    if (err != OK) {
+        return err;
+    }
+
+    profile.nChannels = numChannels;
+    profile.nSampleRate = sampleRate;
+    profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+    return err;
+}
+
+status_t ACodec::setVideoPortFormatType(
+        OMX_U32 portIndex,
+        OMX_VIDEO_CODINGTYPE compressionFormat,
+        OMX_COLOR_FORMATTYPE colorFormat) {
+    OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+    InitOMXParams(&format);
+    format.nPortIndex = portIndex;
+    format.nIndex = 0;
+    bool found = false;
+
+    OMX_U32 index = 0;
+    for (;;) {
+        format.nIndex = index;
+        status_t err = mOMX->getParameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+
+        if (err != OK) {
+            return err;
+        }
+
+        // The following assertion is violated by TI's video decoder.
+        // CHECK_EQ(format.nIndex, index);
+
+        if (!strcmp("OMX.TI.Video.encoder", mComponentName.c_str())) {
+            if (portIndex == kPortIndexInput
+                    && colorFormat == format.eColorFormat) {
+                // eCompressionFormat does not seem right.
+                found = true;
+                break;
+            }
+            if (portIndex == kPortIndexOutput
+                    && compressionFormat == format.eCompressionFormat) {
+                // eColorFormat does not seem right.
+                found = true;
+                break;
+            }
+        }
+
+        if (format.eCompressionFormat == compressionFormat
+            && format.eColorFormat == colorFormat) {
+            found = true;
+            break;
+        }
+
+        ++index;
+    }
+
+    if (!found) {
+        return UNKNOWN_ERROR;
+    }
+
+    status_t err = mOMX->setParameter(
+            mNode, OMX_IndexParamVideoPortFormat,
+            &format, sizeof(format));
+
+    return err;
+}
+
+status_t ACodec::setSupportedOutputFormat() {
+    OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+    InitOMXParams(&format);
+    format.nPortIndex = kPortIndexOutput;
+    format.nIndex = 0;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoPortFormat,
+            &format, sizeof(format));
+    CHECK_EQ(err, (status_t)OK);
+    CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
+
+    static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+    CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
+           || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
+           || format.eColorFormat == OMX_COLOR_FormatCbYCrY
+           || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
+
+    return mOMX->setParameter(
+            mNode, OMX_IndexParamVideoPortFormat,
+            &format, sizeof(format));
+}
+
+status_t ACodec::setupVideoDecoder(
+        const char *mime, int32_t width, int32_t height) {
+    OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+    if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+        compressionFormat = OMX_VIDEO_CodingAVC;
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+        compressionFormat = OMX_VIDEO_CodingMPEG4;
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+        compressionFormat = OMX_VIDEO_CodingH263;
+    } else {
+        TRESPASS();
+    }
+
+    status_t err = setVideoPortFormatType(
+            kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = setSupportedOutputFormat();
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = setVideoFormatOnPort(
+            kPortIndexInput, width, height, compressionFormat);
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = setVideoFormatOnPort(
+            kPortIndexOutput, width, height, OMX_VIDEO_CodingUnused);
+
+    if (err != OK) {
+        return err;
+    }
+
+    return OK;
+}
+
+status_t ACodec::setVideoFormatOnPort(
+        OMX_U32 portIndex,
+        int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = portIndex;
+
+    OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    CHECK_EQ(err, (status_t)OK);
+
+    if (portIndex == kPortIndexInput) {
+        // XXX Need a (much) better heuristic to compute input buffer sizes.
+        const size_t X = 64 * 1024;
+        if (def.nBufferSize < X) {
+            def.nBufferSize = X;
+        }
+    }
+
+    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
+
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+
+    if (portIndex == kPortIndexInput) {
+        video_def->eCompressionFormat = compressionFormat;
+        video_def->eColorFormat = OMX_COLOR_FormatUnused;
+    }
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    return err;
+}
+
+status_t ACodec::initNativeWindow() {
+    if (mNativeWindow != NULL) {
+        return mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE);
+    }
+
+    mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_FALSE);
+    return OK;
+}
+
+bool ACodec::allYourBuffersAreBelongToUs(
+        OMX_U32 portIndex) {
+    for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+        BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+        if (info->mStatus != BufferInfo::OWNED_BY_US
+                && info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+            LOGV("[%s] Buffer %p on port %ld still has status %d",
+                    mComponentName.c_str(),
+                    info->mBufferID, portIndex, info->mStatus);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool ACodec::allYourBuffersAreBelongToUs() {
+    return allYourBuffersAreBelongToUs(kPortIndexInput)
+        && allYourBuffersAreBelongToUs(kPortIndexOutput);
+}
+
+void ACodec::deferMessage(const sp<AMessage> &msg) {
+    bool wasEmptyBefore = mDeferredQueue.empty();
+    mDeferredQueue.push_back(msg);
+}
+
+void ACodec::processDeferredMessages() {
+    List<sp<AMessage> > queue = mDeferredQueue;
+    mDeferredQueue.clear();
+
+    List<sp<AMessage> >::iterator it = queue.begin();
+    while (it != queue.end()) {
+        onMessageReceived(*it++);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState)
+    : AState(parentState),
+      mCodec(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(OMX_U32 portIndex) {
+    return KEEP_BUFFERS;
+}
+
+bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatInputBufferFilled:
+        {
+            onInputBufferFilled(msg);
+            break;
+        }
+
+        case kWhatOutputBufferDrained:
+        {
+            onOutputBufferDrained(msg);
+            break;
+        }
+
+        case ACodec::kWhatOMXMessage:
+        {
+            return onOMXMessage(msg);
+        }
+
+        default:
+            return false;
+    }
+
+    return true;
+}
+
+bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) {
+    int32_t type;
+    CHECK(msg->findInt32("type", &type));
+
+    IOMX::node_id nodeID;
+    CHECK(msg->findPointer("node", &nodeID));
+    CHECK_EQ(nodeID, mCodec->mNode);
+
+    switch (type) {
+        case omx_message::EVENT:
+        {
+            int32_t event, data1, data2;
+            CHECK(msg->findInt32("event", &event));
+            CHECK(msg->findInt32("data1", &data1));
+            CHECK(msg->findInt32("data2", &data2));
+
+            return onOMXEvent(
+                    static_cast<OMX_EVENTTYPE>(event),
+                    static_cast<OMX_U32>(data1),
+                    static_cast<OMX_U32>(data2));
+        }
+
+        case omx_message::EMPTY_BUFFER_DONE:
+        {
+            IOMX::buffer_id bufferID;
+            CHECK(msg->findPointer("buffer", &bufferID));
+
+            return onOMXEmptyBufferDone(bufferID);
+        }
+
+        case omx_message::FILL_BUFFER_DONE:
+        {
+            IOMX::buffer_id bufferID;
+            CHECK(msg->findPointer("buffer", &bufferID));
+
+            int32_t rangeOffset, rangeLength, flags;
+            int64_t timeUs;
+            void *platformPrivate;
+            void *dataPtr;
+
+            CHECK(msg->findInt32("range_offset", &rangeOffset));
+            CHECK(msg->findInt32("range_length", &rangeLength));
+            CHECK(msg->findInt32("flags", &flags));
+            CHECK(msg->findInt64("timestamp", &timeUs));
+            CHECK(msg->findPointer("platform_private", &platformPrivate));
+            CHECK(msg->findPointer("data_ptr", &dataPtr));
+
+            return onOMXFillBufferDone(
+                    bufferID,
+                    (size_t)rangeOffset, (size_t)rangeLength,
+                    (OMX_U32)flags,
+                    timeUs,
+                    platformPrivate,
+                    dataPtr);
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+bool ACodec::BaseState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    if (event != OMX_EventError) {
+        LOGI("[%s] EVENT(%d, 0x%08lx, 0x%08lx)",
+             mCodec->mComponentName.c_str(), event, data1, data2);
+
+        return false;
+    }
+
+    LOGE("[%s] ERROR(0x%08lx, 0x%08lx)",
+         mCodec->mComponentName.c_str(), data1, data2);
+
+    mCodec->changeState(mCodec->mErrorState);
+
+    return true;
+}
+
+bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
+    BufferInfo *info =
+        mCodec->findBufferByID(kPortIndexInput, bufferID);
+
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
+    info->mStatus = BufferInfo::OWNED_BY_US;
+
+    PortMode mode = getPortMode(kPortIndexInput);
+
+    switch (mode) {
+        case KEEP_BUFFERS:
+            break;
+
+        case RESUBMIT_BUFFERS:
+            postFillThisBuffer(info);
+            break;
+
+        default:
+        {
+            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+            TRESPASS();  // Not currently used
+            break;
+        }
+    }
+
+    return true;
+}
+
+void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
+    if (mCodec->mPortEOS[kPortIndexInput]) {
+        return;
+    }
+
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatFillThisBuffer);
+    notify->setPointer("buffer-id", info->mBufferID);
+
+    info->mData->meta()->clear();
+    notify->setObject("buffer", info->mData);
+
+    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
+    reply->setPointer("buffer-id", info->mBufferID);
+
+    notify->setMessage("reply", reply);
+
+    notify->post();
+
+    info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
+}
+
+void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
+    IOMX::buffer_id bufferID;
+    CHECK(msg->findPointer("buffer-id", &bufferID));
+
+    sp<RefBase> obj;
+    int32_t err = OK;
+    if (!msg->findObject("buffer", &obj)) {
+        CHECK(msg->findInt32("err", &err));
+
+        obj.clear();
+    }
+
+    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+    BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID);
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_UPSTREAM);
+
+    info->mStatus = BufferInfo::OWNED_BY_US;
+
+    PortMode mode = getPortMode(kPortIndexInput);
+
+    switch (mode) {
+        case KEEP_BUFFERS:
+        {
+            if (buffer == NULL) {
+                mCodec->mPortEOS[kPortIndexInput] = true;
+            }
+            break;
+        }
+
+        case RESUBMIT_BUFFERS:
+        {
+            if (buffer != NULL) {
+                CHECK(!mCodec->mPortEOS[kPortIndexInput]);
+
+                int64_t timeUs;
+                CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+                OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
+
+                int32_t isCSD;
+                if (buffer->meta()->findInt32("csd", &isCSD) && isCSD != 0) {
+                    flags |= OMX_BUFFERFLAG_CODECCONFIG;
+                }
+
+                if (buffer != info->mData) {
+                    if (!(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
+                        LOGV("[%s] Needs to copy input data.",
+                             mCodec->mComponentName.c_str());
+                    }
+
+                    CHECK_LE(buffer->size(), info->mData->capacity());
+                    memcpy(info->mData->data(), buffer->data(), buffer->size());
+                }
+
+                CHECK_EQ(mCodec->mOMX->emptyBuffer(
+                            mCodec->mNode,
+                            bufferID,
+                            0,
+                            buffer->size(),
+                            flags,
+                            timeUs),
+                         (status_t)OK);
+
+                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+
+                getMoreInputDataIfPossible();
+            } else if (!mCodec->mPortEOS[kPortIndexInput]) {
+                LOGV("[%s] Signalling EOS on the input port",
+                     mCodec->mComponentName.c_str());
+
+                CHECK_EQ(mCodec->mOMX->emptyBuffer(
+                            mCodec->mNode,
+                            bufferID,
+                            0,
+                            0,
+                            OMX_BUFFERFLAG_EOS,
+                            0),
+                         (status_t)OK);
+
+                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+
+                mCodec->mPortEOS[kPortIndexInput] = true;
+            }
+            break;
+
+            default:
+                CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+                break;
+        }
+    }
+}
+
+void ACodec::BaseState::getMoreInputDataIfPossible() {
+    if (mCodec->mPortEOS[kPortIndexInput]) {
+        return;
+    }
+
+    BufferInfo *eligible = NULL;
+
+    for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); ++i) {
+        BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);
+
+#if 0
+        if (info->mStatus == BufferInfo::OWNED_BY_UPSTREAM) {
+            // There's already a "read" pending.
+            return;
+        }
+#endif
+
+        if (info->mStatus == BufferInfo::OWNED_BY_US) {
+            eligible = info;
+        }
+    }
+
+    if (eligible == NULL) {
+        return;
+    }
+
+    postFillThisBuffer(eligible);
+}
+
+bool ACodec::BaseState::onOMXFillBufferDone(
+        IOMX::buffer_id bufferID,
+        size_t rangeOffset, size_t rangeLength,
+        OMX_U32 flags,
+        int64_t timeUs,
+        void *platformPrivate,
+        void *dataPtr) {
+    ssize_t index;
+    BufferInfo *info =
+        mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
+
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
+
+    info->mStatus = BufferInfo::OWNED_BY_US;
+
+    PortMode mode = getPortMode(kPortIndexOutput);
+
+    switch (mode) {
+        case KEEP_BUFFERS:
+            break;
+
+        case RESUBMIT_BUFFERS:
+        {
+            if (rangeLength == 0) {
+                if (!(flags & OMX_BUFFERFLAG_EOS)) {
+                    CHECK_EQ(mCodec->mOMX->fillBuffer(
+                                mCodec->mNode, info->mBufferID),
+                             (status_t)OK);
+
+                    info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+                }
+            } else {
+                if (mCodec->mNativeWindow == NULL) {
+                    info->mData->setRange(rangeOffset, rangeLength);
+                }
+
+                info->mData->meta()->setInt64("timeUs", timeUs);
+
+                sp<AMessage> notify = mCodec->mNotify->dup();
+                notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+                notify->setPointer("buffer-id", info->mBufferID);
+                notify->setObject("buffer", info->mData);
+
+                sp<AMessage> reply =
+                    new AMessage(kWhatOutputBufferDrained, mCodec->id());
+
+                reply->setPointer("buffer-id", info->mBufferID);
+
+                notify->setMessage("reply", reply);
+
+                notify->post();
+
+                info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
+            }
+
+            if (flags & OMX_BUFFERFLAG_EOS) {
+                sp<AMessage> notify = mCodec->mNotify->dup();
+                notify->setInt32("what", ACodec::kWhatEOS);
+                notify->post();
+
+                mCodec->mPortEOS[kPortIndexOutput] = true;
+            }
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+
+            CHECK_EQ((status_t)OK,
+                     mCodec->freeBuffer(kPortIndexOutput, index));
+            break;
+        }
+    }
+
+    return true;
+}
+
+void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
+    IOMX::buffer_id bufferID;
+    CHECK(msg->findPointer("buffer-id", &bufferID));
+
+    ssize_t index;
+    BufferInfo *info =
+        mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM);
+
+    int32_t render;
+    if (mCodec->mNativeWindow != NULL
+            && msg->findInt32("render", &render) && render != 0) {
+        // The client wants this buffer to be rendered.
+
+        CHECK_EQ(mCodec->mNativeWindow->queueBuffer(
+                    mCodec->mNativeWindow.get(),
+                    info->mGraphicBuffer.get()),
+                 0);
+
+        info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
+    } else {
+        info->mStatus = BufferInfo::OWNED_BY_US;
+    }
+
+    PortMode mode = getPortMode(kPortIndexOutput);
+
+    switch (mode) {
+        case KEEP_BUFFERS:
+        {
+            // XXX fishy, revisit!!! What about the FREE_BUFFERS case below?
+
+            if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+                // We cannot resubmit the buffer we just rendered, dequeue
+                // the spare instead.
+
+                info = mCodec->dequeueBufferFromNativeWindow();
+            }
+            break;
+        }
+
+        case RESUBMIT_BUFFERS:
+        {
+            if (!mCodec->mPortEOS[kPortIndexOutput]) {
+                if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+                    // We cannot resubmit the buffer we just rendered, dequeue
+                    // the spare instead.
+
+                    info = mCodec->dequeueBufferFromNativeWindow();
+                }
+
+                CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
+                         (status_t)OK);
+
+                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+            }
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+
+            CHECK_EQ((status_t)OK,
+                     mCodec->freeBuffer(kPortIndexOutput, index));
+            break;
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::UninitializedState::UninitializedState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case ACodec::kWhatSetup:
+        {
+            onSetup(msg);
+
+            handled = true;
+            break;
+        }
+
+        case ACodec::kWhatShutdown:
+        {
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+            notify->post();
+
+            handled = true;
+        }
+
+        case ACodec::kWhatFlush:
+        {
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatFlushCompleted);
+            notify->post();
+
+            handled = true;
+        }
+
+        default:
+            return BaseState::onMessageReceived(msg);
+    }
+
+    return handled;
+}
+
+void ACodec::UninitializedState::onSetup(
+        const sp<AMessage> &msg) {
+    OMXClient client;
+    CHECK_EQ(client.connect(), (status_t)OK);
+
+    sp<IOMX> omx = client.interface();
+
+    AString mime;
+    CHECK(msg->findString("mime", &mime));
+
+    AString componentName;
+
+    if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
+        componentName = "OMX.Nvidia.h264.decode";
+    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+        componentName = "OMX.Nvidia.aac.decoder";
+    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) {
+        componentName = "OMX.Nvidia.mp3.decoder";
+    } else {
+        TRESPASS();
+    }
+
+    sp<CodecObserver> observer = new CodecObserver;
+
+    IOMX::node_id node;
+    CHECK_EQ(omx->allocateNode(componentName.c_str(), observer, &node),
+             (status_t)OK);
+
+    sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
+    observer->setNotificationMessage(notify);
+
+    mCodec->mComponentName = componentName;
+    mCodec->mOMX = omx;
+    mCodec->mNode = node;
+
+    mCodec->configureCodec(mime.c_str(), msg);
+
+    sp<RefBase> obj;
+    if (msg->findObject("surface", &obj)) {
+        mCodec->mNativeWindow = static_cast<Surface *>(obj.get());
+    }
+
+    CHECK_EQ((status_t)OK, mCodec->initNativeWindow());
+
+    CHECK_EQ(omx->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle),
+             (status_t)OK);
+
+    mCodec->changeState(mCodec->mLoadedToIdleState);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::LoadedToIdleState::LoadedToIdleState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+void ACodec::LoadedToIdleState::stateEntered() {
+    LOGI("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());
+
+    CHECK_EQ(allocateBuffers(), (status_t)OK);
+}
+
+status_t ACodec::LoadedToIdleState::allocateBuffers() {
+    status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
+
+    if (err != OK) {
+        return err;
+    }
+
+    return mCodec->allocateBuffersOnPort(kPortIndexOutput);
+}
+
+bool ACodec::LoadedToIdleState::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            mCodec->deferMessage(msg);
+            return true;
+        }
+
+        default:
+            return BaseState::onMessageReceived(msg);
+    }
+}
+
+bool ACodec::LoadedToIdleState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+            CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
+
+            CHECK_EQ(mCodec->mOMX->sendCommand(
+                        mCodec->mNode, OMX_CommandStateSet, OMX_StateExecuting),
+                     (status_t)OK);
+
+            mCodec->changeState(mCodec->mIdleToExecutingState);
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::IdleToExecutingState::IdleToExecutingState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+void ACodec::IdleToExecutingState::stateEntered() {
+    LOGI("[%s] Now Idle->Executing", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::IdleToExecutingState::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            mCodec->deferMessage(msg);
+            return true;
+        }
+
+        default:
+            return BaseState::onMessageReceived(msg);
+    }
+}
+
+bool ACodec::IdleToExecutingState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+            CHECK_EQ(data2, (OMX_U32)OMX_StateExecuting);
+
+            mCodec->mExecutingState->resume();
+            mCodec->changeState(mCodec->mExecutingState);
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ExecutingState::ExecutingState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode(
+        OMX_U32 portIndex) {
+    return RESUBMIT_BUFFERS;
+}
+
+void ACodec::ExecutingState::submitOutputBuffers() {
+    for (size_t i = 0; i < mCodec->mBuffers[kPortIndexOutput].size(); ++i) {
+        BufferInfo *info = &mCodec->mBuffers[kPortIndexOutput].editItemAt(i);
+
+        if (mCodec->mNativeWindow != NULL) {
+            CHECK(info->mStatus == BufferInfo::OWNED_BY_US
+                    || info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+            if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+                continue;
+            }
+
+            status_t err = mCodec->mNativeWindow->lockBuffer(
+                    mCodec->mNativeWindow.get(),
+                    info->mGraphicBuffer.get());
+            CHECK_EQ(err, (status_t)OK);
+        } else {
+            CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+        }
+
+        CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
+                 (status_t)OK);
+
+        info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+    }
+}
+
+void ACodec::ExecutingState::resume() {
+    submitOutputBuffers();
+
+    // Post the first input buffer.
+    CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u);
+    BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0);
+
+    postFillThisBuffer(info);
+}
+
+void ACodec::ExecutingState::stateEntered() {
+    LOGI("[%s] Now Executing", mCodec->mComponentName.c_str());
+
+    mCodec->processDeferredMessages();
+}
+
+bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            CHECK_EQ(mCodec->mOMX->sendCommand(
+                        mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
+                     (status_t)OK);
+
+            mCodec->changeState(mCodec->mExecutingToIdleState);
+
+            handled = true;
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            CHECK_EQ(mCodec->mOMX->sendCommand(
+                        mCodec->mNode, OMX_CommandFlush, OMX_ALL),
+                     (status_t)OK);
+
+            mCodec->changeState(mCodec->mFlushingState);
+
+            handled = true;
+            break;
+        }
+
+        case kWhatResume:
+        {
+            resume();
+
+            handled = true;
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+bool ACodec::ExecutingState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventPortSettingsChanged:
+        {
+            CHECK_EQ(data1, (OMX_U32)kPortIndexOutput);
+
+            if (data2 == OMX_IndexParamPortDefinition) {
+                CHECK_EQ(mCodec->mOMX->sendCommand(
+                            mCodec->mNode,
+                            OMX_CommandPortDisable, kPortIndexOutput),
+                         (status_t)OK);
+
+                if (mCodec->mNativeWindow != NULL) {
+                    CHECK_EQ((status_t)OK,
+                             mCodec->freeOutputBuffersOwnedByNativeWindow());
+                }
+
+                mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
+            } else {
+                LOGV("[%s] OMX_EventPortSettingsChanged 0x%08lx",
+                     mCodec->mComponentName.c_str(), data2);
+            }
+
+            return true;
+        }
+
+        case OMX_EventBufferFlag:
+        {
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::OutputPortSettingsChangedState::OutputPortSettingsChangedState(
+        ACodec *codec)
+    : BaseState(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::OutputPortSettingsChangedState::getPortMode(
+        OMX_U32 portIndex) {
+    if (portIndex == kPortIndexOutput) {
+        return FREE_BUFFERS;
+    }
+
+    CHECK_EQ(portIndex, (OMX_U32)kPortIndexInput);
+
+    return RESUBMIT_BUFFERS;
+}
+
+bool ACodec::OutputPortSettingsChangedState::onMessageReceived(
+        const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatFlush:
+        case kWhatShutdown:
+        {
+            mCodec->deferMessage(msg);
+            handled = true;
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+void ACodec::OutputPortSettingsChangedState::stateEntered() {
+    LOGI("[%s] Now handling output port settings change",
+         mCodec->mComponentName.c_str());
+}
+
+bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            if (data1 == (OMX_U32)OMX_CommandPortDisable) {
+                CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+
+                LOGV("[%s] Output port now disabled.",
+                        mCodec->mComponentName.c_str());
+
+                CHECK(mCodec->mBuffers[kPortIndexOutput].isEmpty());
+                mCodec->mDealer[kPortIndexOutput].clear();
+
+                CHECK_EQ(mCodec->mOMX->sendCommand(
+                            mCodec->mNode, OMX_CommandPortEnable, kPortIndexOutput),
+                         (status_t)OK);
+
+                CHECK_EQ(mCodec->allocateBuffersOnPort(kPortIndexOutput),
+                         (status_t)OK);
+
+                return true;
+            } else if (data1 == (OMX_U32)OMX_CommandPortEnable) {
+                CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+
+                LOGV("[%s] Output port now reenabled.",
+                        mCodec->mComponentName.c_str());
+
+                mCodec->mExecutingState->submitOutputBuffers();
+                mCodec->changeState(mCodec->mExecutingState);
+
+                return true;
+            }
+
+            return false;
+        }
+
+        default:
+            return false;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatFlush:
+        {
+            // Don't send me a flush request if you previously wanted me
+            // to shutdown.
+            TRESPASS();
+            break;
+        }
+
+        case kWhatShutdown:
+        {
+            // We're already doing that...
+
+            handled = true;
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+void ACodec::ExecutingToIdleState::stateEntered() {
+    LOGI("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::ExecutingToIdleState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+            CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
+
+            changeStateIfWeOwnAllBuffers();
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
+    if (mCodec->allYourBuffersAreBelongToUs()) {
+        CHECK_EQ(mCodec->mOMX->sendCommand(
+                    mCodec->mNode, OMX_CommandStateSet, OMX_StateLoaded),
+                 (status_t)OK);
+
+        CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexInput), (status_t)OK);
+        CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexOutput), (status_t)OK);
+
+        mCodec->changeState(mCodec->mIdleToLoadedState);
+    }
+}
+
+void ACodec::ExecutingToIdleState::onInputBufferFilled(
+        const sp<AMessage> &msg) {
+    BaseState::onInputBufferFilled(msg);
+
+    changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::ExecutingToIdleState::onOutputBufferDrained(
+        const sp<AMessage> &msg) {
+    BaseState::onOutputBufferDrained(msg);
+
+    changeStateIfWeOwnAllBuffers();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::IdleToLoadedState::IdleToLoadedState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+bool ACodec::IdleToLoadedState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            // We're already doing that...
+
+            handled = true;
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            // Don't send me a flush request if you previously wanted me
+            // to shutdown.
+            TRESPASS();
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+void ACodec::IdleToLoadedState::stateEntered() {
+    LOGI("[%s] Now Idle->Loaded", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::IdleToLoadedState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+            CHECK_EQ(data2, (OMX_U32)OMX_StateLoaded);
+
+            LOGI("[%s] Now Loaded", mCodec->mComponentName.c_str());
+
+            CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
+
+            mCodec->mNativeWindow.clear();
+            mCodec->mNode = NULL;
+            mCodec->mOMX.clear();
+            mCodec->mComponentName.clear();
+
+            mCodec->changeState(mCodec->mUninitializedState);
+
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+            notify->post();
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ErrorState::ErrorState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+bool ACodec::ErrorState::onMessageReceived(const sp<AMessage> &msg) {
+    return BaseState::onMessageReceived(msg);
+}
+
+void ACodec::ErrorState::stateEntered() {
+    LOGI("[%s] Now in ErrorState", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::ErrorState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    LOGI("EVENT(%d, 0x%08lx, 0x%08lx)", event, data1, data2);
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::FlushingState::FlushingState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+void ACodec::FlushingState::stateEntered() {
+    LOGI("[%s] Now Flushing", mCodec->mComponentName.c_str());
+
+    mFlushComplete[kPortIndexInput] = mFlushComplete[kPortIndexOutput] = false;
+}
+
+bool ACodec::FlushingState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            mCodec->deferMessage(msg);
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            // We're already doing this right now.
+            handled = true;
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+bool ACodec::FlushingState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandFlush);
+
+            if (data2 == kPortIndexInput || data2 == kPortIndexOutput) {
+                CHECK(!mFlushComplete[data2]);
+                mFlushComplete[data2] = true;
+            } else {
+                CHECK_EQ(data2, OMX_ALL);
+                CHECK(mFlushComplete[kPortIndexInput]);
+                CHECK(mFlushComplete[kPortIndexOutput]);
+
+                changeStateIfWeOwnAllBuffers();
+            }
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+
+    return true;
+}
+
+void ACodec::FlushingState::onOutputBufferDrained(const sp<AMessage> &msg) {
+    BaseState::onOutputBufferDrained(msg);
+
+    changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::FlushingState::onInputBufferFilled(const sp<AMessage> &msg) {
+    BaseState::onInputBufferFilled(msg);
+
+    changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::FlushingState::changeStateIfWeOwnAllBuffers() {
+    if (mFlushComplete[kPortIndexInput]
+            && mFlushComplete[kPortIndexOutput]
+            && mCodec->allYourBuffersAreBelongToUs()) {
+        sp<AMessage> notify = mCodec->mNotify->dup();
+        notify->setInt32("what", ACodec::kWhatFlushCompleted);
+        notify->post();
+
+        mCodec->mPortEOS[kPortIndexInput] =
+            mCodec->mPortEOS[kPortIndexOutput] = false;
+
+        mCodec->changeState(mCodec->mExecutingState);
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index db23836..2d486e3 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -4,6 +4,7 @@
 include frameworks/base/media/libstagefright/codecs/common/Config.mk
 
 LOCAL_SRC_FILES:=                         \
+        ACodec.cpp                        \
         AMRExtractor.cpp                  \
         AMRWriter.cpp                     \
         AudioPlayer.cpp                   \
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 4d8165e..4e4f289 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -42,12 +42,21 @@
     unsigned streamType() const;
     unsigned incrementContinuityCounter();
 
+    void readMore();
+
     enum {
         kNotifyStartFailed,
         kNotifyBuffer,
         kNotifyReachedEOS,
     };
 
+    sp<ABuffer> lastAccessUnit();
+    int64_t lastAccessUnitTimeUs();
+    void setLastAccessUnit(const sp<ABuffer> &accessUnit);
+
+    void setEOSReceived();
+    bool eosReceived() const;
+
 protected:
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
@@ -67,13 +76,16 @@
 
     sp<ABuffer> mAACBuffer;
 
+    sp<ABuffer> mLastAccessUnit;
+    bool mEOSReceived;
+
     unsigned mStreamType;
     unsigned mContinuityCounter;
 
     void extractCodecSpecificData();
 
-    void appendAACFrames(MediaBuffer *buffer);
-    void flushAACFrames();
+    bool appendAACFrames(MediaBuffer *buffer);
+    bool flushAACFrames();
 
     void postAVCFrame(MediaBuffer *buffer);
 
@@ -83,6 +95,7 @@
 MPEG2TSWriter::SourceInfo::SourceInfo(const sp<MediaSource> &source)
     : mSource(source),
       mLooper(new ALooper),
+      mEOSReceived(false),
       mStreamType(0),
       mContinuityCounter(0) {
     mLooper->setName("MPEG2TSWriter source");
@@ -232,6 +245,7 @@
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kNotifyBuffer);
     notify->setObject("buffer", out);
+    notify->setInt32("oob", true);
     notify->post();
 }
 
@@ -260,11 +274,13 @@
     notify->post();
 }
 
-void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) {
+bool MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) {
+    bool accessUnitPosted = false;
+
     if (mAACBuffer != NULL
             && mAACBuffer->size() + 7 + buffer->range_length()
                     > mAACBuffer->capacity()) {
-        flushAACFrames();
+        accessUnitPosted = flushAACFrames();
     }
 
     if (mAACBuffer == NULL) {
@@ -324,11 +340,13 @@
     ptr += buffer->range_length();
 
     mAACBuffer->setRange(0, ptr - mAACBuffer->data());
+
+    return accessUnitPosted;
 }
 
-void MPEG2TSWriter::SourceInfo::flushAACFrames() {
+bool MPEG2TSWriter::SourceInfo::flushAACFrames() {
     if (mAACBuffer == NULL) {
-        return;
+        return false;
     }
 
     sp<AMessage> notify = mNotify->dup();
@@ -337,6 +355,12 @@
     notify->post();
 
     mAACBuffer.clear();
+
+    return true;
+}
+
+void MPEG2TSWriter::SourceInfo::readMore() {
+    (new AMessage(kWhatRead, id()))->post();
 }
 
 void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) {
@@ -353,7 +377,7 @@
 
             extractCodecSpecificData();
 
-            (new AMessage(kWhatRead, id()))->post();
+            readMore();
             break;
         }
 
@@ -388,7 +412,9 @@
                            buffer->range_length());
                 } else if (buffer->range_length() > 0) {
                     if (mStreamType == 0x0f) {
-                        appendAACFrames(buffer);
+                        if (!appendAACFrames(buffer)) {
+                            msg->post();
+                        }
                     } else {
                         postAVCFrame(buffer);
                     }
@@ -398,7 +424,7 @@
                 buffer = NULL;
             }
 
-            msg->post();
+            // Do not read more data until told to.
             break;
         }
 
@@ -407,6 +433,35 @@
     }
 }
 
+sp<ABuffer> MPEG2TSWriter::SourceInfo::lastAccessUnit() {
+    return mLastAccessUnit;
+}
+
+void MPEG2TSWriter::SourceInfo::setLastAccessUnit(
+        const sp<ABuffer> &accessUnit) {
+    mLastAccessUnit = accessUnit;
+}
+
+int64_t MPEG2TSWriter::SourceInfo::lastAccessUnitTimeUs() {
+    if (mLastAccessUnit == NULL) {
+        return -1;
+    }
+
+    int64_t timeUs;
+    CHECK(mLastAccessUnit->meta()->findInt64("timeUs", &timeUs));
+
+    return timeUs;
+}
+
+void MPEG2TSWriter::SourceInfo::setEOSReceived() {
+    CHECK(!mEOSReceived);
+    mEOSReceived = true;
+}
+
+bool MPEG2TSWriter::SourceInfo::eosReceived() const {
+    return mEOSReceived;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 MPEG2TSWriter::MPEG2TSWriter(int fd)
@@ -527,15 +582,89 @@
 
             if (what == SourceInfo::kNotifyReachedEOS
                     || what == SourceInfo::kNotifyStartFailed) {
+                sp<SourceInfo> source = mSources.editItemAt(sourceIndex);
+                source->setEOSReceived();
+
+                sp<ABuffer> buffer = source->lastAccessUnit();
+                source->setLastAccessUnit(NULL);
+
+                if (buffer != NULL) {
+                    writeTS();
+                    writeAccessUnit(sourceIndex, buffer);
+                }
+
                 ++mNumSourcesDone;
             } else if (what == SourceInfo::kNotifyBuffer) {
                 sp<RefBase> obj;
                 CHECK(msg->findObject("buffer", &obj));
 
-                writeTS();
-
                 sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
-                writeAccessUnit(sourceIndex, buffer);
+
+                int32_t oob;
+                if (msg->findInt32("oob", &oob) && oob) {
+                    // This is codec specific data delivered out of band.
+                    // It can be written out immediately.
+                    writeTS();
+                    writeAccessUnit(sourceIndex, buffer);
+                    break;
+                }
+
+                // We don't just write out data as we receive it from
+                // the various sources. That would essentially write them
+                // out in random order (as the thread scheduler determines
+                // how the messages are dispatched).
+                // Instead we gather an access unit for all tracks and
+                // write out the one with the smallest timestamp, then
+                // request more data for the written out track.
+                // Rinse, repeat.
+                // If we don't have data on any track we don't write
+                // anything just yet.
+
+                sp<SourceInfo> source = mSources.editItemAt(sourceIndex);
+
+                CHECK(source->lastAccessUnit() == NULL);
+                source->setLastAccessUnit(buffer);
+
+                LOGV("lastAccessUnitTimeUs[%d] = %.2f secs",
+                     sourceIndex, source->lastAccessUnitTimeUs() / 1E6);
+
+                int64_t minTimeUs = -1;
+                size_t minIndex = 0;
+
+                for (size_t i = 0; i < mSources.size(); ++i) {
+                    const sp<SourceInfo> &source = mSources.editItemAt(i);
+
+                    if (source->eosReceived()) {
+                        continue;
+                    }
+
+                    int64_t timeUs = source->lastAccessUnitTimeUs();
+                    if (timeUs < 0) {
+                        minTimeUs = -1;
+                        break;
+                    } else if (minTimeUs < 0 || timeUs < minTimeUs) {
+                        minTimeUs = timeUs;
+                        minIndex = i;
+                    }
+                }
+
+                if (minTimeUs < 0) {
+                    LOGV("not a all tracks have valid data.");
+                    break;
+                }
+
+                LOGV("writing access unit at time %.2f secs (index %d)",
+                     minTimeUs / 1E6, minIndex);
+
+                source = mSources.editItemAt(minIndex);
+
+                buffer = source->lastAccessUnit();
+                source->setLastAccessUnit(NULL);
+
+                writeTS();
+                writeAccessUnit(minIndex, buffer);
+
+                source->readMore();
             }
             break;
         }
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index f58c16d..208431c 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -21,8 +21,8 @@
 
 #include "pvmp4audiodecoder_api.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 
@@ -84,7 +84,7 @@
     sp<MetaData> meta = mSource->getFormat();
     if (meta->findData(kKeyESDS, &type, &data, &size)) {
         ESDS esds((const char *)data, size);
-        CHECK_EQ(esds.InitCheck(), OK);
+        CHECK_EQ(esds.InitCheck(), (status_t)OK);
 
         const void *codec_specific_data;
         size_t codec_specific_data_size;
@@ -197,7 +197,7 @@
     }
 
     MediaBuffer *buffer;
-    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK);
 
     mConfig->pInputBuffer =
         (UChar *)mInputBuffer->data() + mInputBuffer->range_offset();
@@ -308,7 +308,7 @@
             mAnchorTimeUs
                 + (mNumSamplesOutput * 1000000) / mConfig->samplingRate);
 
-    mNumSamplesOutput += mConfig->frameLength;
+    mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor;
 
     *out = buffer;
 
diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
new file mode 100644
index 0000000..30286d8
--- /dev/null
+++ b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
@@ -0,0 +1,97 @@
+#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+AState::AState(const sp<AState> &parentState)
+    : mParentState(parentState) {
+}
+
+AState::~AState() {
+}
+
+sp<AState> AState::parentState() {
+    return mParentState;
+}
+
+void AState::stateEntered() {
+}
+
+void AState::stateExited() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AHierarchicalStateMachine::AHierarchicalStateMachine() {
+}
+
+AHierarchicalStateMachine::~AHierarchicalStateMachine() {
+}
+
+void AHierarchicalStateMachine::onMessageReceived(const sp<AMessage> &msg) {
+    sp<AState> save = mState;
+
+    sp<AState> cur = mState;
+    while (cur != NULL && !cur->onMessageReceived(msg)) {
+        // If you claim not to have handled the message you shouldn't
+        // have called setState...
+        CHECK(save == mState);
+
+        cur = cur->parentState();
+    }
+
+    if (cur != NULL) {
+        return;
+    }
+
+    LOGW("Warning message %s unhandled in root state.",
+         msg->debugString().c_str());
+}
+
+void AHierarchicalStateMachine::changeState(const sp<AState> &state) {
+    if (state == mState) {
+        // Quick exit for the easy case.
+        return;
+    }
+
+    Vector<sp<AState> > A;
+    sp<AState> cur = mState;
+    for (;;) {
+        A.push(cur);
+        if (cur == NULL) {
+            break;
+        }
+        cur = cur->parentState();
+    }
+
+    Vector<sp<AState> > B;
+    cur = state;
+    for (;;) {
+        B.push(cur);
+        if (cur == NULL) {
+            break;
+        }
+        cur = cur->parentState();
+    }
+
+    // Remove the common tail.
+    while (A.size() > 0 && B.size() > 0 && A.top() == B.top()) {
+        A.pop();
+        B.pop();
+    }
+
+    mState = state;
+
+    for (size_t i = 0; i < A.size(); ++i) {
+        A.editItemAt(i)->stateExited();
+    }
+
+    for (size_t i = B.size(); i-- > 0;) {
+        B.editItemAt(i)->stateEntered();
+    }
+}
+
+}  // namespace android
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index a4d4809..4e07f6f 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -1,16 +1,17 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:=               \
-    AAtomizer.cpp               \
-    ABitReader.cpp              \
-    ABuffer.cpp                 \
-    AHandler.cpp                \
-    ALooper.cpp                 \
-    ALooperRoster.cpp           \
-    AMessage.cpp                \
-    AString.cpp                 \
-    base64.cpp                  \
+LOCAL_SRC_FILES:=                 \
+    AAtomizer.cpp                 \
+    ABitReader.cpp                \
+    ABuffer.cpp                   \
+    AHandler.cpp                  \
+    AHierarchicalStateMachine.cpp \
+    ALooper.cpp                   \
+    ALooperRoster.cpp             \
+    AMessage.cpp                  \
+    AString.cpp                   \
+    base64.cpp                    \
     hexdump.cpp
 
 LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index a559b21d..de6346b 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -43,19 +43,21 @@
 static const size_t kTSPacketSize = 188;
 
 struct ATSParser::Program : public RefBase {
-    Program(unsigned programMapPID);
+    Program(ATSParser *parser, unsigned programMapPID);
 
     bool parsePID(
             unsigned pid, unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity(bool isASeek);
+    void signalDiscontinuity(DiscontinuityType type);
+    void signalEOS(status_t finalResult);
 
     sp<MediaSource> getSource(SourceType type);
 
     int64_t convertPTSToTimestamp(uint64_t PTS);
 
 private:
+    ATSParser *mParser;
     unsigned mProgramMapPID;
     KeyedVector<unsigned, sp<Stream> > mStreams;
     bool mFirstPTSValid;
@@ -73,7 +75,8 @@
             unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity(bool isASeek);
+    void signalDiscontinuity(DiscontinuityType type);
+    void signalEOS(status_t finalResult);
 
     sp<MediaSource> getSource(SourceType type);
 
@@ -105,8 +108,9 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-ATSParser::Program::Program(unsigned programMapPID)
-    : mProgramMapPID(programMapPID),
+ATSParser::Program::Program(ATSParser *parser, unsigned programMapPID)
+    : mParser(parser),
+      mProgramMapPID(programMapPID),
       mFirstPTSValid(false),
       mFirstPTS(0) {
 }
@@ -135,9 +139,15 @@
     return true;
 }
 
-void ATSParser::Program::signalDiscontinuity(bool isASeek) {
+void ATSParser::Program::signalDiscontinuity(DiscontinuityType type) {
     for (size_t i = 0; i < mStreams.size(); ++i) {
-        mStreams.editValueAt(i)->signalDiscontinuity(isASeek);
+        mStreams.editValueAt(i)->signalDiscontinuity(type);
+    }
+}
+
+void ATSParser::Program::signalEOS(status_t finalResult) {
+    for (size_t i = 0; i < mStreams.size(); ++i) {
+        mStreams.editValueAt(i)->signalEOS(finalResult);
     }
 }
 
@@ -155,7 +165,7 @@
 
     unsigned section_length = br->getBits(12);
     LOGV("  section_length = %u", section_length);
-    CHECK((section_length & 0xc00) == 0);
+    CHECK_EQ(section_length & 0xc00, 0u);
     CHECK_LE(section_length, 1021u);
 
     MY_LOGV("  program_number = %u", br->getBits(16));
@@ -170,7 +180,7 @@
 
     unsigned program_info_length = br->getBits(12);
     LOGV("  program_info_length = %u", program_info_length);
-    CHECK((program_info_length & 0xc00) == 0);
+    CHECK_EQ(program_info_length & 0xc00, 0u);
 
     br->skipBits(program_info_length * 8);  // skip descriptors
 
@@ -194,7 +204,7 @@
 
         unsigned ES_info_length = br->getBits(12);
         LOGV("    ES_info_length = %u", ES_info_length);
-        CHECK((ES_info_length & 0xc00) == 0);
+        CHECK_EQ(ES_info_length & 0xc00, 0u);
 
         CHECK_GE(infoBytesRemaining - 5, ES_info_length);
 
@@ -305,7 +315,7 @@
     }
 
     size_t payloadSizeBits = br->numBitsLeft();
-    CHECK((payloadSizeBits % 8) == 0);
+    CHECK_EQ(payloadSizeBits % 8, 0u);
 
     CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
 
@@ -313,27 +323,45 @@
     mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
 }
 
-void ATSParser::Stream::signalDiscontinuity(bool isASeek) {
-    isASeek = false;  // Always signal a "real" discontinuity
-
+void ATSParser::Stream::signalDiscontinuity(DiscontinuityType type) {
     mPayloadStarted = false;
     mBuffer->setRange(0, 0);
 
-    mQueue.clear();
+    switch (type) {
+        case DISCONTINUITY_HTTPLIVE:
+        {
+            mQueue.clear(true);
 
-    if (isASeek) {
-        // This is only a "minor" discontinuity, we stay within the same
-        // bitstream.
-
-        if (mSource != NULL) {
-            mSource->clear();
+            if (mStreamType == 0x1b && mSource != NULL) {
+                // Don't signal discontinuities on audio streams.
+                mSource->queueDiscontinuity();
+            }
+            break;
         }
-        return;
-    }
 
-    if (mStreamType == 0x1b && mSource != NULL) {
-        // Don't signal discontinuities on audio streams.
-        mSource->queueDiscontinuity();
+        case DISCONTINUITY_SEEK:
+        case DISCONTINUITY_FORMATCHANGE:
+        {
+            bool isASeek = (type == DISCONTINUITY_SEEK);
+
+            mQueue.clear(!isASeek);
+
+            if (mSource != NULL) {
+                mSource->clear();
+                mSource->queueDiscontinuity();
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void ATSParser::Stream::signalEOS(status_t finalResult) {
+    if (mSource != NULL) {
+        mSource->signalEOS(finalResult);
     }
 }
 
@@ -478,7 +506,7 @@
                     br->data(), br->numBitsLeft() / 8);
 
             size_t payloadSizeBits = br->numBitsLeft();
-            CHECK((payloadSizeBits % 8) == 0);
+            CHECK_EQ(payloadSizeBits % 8, 0u);
 
             LOGV("There's %d bytes of payload.", payloadSizeBits / 8);
         }
@@ -526,6 +554,7 @@
             if (meta != NULL) {
                 LOGV("created source!");
                 mSource = new AnotherPacketSource(meta);
+
                 mSource->queueAccessUnit(accessUnit);
             }
         } else if (mQueue.getFormat() != NULL) {
@@ -561,9 +590,17 @@
     parseTS(&br);
 }
 
-void ATSParser::signalDiscontinuity(bool isASeek) {
+void ATSParser::signalDiscontinuity(DiscontinuityType type) {
     for (size_t i = 0; i < mPrograms.size(); ++i) {
-        mPrograms.editItemAt(i)->signalDiscontinuity(isASeek);
+        mPrograms.editItemAt(i)->signalDiscontinuity(type);
+    }
+}
+
+void ATSParser::signalEOS(status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    for (size_t i = 0; i < mPrograms.size(); ++i) {
+        mPrograms.editItemAt(i)->signalEOS(finalResult);
     }
 }
 
@@ -581,7 +618,7 @@
 
     unsigned section_length = br->getBits(12);
     LOGV("  section_length = %u", section_length);
-    CHECK((section_length & 0xc00) == 0);
+    CHECK_EQ(section_length & 0xc00, 0u);
 
     MY_LOGV("  transport_stream_id = %u", br->getBits(16));
     MY_LOGV("  reserved = %u", br->getBits(2));
@@ -606,7 +643,7 @@
 
             LOGV("    program_map_PID = 0x%04x", programMapPID);
 
-            mPrograms.push(new Program(programMapPID));
+            mPrograms.push(new Program(this, programMapPID));
         }
     }
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 11b1de4..ef78c77 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -21,19 +21,28 @@
 #include <sys/types.h>
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AMessage.h>
 #include <utils/Vector.h>
 #include <utils/RefBase.h>
 
 namespace android {
 
 struct ABitReader;
+struct ABuffer;
 struct MediaSource;
 
 struct ATSParser : public RefBase {
+    enum DiscontinuityType {
+        DISCONTINUITY_HTTPLIVE,
+        DISCONTINUITY_SEEK,
+        DISCONTINUITY_FORMATCHANGE
+    };
+
     ATSParser();
 
     void feedTSPacket(const void *data, size_t size);
-    void signalDiscontinuity(bool isASeek = false);
+    void signalDiscontinuity(DiscontinuityType type);
+    void signalEOS(status_t finalResult);
 
     enum SourceType {
         AVC_VIDEO,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index ea747c8..7a1d5b0 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -48,6 +48,32 @@
     return mFormat;
 }
 
+status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) {
+    buffer->clear();
+
+    Mutex::Autolock autoLock(mLock);
+    while (mEOSResult == OK && mBuffers.empty()) {
+        mCondition.wait(mLock);
+    }
+
+    if (!mBuffers.empty()) {
+        *buffer = *mBuffers.begin();
+        mBuffers.erase(mBuffers.begin());
+
+        int32_t discontinuity;
+        if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)
+                && discontinuity) {
+            buffer->clear();
+
+            return INFO_DISCONTINUITY;
+        }
+
+        return OK;
+    }
+
+    return mEOSResult;
+}
+
 status_t AnotherPacketSource::read(
         MediaBuffer **out, const ReadOptions *) {
     *out = NULL;
@@ -66,9 +92,8 @@
                 && discontinuity) {
             return INFO_DISCONTINUITY;
         } else {
-            uint64_t timeUs;
-            CHECK(buffer->meta()->findInt64(
-                        "time", (int64_t *)&timeUs));
+            int64_t timeUs;
+            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
             MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
             mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
@@ -92,7 +117,7 @@
     }
 
     int64_t timeUs;
-    CHECK(buffer->meta()->findInt64("time", &timeUs));
+    CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
     LOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", timeUs, timeUs / 1E6);
 
     Mutex::Autolock autoLock(mLock);
@@ -134,4 +159,19 @@
     return false;
 }
 
+status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
+    *timeUs = 0;
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (mBuffers.empty()) {
+        return mEOSResult != OK ? mEOSResult : -EWOULDBLOCK;
+    }
+
+    sp<ABuffer> buffer = *mBuffers.begin();
+    CHECK(buffer->meta()->findInt64("timeUs", timeUs));
+
+    return OK;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 6999175..2bc7404 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -39,12 +39,16 @@
 
     bool hasBufferAvailable(status_t *finalResult);
 
+    status_t nextBufferTime(int64_t *timeUs);
+
     void queueAccessUnit(const sp<ABuffer> &buffer);
     void queueDiscontinuity();
     void signalEOS(status_t result);
 
     void clear();
 
+    status_t dequeueAccessUnit(sp<ABuffer> *buffer);
+
 protected:
     virtual ~AnotherPacketSource();
 
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 1fb7c39..4e7759d 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -40,13 +40,16 @@
     return mFormat;
 }
 
-void ElementaryStreamQueue::clear() {
+void ElementaryStreamQueue::clear(bool clearFormat) {
     if (mBuffer != NULL) {
         mBuffer->setRange(0, 0);
     }
 
-    mTimestamps.clear();
-    mFormat.clear();
+    mRangeInfos.clear();
+
+    if (clearFormat) {
+        mFormat.clear();
+    }
 }
 
 static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
@@ -171,7 +174,17 @@
     memcpy(mBuffer->data() + mBuffer->size(), data, size);
     mBuffer->setRange(0, mBuffer->size() + size);
 
-    mTimestamps.push_back(timeUs);
+    RangeInfo info;
+    info.mLength = size;
+    info.mTimestampUs = timeUs;
+    mRangeInfos.push_back(info);
+
+#if 0
+    if (mMode == AAC) {
+        LOGI("size = %d, timeUs = %.2f secs", size, timeUs / 1E6);
+        hexdump(data, size);
+    }
+#endif
 
     return OK;
 }
@@ -186,6 +199,7 @@
 }
 
 sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
+    Vector<size_t> ranges;
     Vector<size_t> frameOffsets;
     Vector<size_t> frameSizes;
     size_t auSize = 0;
@@ -239,6 +253,7 @@
 
         size_t headerSize = protection_absent ? 7 : 9;
 
+        ranges.push(aac_frame_length);
         frameOffsets.push(offset + headerSize);
         frameSizes.push(aac_frame_length - headerSize);
         auSize += aac_frame_length - headerSize;
@@ -250,11 +265,23 @@
         return NULL;
     }
 
+    int64_t timeUs = -1;
+
+    for (size_t i = 0; i < ranges.size(); ++i) {
+        int64_t tmpUs = fetchTimestamp(ranges.itemAt(i));
+
+        if (i == 0) {
+            timeUs = tmpUs;
+        }
+    }
+
     sp<ABuffer> accessUnit = new ABuffer(auSize);
     size_t dstOffset = 0;
     for (size_t i = 0; i < frameOffsets.size(); ++i) {
+        size_t frameOffset = frameOffsets.itemAt(i);
+
         memcpy(accessUnit->data() + dstOffset,
-               mBuffer->data() + frameOffsets.itemAt(i),
+               mBuffer->data() + frameOffset,
                frameSizes.itemAt(i));
 
         dstOffset += frameSizes.itemAt(i);
@@ -264,15 +291,48 @@
             mBuffer->size() - offset);
     mBuffer->setRange(0, mBuffer->size() - offset);
 
-    CHECK_GT(mTimestamps.size(), 0u);
-    int64_t timeUs = *mTimestamps.begin();
-    mTimestamps.erase(mTimestamps.begin());
-
-    accessUnit->meta()->setInt64("time", timeUs);
+    if (timeUs >= 0) {
+        accessUnit->meta()->setInt64("timeUs", timeUs);
+    } else {
+        LOGW("no time for AAC access unit");
+    }
 
     return accessUnit;
 }
 
+int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) {
+    int64_t timeUs = -1;
+    bool first = true;
+
+    while (size > 0) {
+        CHECK(!mRangeInfos.empty());
+
+        RangeInfo *info = &*mRangeInfos.begin();
+
+        if (first) {
+            timeUs = info->mTimestampUs;
+            first = false;
+        }
+
+        if (info->mLength > size) {
+            info->mLength -= size;
+
+            if (first) {
+                info->mTimestampUs = -1;
+            }
+
+            size = 0;
+        } else {
+            size -= info->mLength;
+
+            mRangeInfos.erase(mRangeInfos.begin());
+            info = NULL;
+        }
+    }
+
+    return timeUs;
+}
+
 // static
 sp<MetaData> ElementaryStreamQueue::MakeAACCodecSpecificData(
         unsigned profile, unsigned sampling_freq_index,
@@ -410,11 +470,10 @@
 
             mBuffer->setRange(0, mBuffer->size() - nextScan);
 
-            CHECK_GT(mTimestamps.size(), 0u);
-            int64_t timeUs = *mTimestamps.begin();
-            mTimestamps.erase(mTimestamps.begin());
+            int64_t timeUs = fetchTimestamp(nextScan);
+            CHECK_GE(timeUs, 0ll);
 
-            accessUnit->meta()->setInt64("time", timeUs);
+            accessUnit->meta()->setInt64("timeUs", timeUs);
 
             if (mFormat == NULL) {
                 mFormat = MakeAVCCodecSpecificData(accessUnit);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 9eaf834..5b7957e 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -35,23 +35,32 @@
     ElementaryStreamQueue(Mode mode);
 
     status_t appendData(const void *data, size_t size, int64_t timeUs);
-    void clear();
+    void clear(bool clearFormat);
 
     sp<ABuffer> dequeueAccessUnit();
 
     sp<MetaData> getFormat();
 
 private:
+    struct RangeInfo {
+        int64_t mTimestampUs;
+        size_t mLength;
+    };
+
     Mode mMode;
 
     sp<ABuffer> mBuffer;
-    List<int64_t> mTimestamps;
+    List<RangeInfo> mRangeInfos;
 
     sp<MetaData> mFormat;
 
     sp<ABuffer> dequeueAccessUnitH264();
     sp<ABuffer> dequeueAccessUnitAAC();
 
+    // consume a logical (compressed) access unit of size "size",
+    // returns its timestamp in us (or -1 if no time information).
+    int64_t fetchTimestamp(size_t size);
+
     static sp<MetaData> MakeAACCodecSpecificData(
             unsigned profile, unsigned sampling_freq_index,
             unsigned channel_configuration);
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 600116e..a1f0796 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -214,7 +214,7 @@
 
     if (isDiscontinuity(packet, n)) {
         LOGI("XXX discontinuity detected");
-        mParser->signalDiscontinuity();
+        mParser->signalDiscontinuity(ATSParser::DISCONTINUITY_HTTPLIVE);
     } else if (n < (ssize_t)kTSPacketSize) {
         return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
     } else {
diff --git a/media/tests/CameraBrowser/res/values/strings.xml b/media/tests/CameraBrowser/res/values/strings.xml
index 7955773..932aaec 100644
--- a/media/tests/CameraBrowser/res/values/strings.xml
+++ b/media/tests/CameraBrowser/res/values/strings.xml
@@ -41,5 +41,6 @@
     <string name="save_failed_message">Could not save object</string>
     <string name="object_deleted_message">Object deleted</string>
     <string name="delete_failed_message">Could not delete object</string>
+    <string name="start_activity_failed_message">Import succeeded, but could not display object</string>
 
 </resources>
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
index 3a6c6a4..d53dbff 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
@@ -16,6 +16,7 @@
 package com.android.camerabrowser;
 
 import android.app.Activity;
+import android.content.ActivityNotFoundException;
 import android.content.ContentValues;
 import android.content.Intent;
 import android.database.Cursor;
@@ -155,7 +156,11 @@
         if (resultUri != null) {
             Toast.makeText(this, R.string.object_saved_message, Toast.LENGTH_SHORT).show();
             Intent intent = new Intent(Intent.ACTION_VIEW, resultUri);
-            startActivity(intent);
+            try {
+                startActivity(intent);
+            } catch (ActivityNotFoundException e) {
+                Toast.makeText(this, R.string.start_activity_failed_message, Toast.LENGTH_SHORT).show();
+            }
         } else {
             Toast.makeText(this, R.string.save_failed_message, Toast.LENGTH_SHORT).show();
         }
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_0.png b/packages/SystemUI/res/drawable-hdpi/battery_0.png
deleted file mode 100644
index f4103a8..0000000
--- a/packages/SystemUI/res/drawable-hdpi/battery_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_100.png b/packages/SystemUI/res/drawable-hdpi/battery_100.png
deleted file mode 100644
index 061cbe5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/battery_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_20.png b/packages/SystemUI/res/drawable-hdpi/battery_20.png
deleted file mode 100644
index 0064027..0000000
--- a/packages/SystemUI/res/drawable-hdpi/battery_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_40.png b/packages/SystemUI/res/drawable-hdpi/battery_40.png
deleted file mode 100644
index 10de0e7..0000000
--- a/packages/SystemUI/res/drawable-hdpi/battery_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_60.png b/packages/SystemUI/res/drawable-hdpi/battery_60.png
deleted file mode 100644
index aa2b8ef..0000000
--- a/packages/SystemUI/res/drawable-hdpi/battery_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_80.png b/packages/SystemUI/res/drawable-hdpi/battery_80.png
deleted file mode 100644
index fe231f0..0000000
--- a/packages/SystemUI/res/drawable-hdpi/battery_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/dots_empty.png b/packages/SystemUI/res/drawable-hdpi/dots_empty.png
deleted file mode 100644
index 31e7654..0000000
--- a/packages/SystemUI/res/drawable-hdpi/dots_empty.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/dots_full.png b/packages/SystemUI/res/drawable-hdpi/dots_full.png
deleted file mode 100644
index 8c5c604..0000000
--- a/packages/SystemUI/res/drawable-hdpi/dots_full.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notification_veto.png b/packages/SystemUI/res/drawable-hdpi/ic_notification_veto.png
deleted file mode 100644
index 395604f..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notification_veto.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_off.png
deleted file mode 100644
index c7578f1..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_mini.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_mini.png
deleted file mode 100644
index c541c7c..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_mini.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_on.png
deleted file mode 100644
index 1e55c88..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_on.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_off.png
deleted file mode 100644
index 8045e96..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_on.png
deleted file mode 100644
index 858a191..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_on.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_auto.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_auto.png
deleted file mode 100644
index 35d9425ed..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_auto.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_low.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_low.png
deleted file mode 100644
index 808af5c..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_low.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_close.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_close.png
deleted file mode 100644
index 0ae98d6..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_close.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_default_bg.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_default_bg.png
deleted file mode 100644
index 4272f32..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_default_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_off.png
deleted file mode 100644
index 8a4932b..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lightsout.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lightsout.png
deleted file mode 100644
index 97897b9..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lightsout.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail.png
deleted file mode 100644
index 1a2af70..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail_open.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail_open.png
deleted file mode 100644
index cdeadb8..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail_open.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_dnd.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_dnd.png
deleted file mode 100644
index 2bccff0..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_dnd.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none.png
deleted file mode 100644
index 5417216..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none_open.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none_open.png
deleted file mode 100644
index 544e6ba..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none_open.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_notification_dnd.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_notification_dnd.png
deleted file mode 100644
index 51b5809..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_notification_dnd.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_notification_dnd_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_notification_dnd_off.png
deleted file mode 100644
index 37ef7e1..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_notification_dnd_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_open.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_open.png
deleted file mode 100644
index 5813a34..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_open.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_press_bg.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_press_bg.png
deleted file mode 100644
index 513f2a2..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_press_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off.png
deleted file mode 100644
index cf5174a..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off_lanscape.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off_lanscape.png
deleted file mode 100644
index c091489..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off_lanscape.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off_portrait.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off_portrait.png
deleted file mode 100644
index 73a9897..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off_portrait.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_off.png
deleted file mode 100644
index efdf566..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_mini.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_mini.png
deleted file mode 100644
index 2475eb1..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_mini.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_off.png
deleted file mode 100644
index fd747a1..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/shade_bg.png b/packages/SystemUI/res/drawable-hdpi/shade_bg.png
deleted file mode 100644
index 3d00cd0..0000000
--- a/packages/SystemUI/res/drawable-hdpi/shade_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_100.png b/packages/SystemUI/res/drawable-hdpi/signal_100.png
deleted file mode 100644
index 96e52ff..0000000
--- a/packages/SystemUI/res/drawable-hdpi/signal_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_20.png b/packages/SystemUI/res/drawable-hdpi/signal_20.png
deleted file mode 100644
index c0f652a..0000000
--- a/packages/SystemUI/res/drawable-hdpi/signal_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_40.png b/packages/SystemUI/res/drawable-hdpi/signal_40.png
deleted file mode 100644
index 995dd8e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/signal_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_60.png b/packages/SystemUI/res/drawable-hdpi/signal_60.png
deleted file mode 100644
index 51e31ba..0000000
--- a/packages/SystemUI/res/drawable-hdpi/signal_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_80.png b/packages/SystemUI/res/drawable-hdpi/signal_80.png
deleted file mode 100644
index afa656e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/signal_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_back_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_back_default.png
deleted file mode 100644
index a9f9540..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_back_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_back_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_back_pressed.png
deleted file mode 100644
index b8aa190..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_back_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_home_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_home_default.png
deleted file mode 100644
index cb951dd..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_home_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_home_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_home_pressed.png
deleted file mode 100644
index a835ad5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_home_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_icon_tray.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_icon_tray.9.png
deleted file mode 100644
index e1f041c..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_icon_tray.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
deleted file mode 100644
index 4fbfa4f..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_menu_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_menu_default.png
deleted file mode 100644
index 14779cf..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_menu_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_menu_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_menu_pressed.png
deleted file mode 100644
index 6c3d4c9..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_menu_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_recent_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_recent_default.png
deleted file mode 100644
index dc4cb51..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_recent_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_recent_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_recent_pressed.png
deleted file mode 100644
index dc4cb51..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_recent_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_hidenotification_handle.png b/packages/SystemUI/res/drawable-hdpi/sysbar_hidenotification_handle.png
deleted file mode 100644
index 64945d4..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_hidenotification_handle.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_minimeter_bg.png b/packages/SystemUI/res/drawable-hdpi/sysbar_minimeter_bg.png
deleted file mode 100644
index b59bf1d..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_minimeter_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_panel_bg.9.png b/packages/SystemUI/res/drawable-hdpi/sysbar_panel_bg.9.png
deleted file mode 100644
index 2d5ccfa..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_panel_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_panel_recents_bg.9.png b/packages/SystemUI/res/drawable-hdpi/sysbar_panel_recents_bg.9.png
deleted file mode 100644
index cd8120b..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_panel_recents_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_0.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_0.png
deleted file mode 100644
index 8811252..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_10.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_10.png
deleted file mode 100644
index 9c6d641..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_10.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_100.png
deleted file mode 100644
index 58f0129..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_20.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_20.png
deleted file mode 100644
index 440bd50..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_30.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_30.png
deleted file mode 100644
index cc17a9f..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_30.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_40.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_40.png
deleted file mode 100644
index 87537f7..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_50.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_50.png
deleted file mode 100644
index 2aa5967..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_50.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_60.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_60.png
deleted file mode 100644
index bedb646..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_70.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_70.png
deleted file mode 100644
index 9b4a966..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_70.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_80.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_80.png
deleted file mode 100644
index 73ed185..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_90.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_90.png
deleted file mode 100644
index 8e0c706..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_90.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png
deleted file mode 100644
index d89f8891..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_off.9.png b/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_off.9.png
deleted file mode 100644
index 9dc0390..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_off.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_on.9.png b/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_on.9.png
deleted file mode 100644
index 08fe94f..0000000
--- a/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_on.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png
deleted file mode 100644
index e375ee9..0000000
--- a/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png
deleted file mode 100644
index f62502b..0000000
--- a/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png
deleted file mode 100644
index e887fb8..0000000
--- a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png
deleted file mode 100644
index 58159d5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png
deleted file mode 100644
index 6e857b5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_rotate_off.png
deleted file mode 100644
index 66f299c..0000000
--- a/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_rotate_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-mdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-land-mdpi/ic_sysbar_rotate_off.png
deleted file mode 100644
index adaadf7..0000000
--- a/packages/SystemUI/res/drawable-land-mdpi/ic_sysbar_rotate_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_0.png b/packages/SystemUI/res/drawable-mdpi/battery_0.png
deleted file mode 100644
index 00fb261..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_100.png b/packages/SystemUI/res/drawable-mdpi/battery_100.png
deleted file mode 100644
index 600da18..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_20.png b/packages/SystemUI/res/drawable-mdpi/battery_20.png
deleted file mode 100644
index 5545cdb..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_40.png b/packages/SystemUI/res/drawable-mdpi/battery_40.png
deleted file mode 100644
index f57ce03..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_60.png b/packages/SystemUI/res/drawable-mdpi/battery_60.png
deleted file mode 100644
index c8affe2..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_80.png b/packages/SystemUI/res/drawable-mdpi/battery_80.png
deleted file mode 100644
index d877fe1..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/dots_empty.png b/packages/SystemUI/res/drawable-mdpi/dots_empty.png
deleted file mode 100644
index 22ada41..0000000
--- a/packages/SystemUI/res/drawable-mdpi/dots_empty.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/dots_full.png b/packages/SystemUI/res/drawable-mdpi/dots_full.png
deleted file mode 100644
index 2a346d6..0000000
--- a/packages/SystemUI/res/drawable-mdpi/dots_full.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notification_veto.png b/packages/SystemUI/res/drawable-mdpi/ic_notification_veto.png
deleted file mode 100644
index f6299e9..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notification_veto.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png
deleted file mode 100644
index d897ba61..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png
deleted file mode 100644
index 668b472..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png
deleted file mode 100644
index e463ba4..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png
deleted file mode 100644
index 1239d50..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png
deleted file mode 100644
index 37a1533..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png
deleted file mode 100644
index 8a55e3a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png
deleted file mode 100644
index 53abcbc..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png
deleted file mode 100644
index 3e82d4e..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png
deleted file mode 100644
index dc2ed34..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.png
deleted file mode 100644
index 8a07acc..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail.png
deleted file mode 100644
index 9123fef..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail_open.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail_open.png
deleted file mode 100644
index 8e56f2a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail_open.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_dnd.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_dnd.png
deleted file mode 100644
index 80cf99c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_dnd.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none.png
deleted file mode 100644
index e0d018b..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none_open.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none_open.png
deleted file mode 100644
index 5db8c9c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none_open.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_notification_dnd.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_notification_dnd.png
deleted file mode 100644
index fe44063..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_notification_dnd.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_notification_dnd_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_notification_dnd_off.png
deleted file mode 100644
index 8088a2f..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_notification_dnd_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png
deleted file mode 100644
index b7d624e..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.png
deleted file mode 100644
index 0958393..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off.png
deleted file mode 100644
index 9e6793a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_lanscape.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_lanscape.png
deleted file mode 100644
index 96bf2ec..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_lanscape.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_portrait.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_portrait.png
deleted file mode 100644
index cc56778..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_portrait.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png
deleted file mode 100644
index bd11e86..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png
deleted file mode 100644
index 87acc14..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/shade_bg.png b/packages/SystemUI/res/drawable-mdpi/shade_bg.png
deleted file mode 100644
index 941d3b1..0000000
--- a/packages/SystemUI/res/drawable-mdpi/shade_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_100.png b/packages/SystemUI/res/drawable-mdpi/signal_100.png
deleted file mode 100644
index 59df547..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_20.png b/packages/SystemUI/res/drawable-mdpi/signal_20.png
deleted file mode 100644
index 3bce724..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_40.png b/packages/SystemUI/res/drawable-mdpi/signal_40.png
deleted file mode 100644
index 570069c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_60.png b/packages/SystemUI/res/drawable-mdpi/signal_60.png
deleted file mode 100644
index 0cfc7a6..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_80.png b/packages/SystemUI/res/drawable-mdpi/signal_80.png
deleted file mode 100644
index 66201d0..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png
deleted file mode 100644
index 1c59b2a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
deleted file mode 100644
index f99a66d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
deleted file mode 100644
index 94a0649..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
deleted file mode 100644
index 7e8ade5..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
deleted file mode 100644
index 7e8ade5..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
deleted file mode 100644
index 502acce..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
deleted file mode 100644
index c079615..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_menu_default.png
deleted file mode 100644
index bf3a755..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_menu_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_menu_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_menu_pressed.png
deleted file mode 100644
index 15e21d73..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_menu_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png
deleted file mode 100644
index 7de67b0..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
deleted file mode 100644
index 7de67b0..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png b/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png
deleted file mode 100644
index e43edd7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png b/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png
deleted file mode 100644
index 0d265fc..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png
deleted file mode 100644
index 77e034b..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_panel_recents_bg.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_panel_recents_bg.9.png
deleted file mode 100644
index 85726d2..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_panel_recents_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png
deleted file mode 100644
index 7371571..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png
deleted file mode 100644
index 5781e9a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png
deleted file mode 100644
index c65728c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png
deleted file mode 100644
index a249fa2..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png
deleted file mode 100644
index 289c088..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png
deleted file mode 100644
index f49dbd6..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png
deleted file mode 100644
index 2f83e68..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png
deleted file mode 100644
index bedc021..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png
deleted file mode 100644
index edd8c05..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png
deleted file mode 100644
index 148e20c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png
deleted file mode 100644
index 5a90ccb..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png
deleted file mode 100644
index a5eaa63..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png
deleted file mode 100644
index 94849d8..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png
deleted file mode 100644
index f11058c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png
deleted file mode 100644
index eb87532..0000000
--- a/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png
deleted file mode 100644
index 3bfc83e..0000000
--- a/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png
deleted file mode 100644
index 0d8479c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png
deleted file mode 100644
index 8f1d26c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png
deleted file mode 100644
index 22636d6..0000000
--- a/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-port-hdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-port-hdpi/ic_sysbar_rotate_off.png
deleted file mode 100644
index fcdda31..0000000
--- a/packages/SystemUI/res/drawable-port-hdpi/ic_sysbar_rotate_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-port-mdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-port-mdpi/ic_sysbar_rotate_off.png
deleted file mode 100644
index fdc0ac7..0000000
--- a/packages/SystemUI/res/drawable-port-mdpi/ic_sysbar_rotate_off.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
index 97cbfca..cc044a1 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
@@ -27,15 +27,6 @@
     android:id="@+id/recents_bg_protect"
     android:orientation="vertical">
 
-    <TextView android:id="@+id/recents_no_recents"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/recent_tasks_empty"
-        android:textSize="22dip"
-        android:gravity="center_horizontal|center_vertical"
-        android:visibility="gone">
-    </TextView>
-
     <View
         android:layout_width="match_parent"
         android:layout_height="0dip"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
index aa431bc..ddce6bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
@@ -162,7 +162,7 @@
     // * There are no explicitly enabled (by the user) subtypes of the IME, or the IME doesn't have
     // its subtypes at all
     private boolean needsToShowIMEButton() {
-        List<InputMethodInfo> imis = mImm.getInputMethodList();
+        List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
         final int size = imis.size();
         return size > 1
                 || (size == 1 && mImm.getEnabledInputMethodSubtypeList(imis.get(0)).size() > 1);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index 1301329..e0f37ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -46,6 +46,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -54,13 +55,15 @@
 import com.android.systemui.R;
 
 public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnClickListener {
+    private static final int COLLAPSE_DURATION = 360;
     private static final String TAG = "RecentAppsPanel";
     private static final boolean DEBUG = TabletStatusBar.DEBUG;
     private static final int DISPLAY_TASKS_PORTRAIT = 8;
     private static final int DISPLAY_TASKS_LANDSCAPE = 5; // number of recent tasks to display
     private static final int MAX_TASKS = DISPLAY_TASKS_PORTRAIT + 2; // allow extra for non-apps
+    private static final int STAGGER_ANIMATION_DELAY = 30;
+    private static final long ALPHA_ANIMATION_DURATION = 120;
     private TabletStatusBar mBar;
-    private TextView mNoRecents;
     private LinearLayout mRecentsContainer;
     private View mRecentsGlowView;
     private ArrayList<ActivityDescription> mActivityDescriptions;
@@ -123,7 +126,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mNoRecents = (TextView) findViewById(R.id.recents_no_recents);
         mRecentsContainer = (LinearLayout) findViewById(R.id.recents_container);
         mRecentsGlowView = findViewById(R.id.recents_glow);
         mBackgroundProtector = (View) findViewById(R.id.recents_bg_protect);
@@ -193,7 +195,10 @@
                     .resolveActivityInfo(pm, 0);
 
         int numTasks = recentTasks.size();
-        for (int i = 0, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+
+        // skip the first activity - assume it's either the home screen or the current app.
+        final int first = 1;
+        for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
             final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
 
             Intent intent = new Intent(recentInfo.baseIntent);
@@ -297,22 +302,21 @@
             if (animate) {
                 view.setAlpha(initialAlpha);
                 ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", initialAlpha, 1.0f);
-                anim.setDuration(200);
-                anim.setStartDelay((last-i)*80);
+                anim.setDuration(ALPHA_ANIMATION_DURATION);
+                anim.setStartDelay((last-i) * STAGGER_ANIMATION_DELAY);
                 anim.setInterpolator(interp);
                 anims.add(anim);
             }
         }
 
         int views = mRecentsContainer.getChildCount();
-        mNoRecents.setVisibility(View.GONE); // views == 0 ? View.VISIBLE : View.GONE);
         mRecentsContainer.setVisibility(views > 0 ? View.VISIBLE : View.GONE);
         mRecentsGlowView.setVisibility(views > 0 ? View.VISIBLE : View.GONE);
 
         if (animate && views > 0) {
             ObjectAnimator anim = ObjectAnimator.ofFloat(mRecentsGlowView, "alpha",
                     initialAlpha, 1.0f);
-            anim.setDuration((last-first)*80);
+            anim.setDuration((last-first) * STAGGER_ANIMATION_DELAY);
             anim.setInterpolator(interp);
             anims.add(anim);
         }
@@ -320,7 +324,7 @@
         if (animate) {
             ObjectAnimator anim = ObjectAnimator.ofFloat(mBackgroundProtector, "alpha",
                     initialAlpha, 1.0f);
-            anim.setDuration(last*80);
+            anim.setDuration(last * STAGGER_ANIMATION_DELAY);
             anim.setInterpolator(interp);
             anims.add(anim);
         }
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index 175f613..5b9273d 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -1641,19 +1641,19 @@
         // FALL THROUGH
 
     case STRATEGY_MEDIA: {
-        uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
-        if (device2 == 0) {
-            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
-        }
+        uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
         if (device2 == 0) {
             device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
         }
         if (device2 == 0) {
-            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
         }
         if (device2 == 0) {
             device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
         }
+        if (device2 == 0) {
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+        }
 #ifdef WITH_A2DP
         if (mA2dpOutput != 0) {
             if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 9d11d87..f82a243 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -698,9 +698,8 @@
         }
         NetworkStateTracker network = mNetTrackers[usedNetworkType];
         if (network != null) {
+            Integer currentPid = new Integer(getCallingPid());
             if (usedNetworkType != networkType) {
-                Integer currentPid = new Integer(getCallingPid());
-
                 NetworkStateTracker radio = mNetTrackers[networkType];
                 NetworkInfo ni = network.getNetworkInfo();
 
@@ -739,6 +738,14 @@
                 network.reconnect();
                 return Phone.APN_REQUEST_STARTED;
             } else {
+                // need to remember this unsupported request so we respond appropriately on stop
+                synchronized(this) {
+                    mFeatureUsers.add(f);
+                    if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
+                        // this gets used for per-pid dns when connected
+                        mNetRequestersPids[usedNetworkType].add(currentPid);
+                    }
+                }
                 return -1;
             }
         }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 4fbdb2d..0490190 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -332,8 +332,6 @@
                 boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals(
                                     extras.getString(UsbManager.USB_FUNCTION_ADB)));
                 updateAdbNotification(usbConnected && adbEnabled);
-            } else if (action.equals(UsbManager.ACTION_USB_DISCONNECTED)) {
-                updateAdbNotification(false);
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/UsbService.java
index b90731f..a2bf75d 100644
--- a/services/java/com/android/server/UsbService.java
+++ b/services/java/com/android/server/UsbService.java
@@ -41,15 +41,33 @@
     private static final String TAG = UsbService.class.getSimpleName();
     private static final boolean LOG = false;
 
-    private static final String USB_CONFIGURATION_MATCH = "DEVPATH=/devices/virtual/switch/usb_configuration";
-    private static final String USB_FUNCTIONS_MATCH = "DEVPATH=/devices/virtual/usb_composite/";
-    private static final String USB_CONFIGURATION_PATH = "/sys/class/switch/usb_configuration/state";
-    private static final String USB_COMPOSITE_CLASS_PATH = "/sys/class/usb_composite";
+    private static final String USB_CONNECTED_MATCH =
+            "DEVPATH=/devices/virtual/switch/usb_connected";
+    private static final String USB_CONFIGURATION_MATCH =
+            "DEVPATH=/devices/virtual/switch/usb_configuration";
+    private static final String USB_FUNCTIONS_MATCH =
+            "DEVPATH=/devices/virtual/usb_composite/";
+    private static final String USB_CONNECTED_PATH =
+            "/sys/class/switch/usb_connected/state";
+    private static final String USB_CONFIGURATION_PATH =
+            "/sys/class/switch/usb_configuration/state";
+    private static final String USB_COMPOSITE_CLASS_PATH =
+            "/sys/class/usb_composite";
 
     private static final int MSG_UPDATE = 0;
 
-    private int mUsbConfig = 0;
-    private int mPreviousUsbConfig = 0;
+    // Delay for debouncing USB disconnects.
+    // We often get rapid connect/disconnect events when enabling USB functions,
+    // which need debouncing.
+    private static final int UPDATE_DELAY = 1000;
+
+    // current connected and configuration state
+    private int mConnected;
+    private int mConfiguration;
+
+    // last broadcasted connected and configuration state
+    private int mLastConnected = -1;
+    private int mLastConfiguration = -1;
 
     // lists of enabled and disabled USB functions
     private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
@@ -59,8 +77,6 @@
 
     private final Context mContext;
 
-    private PowerManagerService mPowerManager;
-
     private final UEventObserver mUEventObserver = new UEventObserver() {
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
@@ -69,16 +85,23 @@
             }
 
             synchronized (this) {
-                String switchState = event.get("SWITCH_STATE");
-                if (switchState != null) {
+                String name = event.get("SWITCH_NAME");
+                String state = event.get("SWITCH_STATE");
+                if (name != null && state != null) {
                     try {
-                        int newConfig = Integer.parseInt(switchState);
-                        if (newConfig != mUsbConfig) {
-                            mPreviousUsbConfig = mUsbConfig;
-                            mUsbConfig = newConfig;
+                        int intState = Integer.parseInt(state);
+                        if ("usb_connected".equals(name)) {
+                            mConnected = intState;
                             // trigger an Intent broadcast
                             if (mSystemReady) {
-                                update();
+                                // debounce disconnects
+                                update(mConnected == 0);
+                            }
+                        } else if ("usb_configuration".equals(name)) {
+                            mConfiguration = intState;
+                            // trigger an Intent broadcast
+                            if (mSystemReady) {
+                                update(mConnected == 0);
                             }
                         }
                     } catch (NumberFormatException e) {
@@ -112,6 +135,7 @@
         mContext = context;
         init();  // set initial status
 
+        mUEventObserver.startObserving(USB_CONNECTED_MATCH);
         mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
         mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
     }
@@ -120,10 +144,15 @@
         char[] buffer = new char[1024];
 
         try {
-            FileReader file = new FileReader(USB_CONFIGURATION_PATH);
+            FileReader file = new FileReader(USB_CONNECTED_PATH);
             int len = file.read(buffer, 0, 1024);
             file.close();
-            mPreviousUsbConfig = mUsbConfig = Integer.valueOf((new String(buffer, 0, len)).trim());
+            mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+            file = new FileReader(USB_CONFIGURATION_PATH);
+            len = file.read(buffer, 0, 1024);
+            file.close();
+            mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
 
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "This kernel does not have USB configuration switch support");
@@ -190,13 +219,14 @@
                 initHostSupport();
             }
 
-            update();
+            update(false);
             mSystemReady = true;
         }
     }
 
-    private final void update() {
-        mHandler.sendEmptyMessage(MSG_UPDATE);
+    private final void update(boolean delayed) {
+        mHandler.removeMessages(MSG_UPDATE);
+        mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0);
     }
 
     private final Handler mHandler = new Handler() {
@@ -215,31 +245,26 @@
             switch (msg.what) {
                 case MSG_UPDATE:
                     synchronized (this) {
-                        final ContentResolver cr = mContext.getContentResolver();
+                        if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
 
-                        if (Settings.Secure.getInt(cr,
-                                Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
-                            Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
-                            return;
-                        }
-                        // Send an Intent containing connected/disconnected state
-                        // and the enabled/disabled state of all USB functions
-                        Intent intent;
-                        boolean usbConnected = (mUsbConfig != 0);
-                        if (usbConnected) {
-                            intent = new Intent(UsbManager.ACTION_USB_CONNECTED);
+                            final ContentResolver cr = mContext.getContentResolver();
+                            if (Settings.Secure.getInt(cr,
+                                    Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+                                Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
+                                return;
+                            }
+
+                            mLastConnected = mConnected;
+                            mLastConfiguration = mConfiguration;
+
+                            // send a sticky broadcast containing current USB state
+                            Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+                            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+                            intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
+                            intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
                             addEnabledFunctions(intent);
-                        } else {
-                            intent = new Intent(UsbManager.ACTION_USB_DISCONNECTED);
+                            mContext.sendStickyBroadcast(intent);
                         }
-                        mContext.sendBroadcast(intent);
-
-                        // send a sticky broadcast for clients interested in both connect and disconnect
-                        intent = new Intent(UsbManager.ACTION_USB_STATE);
-                        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-                        intent.putExtra(UsbManager.USB_CONNECTED, usbConnected);
-                        addEnabledFunctions(intent);
-                        mContext.sendStickyBroadcast(intent);
                     }
                     break;
             }
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 920bbc9..516dfa21 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -662,7 +662,7 @@
         }
 
         if (w > 0) {
-            //return mService.mWindowManager.screenshotApplications(who, w, h);
+            return mService.mWindowManager.screenshotApplications(who, w, h);
         }
         return null;
     }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index d742d4c..a93d596 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -128,14 +128,6 @@
     private boolean mUsbMassStorageOff;  // track the status of USB Mass Storage
     private boolean mUsbConnected;       // track the status of USB connection
 
-    // mUsbHandler message
-    static final int USB_STATE_CHANGE = 1;
-    static final int USB_DISCONNECTED = 0;
-    static final int USB_CONNECTED = 1;
-
-    // Time to delay before processing USB disconnect events
-    static final long USB_DISCONNECT_DELAY = 1000;
-
     public Tethering(Context context, Looper looper) {
         mContext = context;
         mLooper = looper;
@@ -482,25 +474,12 @@
         }
     }
 
-    private Handler mUsbHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            mUsbConnected = (msg.arg1 == USB_CONNECTED);
-            updateUsbStatus();
-        }
-    };
-
     private class StateReceiver extends BroadcastReceiver {
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
             if (action.equals(UsbManager.ACTION_USB_STATE)) {
-                // process connect events immediately, but delay handling disconnects
-                // to debounce USB configuration changes
-                boolean connected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
-                Message msg = Message.obtain(mUsbHandler, USB_STATE_CHANGE,
-                        (connected ? USB_CONNECTED : USB_DISCONNECTED), 0);
-                mUsbHandler.removeMessages(USB_STATE_CHANGE);
-                mUsbHandler.sendMessageDelayed(msg, connected ? 0 : USB_DISCONNECT_DELAY);
+                mUsbConnected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
+                updateUsbStatus();
             } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
                 mUsbMassStorageOff = false;
                 updateUsbStatus();
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5a0f115..fde68f6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -804,7 +804,7 @@
 
 Layer::BufferManager::BufferManager(TextureManager& tm)
     : mNumBuffers(NUM_BUFFERS), mTextureManager(tm),
-      mActiveBuffer(-1), mFailover(false)
+      mActiveBufferIndex(-1), mFailover(false)
 {
 }
 
@@ -819,10 +819,10 @@
 
     if (size < mNumBuffers) {
         // Move the active texture into slot 0
-        BufferData activeBufferData = mBufferData[mActiveBuffer];
-        mBufferData[mActiveBuffer] = mBufferData[0];
+        BufferData activeBufferData = mBufferData[mActiveBufferIndex];
+        mBufferData[mActiveBufferIndex] = mBufferData[0];
         mBufferData[0] = activeBufferData;
-        mActiveBuffer = 0;
+        mActiveBufferIndex = 0;
 
         // Free the buffers that are no longer needed.
         for (size_t i = size; i < mNumBuffers; i++) {
@@ -868,37 +868,33 @@
 }
 
 status_t Layer::BufferManager::setActiveBufferIndex(size_t index) {
-    mActiveBuffer = index;
+    BufferData const * const buffers = mBufferData;
+    Mutex::Autolock _l(mLock);
+    mActiveBuffer = buffers[index].buffer;
+    mActiveBufferIndex = index;
     return NO_ERROR;
 }
 
 size_t Layer::BufferManager::getActiveBufferIndex() const {
-    return mActiveBuffer;
+    return mActiveBufferIndex;
 }
 
 Texture Layer::BufferManager::getActiveTexture() const {
     Texture res;
-    if (mFailover || mActiveBuffer<0) {
+    if (mFailover || mActiveBufferIndex<0) {
         res = mFailoverTexture;
     } else {
-        static_cast<Image&>(res) = mBufferData[mActiveBuffer].texture;
+        static_cast<Image&>(res) = mBufferData[mActiveBufferIndex].texture;
     }
     return res;
 }
 
 sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const {
-    sp<GraphicBuffer> result;
-    const ssize_t activeBuffer = mActiveBuffer;
-    if (activeBuffer >= 0) {
-        BufferData const * const buffers = mBufferData;
-        Mutex::Autolock _l(mLock);
-        result = buffers[activeBuffer].buffer;
-    }
-    return result;
+    return mActiveBuffer;
 }
 
 bool Layer::BufferManager::hasActiveBuffer() const {
-    return mActiveBuffer >= 0;
+    return mActiveBufferIndex >= 0;
 }
 
 sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index)
@@ -943,7 +939,7 @@
         const sp<GraphicBuffer>& buffer)
 {
     status_t err = NO_INIT;
-    ssize_t index = mActiveBuffer;
+    ssize_t index = mActiveBufferIndex;
     if (index >= 0) {
         if (!mFailover) {
             Image& texture(mBufferData[index].texture);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c367a8d..5444d2f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -164,7 +164,8 @@
         size_t              mNumBuffers;
         Texture             mFailoverTexture;
         TextureManager&     mTextureManager;
-        ssize_t             mActiveBuffer;
+        ssize_t             mActiveBufferIndex;
+        sp<GraphicBuffer>   mActiveBuffer;
         bool                mFailover;
         static status_t destroyTexture(Image* tex, EGLDisplay dpy);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a6b1422..7c4790f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -395,6 +395,7 @@
         logger.log(GraphicLog::SF_REPAINT_DONE, index);
     } else {
         // pretend we did the post
+        hw.compositionComplete();
         unlockClients();
         usleep(16667); // 60 fps period
     }
@@ -2184,6 +2185,9 @@
     glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
     glDeleteRenderbuffersOES(1, &tname);
     glDeleteFramebuffersOES(1, &name);
+
+    hw.compositionComplete();
+
     return result;
 }
 
diff --git a/test-runner/src/android/test/ActivityUnitTestCase.java b/test-runner/src/android/test/ActivityUnitTestCase.java
index 6bd19a6..8aa8824 100644
--- a/test-runner/src/android/test/ActivityUnitTestCase.java
+++ b/test-runner/src/android/test/ActivityUnitTestCase.java
@@ -50,7 +50,6 @@
  * <li>{@link android.app.Activity#getTaskId()}</li>
  * <li>{@link android.app.Activity#isTaskRoot()}</li>
  * <li>{@link android.app.Activity#moveTaskToBack(boolean)}</li>
- * <li>{@link android.app.Activity#setPersistent(boolean)}</li>
  * </ul>
  * 
  * <p>The following methods may be called but will not do anything.  For test purposes, you can use 
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index f6d7b3a..c75e1b6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -18,7 +18,7 @@
 
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.Stack;
+import com.android.layoutlib.bridge.impl.GcSnapshot;
 
 import android.graphics.Paint_Delegate.FontInfo;
 import android.text.TextUtils;
@@ -26,7 +26,6 @@
 import java.awt.AlphaComposite;
 import java.awt.BasicStroke;
 import java.awt.Color;
-import java.awt.Composite;
 import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.RenderingHints;
@@ -58,7 +57,7 @@
 
     // ---- delegate data ----
     private BufferedImage mBufferedImage;
-    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
+    private GcSnapshot mSnapshot = new GcSnapshot();
 
     // ---- Public Helper methods ----
 
@@ -79,8 +78,8 @@
     /**
      * Returns the current {@link Graphics2D} used to draw.
      */
-    public Graphics2D getGraphics2d() {
-        return mGraphicsStack.peek();
+    public GcSnapshot getGcSnapshot() {
+        return mSnapshot;
     }
 
     // ---- native methods ----
@@ -120,7 +119,7 @@
             return;
         }
 
-        canvasDelegate.getGraphics2d().translate(dx, dy);
+        canvasDelegate.getGcSnapshot().translate(dx, dy);
     }
 
     /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
@@ -131,7 +130,7 @@
             return;
         }
 
-        canvasDelegate.getGraphics2d().rotate(Math.toRadians(degrees));
+        canvasDelegate.getGcSnapshot().rotate(Math.toRadians(degrees));
     }
 
     /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
@@ -142,7 +141,7 @@
             return;
         }
 
-        canvasDelegate.getGraphics2d().scale(sx, sy);
+        canvasDelegate.getGcSnapshot().scale(sx, sy);
     }
 
     /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
@@ -154,7 +153,7 @@
         }
 
         // get the current top graphics2D object.
-        Graphics2D g = canvasDelegate.getGraphics2d();
+        GcSnapshot g = canvasDelegate.getGcSnapshot();
 
         // get its current matrix
         AffineTransform currentTx = g.getTransform();
@@ -170,21 +169,16 @@
     }
 
     /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
-        return clipRect(thisCanvas,
-                (int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom);
+        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
     }
 
     /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
-        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
+        return clipRect(thisCanvas, (float) rect.left, (float) rect.top,
+                (float) rect.right, (float) rect.bottom);
     }
 
     /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
             float bottom) {
-        return clipRect(thisCanvas, (int) left, (int) top, (int) right, (int) bottom);
-    }
-
-    /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
-            int bottom) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
         if (canvasDelegate == null) {
@@ -195,7 +189,17 @@
         return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
     }
 
+    /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
+            int bottom) {
+
+        return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom);
+    }
+
     /*package*/ static int save(Canvas thisCanvas) {
+        return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
+    }
+
+    /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
         if (canvasDelegate == null) {
@@ -203,21 +207,7 @@
             return 0;
         }
 
-        // get the current save count
-        int count = canvasDelegate.mGraphicsStack.size();
-
-        // create a new graphics and add it to the stack
-        Graphics2D g = (Graphics2D)canvasDelegate.getGraphics2d().create();
-        canvasDelegate.mGraphicsStack.push(g);
-
-        // return the old save count
-        return count;
-
-    }
-
-    /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
-        // FIXME implement save(flags)
-        return save(thisCanvas);
+        return canvasDelegate.save(saveFlags);
     }
 
     /*package*/ static void restore(Canvas thisCanvas) {
@@ -228,7 +218,7 @@
             return;
         }
 
-        canvasDelegate.mGraphicsStack.pop();
+        canvasDelegate.restore();
     }
 
     /*package*/ static int getSaveCount(Canvas thisCanvas) {
@@ -239,7 +229,7 @@
             return 0;
         }
 
-        return canvasDelegate.mGraphicsStack.size();
+        return canvasDelegate.getGcSnapshot().size();
     }
 
     /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
@@ -250,9 +240,7 @@
             return;
         }
 
-        while (canvasDelegate.mGraphicsStack.size() > saveCount) {
-            canvasDelegate.mGraphicsStack.pop();
-        }
+        canvasDelegate.restoreTo(saveCount);
     }
 
     /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
@@ -282,7 +270,7 @@
         }
 
         // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+        Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
 
         try {
             for (int i = 0 ; i < count ; i += 4) {
@@ -378,10 +366,10 @@
         }
 
         // get the current top graphics2D object.
-        Graphics2D g = canvasDelegate.getGraphics2d();
+        GcSnapshot snapshot = canvasDelegate.getGcSnapshot();
 
         // get its current matrix
-        AffineTransform currentTx = g.getTransform();
+        AffineTransform currentTx = snapshot.getTransform();
         // get the AffineTransform of the given matrix
         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
 
@@ -389,7 +377,7 @@
         currentTx.preConcatenate(matrixTx);
 
         // give it to the graphics2D as a new matrix replacing all previous transform
-        g.setTransform(currentTx);
+        snapshot.setTransform(currentTx);
     }
 
     /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
@@ -405,15 +393,16 @@
         }
 
         // get the current top graphics2D object.
-        Graphics2D g = canvasDelegate.getGraphics2d();
+        GcSnapshot snapshot = canvasDelegate.getGcSnapshot();
 
         // get the AffineTransform of the given matrix
         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
 
         // give it to the graphics2D as a new matrix replacing all previous transform
-        g.setTransform(matrixTx);
+        snapshot.setTransform(matrixTx);
 
         if (matrixDelegate.hasPerspective()) {
+            assert false;
             Bridge.getLog().warning(null,
                     "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
                     "supports affine transformations in the Layout Preview.");
@@ -431,9 +420,7 @@
             assert false;
         }
 
-        return canvasDelegate.clipRect(
-                (int) left, (int) top, (int) right, (int) bottom,
-                regionOp);
+        return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
     }
 
     /*package*/ static boolean native_clipPath(int nativeCanvas,
@@ -465,7 +452,7 @@
             return false;
         }
 
-        Rectangle rect = canvasDelegate.getGraphics2d().getClipBounds();
+        Rectangle rect = canvasDelegate.getGcSnapshot().getClip().getBounds();
         if (rect != null) {
             bounds.left = rect.x;
             bounds.top = rect.y;
@@ -527,7 +514,7 @@
         }
 
         // get a new graphics context.
-        Graphics2D graphics = (Graphics2D)canvasDelegate.getGraphics2d().create();
+        Graphics2D graphics = (Graphics2D)canvasDelegate.getGcSnapshot().create();
         try {
             // reset its transform just in case
             graphics.setTransform(new AffineTransform());
@@ -568,12 +555,14 @@
         }
 
         // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+        Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
 
-        g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
-
-        // dispose Graphics2D object
-        g.dispose();
+        try {
+            g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
+        } finally {
+            // dispose Graphics2D object
+            g.dispose();
+        }
     }
 
     /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
@@ -600,23 +589,25 @@
 
         if (right > left && bottom > top) {
             // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+            Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
 
-            int style = paintDelegate.getStyle();
+            try {
+                int style = paintDelegate.getStyle();
 
-            // draw
-            if (style == Paint.Style.FILL.nativeInt ||
-                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
+                // draw
+                if (style == Paint.Style.FILL.nativeInt ||
+                        style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                    g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
+                }
+
+                if (style == Paint.Style.STROKE.nativeInt ||
+                        style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                    g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
+                }
+            } finally {
+                // dispose Graphics2D object
+                g.dispose();
             }
-
-            if (style == Paint.Style.STROKE.nativeInt ||
-                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
-            }
-
-            // dispose Graphics2D object
-            g.dispose();
         }
     }
 
@@ -638,7 +629,7 @@
 
         if (oval.right > oval.left && oval.bottom > oval.top) {
             // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+            Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
 
             int style = paintDelegate.getStyle();
 
@@ -692,7 +683,7 @@
 
         if (rect.right > rect.left && rect.bottom > rect.top) {
             // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+            Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
 
             int style = paintDelegate.getStyle();
 
@@ -833,7 +824,7 @@
             return;
         }
 
-        Graphics2D g = (Graphics2D) canvasDelegate.getCustomGraphics(paintDelegate);
+        Graphics2D g = (Graphics2D) canvasDelegate.createCustomGraphics(paintDelegate);
         try {
             // Paint.TextAlign indicates how the text is positioned relative to X.
             // LEFT is the default and there's nothing to do.
@@ -1018,34 +1009,53 @@
      * Disposes of the {@link Graphics2D} stack.
      */
     private void dispose() {
-        while (mGraphicsStack.size() > 0) {
-            mGraphicsStack.pop().dispose();
-        }
+        mSnapshot.dispose();
     }
 
-    private boolean clipRect(int left, int top, int right, int bottom, int regionOp) {
-        if (regionOp == Region.Op.INTERSECT.nativeInt) {
-            Graphics2D gc = getGraphics2d();
-            gc.clipRect(left, top, right - left, bottom - top);
-            return gc.getClip().getBounds().isEmpty() == false;
-        } else {
-            throw new UnsupportedOperationException();
-        }
+    private int save(int saveFlags) {
+        // get the current save count
+        int count = mSnapshot.size();
+
+        // create a new snapshot and add it to the stack
+        mSnapshot = new GcSnapshot(mSnapshot, saveFlags);
+
+        // return the old save count
+        return count;
+    }
+
+    /**
+     * Restores the {@link GcSnapshot} to <var>saveCount</var>
+     * @param saveCount the saveCount
+     */
+    private void restoreTo(int saveCount) {
+        mSnapshot = mSnapshot.restoreTo(saveCount);
+    }
+
+    /**
+     * Restores the {@link GcSnapshot} to <var>saveCount</var>
+     * @param saveCount the saveCount
+     */
+    private void restore() {
+        mSnapshot = mSnapshot.restore();
+    }
+
+    private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
+        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
     }
 
     private void setBitmap(BufferedImage image) {
         mBufferedImage = image;
-        mGraphicsStack.push(mBufferedImage.createGraphics());
+        assert mSnapshot.size() == 1;
+        mSnapshot.setGraphics2D(mBufferedImage.createGraphics());
     }
 
     /**
      * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
      * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
      */
-    /*package*/ Graphics2D getCustomGraphics(Paint_Delegate paint) {
+    /*package*/ Graphics2D createCustomGraphics(Paint_Delegate paint) {
         // make new one
-        Graphics2D g = getGraphics2d();
-        g = (Graphics2D)g.create();
+        Graphics2D g = getGcSnapshot().create();
 
         // configure it
 
@@ -1062,6 +1072,7 @@
         Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(paint.getShader());
         if (shaderDelegate != null) {
             java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
+            assert shaderPaint != null;
             if (shaderPaint != null) {
                 g.setPaint(shaderPaint);
                 useColorPaint = false;
@@ -1113,6 +1124,7 @@
 
             // if xfermode wasn't null, then it's something we don't support. log it.
             if (xfermodeDelegate != null) {
+                assert false;
                 Bridge.getLog().warning(null,
                         String.format(
                             "Xfermode '%1$s' is not supported in the Layout Preview.",
@@ -1217,30 +1229,18 @@
             int sleft, int stop, int sright, int sbottom,
             int dleft, int dtop, int dright, int dbottom) {
 
-        Graphics2D g = canvasDelegate.getGraphics2d();
-
-        Composite c = null;
-
-        if (paintDelegate != null) {
-            if (paintDelegate.isFilterBitmap()) {
-                g = (Graphics2D)g.create();
+        Graphics2D g = canvasDelegate.getGcSnapshot().create();
+        try {
+            if (paintDelegate != null && paintDelegate.isFilterBitmap()) {
                 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                         RenderingHints.VALUE_INTERPOLATION_BILINEAR);
             }
-        }
 
-        g.drawImage(image, dleft, dtop, dright, dbottom,
-                sleft, stop, sright, sbottom, null);
-
-        if (paintDelegate != null) {
-            if (paintDelegate.isFilterBitmap()) {
-                g.dispose();
-            }
-            if (c != null) {
-                g.setComposite(c);
-            }
+            g.drawImage(image, dleft, dtop, dright, dbottom,
+                    sleft, stop, sright, sbottom, null);
+        } finally {
+            g.dispose();
         }
     }
-
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 6b43544..b464f66 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -195,7 +195,8 @@
         setTranslate(d.mValues, dx, dy);
     }
 
-    /*package*/ static void native_setScale(int native_object, float sx, float sy, float px, float py) {
+    /*package*/ static void native_setScale(int native_object, float sx, float sy,
+            float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
             assert false;
@@ -272,7 +273,8 @@
         setRotate(d.mValues, sinValue, cosValue);
     }
 
-    /*package*/ static void native_setSkew(int native_object, float kx, float ky, float px, float py) {
+    /*package*/ static void native_setSkew(int native_object, float kx, float ky,
+            float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
             assert false;
@@ -364,7 +366,8 @@
         return true;
     }
 
-    /*package*/ static boolean native_preRotate(int native_object, float degrees, float px, float py) {
+    /*package*/ static boolean native_preRotate(int native_object, float degrees,
+            float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
             assert false;
@@ -464,7 +467,8 @@
         return true;
     }
 
-    /*package*/ static boolean native_postRotate(int native_object, float degrees, float px, float py) {
+    /*package*/ static boolean native_postRotate(int native_object, float degrees,
+            float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
             assert false;
@@ -526,7 +530,8 @@
         return true;
     }
 
-    /*package*/ static boolean native_setRectToRect(int native_object, RectF src, RectF dst, int stf) {
+    /*package*/ static boolean native_setRectToRect(int native_object, RectF src,
+            RectF dst, int stf) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
             assert false;
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index 95663ec..fe9bef9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -160,20 +160,17 @@
 
        Graphics2D graphics;
        if (paint_delegate != null) {
-           graphics = canvas_delegate.getCustomGraphics(paint_delegate);
+           graphics = canvas_delegate.createCustomGraphics(paint_delegate);
        } else {
-           graphics = canvas_delegate.getGraphics2d();
+           graphics = canvas_delegate.getGcSnapshot().create();
        }
 
        try {
            chunkObject.draw(bitmap_delegate.getImage(), graphics,
                    left, top, right - left, bottom - top, destDensity, srcDensity);
        } finally {
-           if (paint_delegate != null) {
-               graphics.dispose();
-           }
+           graphics.dispose();
        }
-
     }
 
     /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
new file mode 100644
index 0000000..8c6b1be
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import android.graphics.Canvas;
+import android.graphics.Region;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Class representing a graphics context snapshot, as well as a context stack as a linked list.
+ * <p>
+ * This is based on top of {@link Graphics2D} but can operate independently if none are available
+ * yet when setting transforms and clip information.
+ *
+ */
+public class GcSnapshot {
+
+    private final GcSnapshot mPrevious;
+    private final int mFlags;
+
+    private Graphics2D mGraphics2D = null;
+    /** temp transform in case transformation are set before a Graphics2D exists */
+    private AffineTransform mTransform = null;
+    /** temp clip in case clipping is set before a Graphics2D exists */
+    private Area mClip = null;
+
+    /**
+     * Creates a new {@link GcSnapshot} on top of another one.
+     * <p/>
+     * This is basically the equivalent of {@link Canvas#save(int)}
+     * @param previous the previous snapshot head.
+     * @param flags the flags regarding what should be saved.
+     */
+    public GcSnapshot(GcSnapshot previous, int flags) {
+        assert previous != null;
+        mPrevious = previous;
+        mFlags = flags;
+        mGraphics2D = (Graphics2D) previous.mGraphics2D.create();
+    }
+
+    /**
+     * Creates the root snapshot.
+     * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible.
+     */
+    public GcSnapshot() {
+        mPrevious = null;
+        mFlags = 0;
+    }
+
+    public void dispose() {
+        if (mGraphics2D != null) {
+            mGraphics2D.dispose();
+        }
+
+        if (mPrevious != null) {
+            mPrevious.dispose();
+        }
+    }
+
+    /**
+     * Restores the top {@link GcSnapshot}, and returns the next one.
+     */
+    public GcSnapshot restore() {
+        return doRestore();
+    }
+
+    /**
+     * Restores the {@link GcSnapshot} to <var>saveCount</var>.
+     * @param saveCount the saveCount or -1 to only restore 1.
+     *
+     * @return the new head of the Gc snapshot stack.
+     */
+    public GcSnapshot restoreTo(int saveCount) {
+        return doRestoreTo(size(), saveCount);
+    }
+
+    public int size() {
+        if (mPrevious != null) {
+            return mPrevious.size() + 1;
+        }
+
+        return 1;
+    }
+
+    /**
+     * Sets the Graphics2D object for this snapshot if it was created through {@link #GcSnapshot()}.
+     * If any transform or clip information was set before, they are put into the Graphics object.
+     * @param graphics2D the graphics object to set.
+     */
+    public void setGraphics2D(Graphics2D graphics2D) {
+        mGraphics2D = graphics2D;
+        if (mTransform != null) {
+            mGraphics2D.setTransform(mTransform);
+            mTransform = null;
+        }
+
+        if (mClip != null) {
+            mGraphics2D.setClip(mClip);
+            mClip = null;
+        }
+    }
+
+    /**
+     * Creates and return a copy of the current {@link Graphics2D}.
+     * @return a new {@link Graphics2D}.
+     */
+    public Graphics2D create() {
+        assert mGraphics2D != null;
+        return (Graphics2D) mGraphics2D.create();
+    }
+
+    public void translate(float dx, float dy) {
+        if (mGraphics2D != null) {
+            mGraphics2D.translate(dx, dy);
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            mTransform.translate(dx, dy);
+        }
+    }
+
+    public void rotate(double radians) {
+        if (mGraphics2D != null) {
+            mGraphics2D.rotate(radians);
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            mTransform.rotate(radians);
+        }
+    }
+
+    public void scale(float sx, float sy) {
+        if (mGraphics2D != null) {
+            mGraphics2D.scale(sx, sy);
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            mTransform.scale(sx, sy);
+        }
+    }
+
+    public AffineTransform getTransform() {
+        if (mGraphics2D != null) {
+            return mGraphics2D.getTransform();
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            return mTransform;
+        }
+    }
+
+    public void setTransform(AffineTransform transform) {
+        if (mGraphics2D != null) {
+            mGraphics2D.setTransform(transform);
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            mTransform.setTransform(transform);
+        }
+    }
+
+    public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
+        if (mGraphics2D != null) {
+            if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
+                Area newClip = new Area(mGraphics2D.getClip());
+                newClip.subtract(new Area(
+                        new Rectangle2D.Float(left, top, right - left, bottom - top)));
+                mGraphics2D.setClip(newClip);
+
+            } else if (regionOp == Region.Op.INTERSECT.nativeInt) {
+                mGraphics2D.clipRect((int) left, (int) top,
+                        (int) (right - left), (int) (bottom - top));
+
+            } else if (regionOp == Region.Op.UNION.nativeInt) {
+                Area newClip = new Area(mGraphics2D.getClip());
+                newClip.add(new Area(
+                        new Rectangle2D.Float(left, top, right - left, bottom - top)));
+                mGraphics2D.setClip(newClip);
+
+            } else if (regionOp == Region.Op.XOR.nativeInt) {
+                Area newClip = new Area(mGraphics2D.getClip());
+                newClip.exclusiveOr(new Area(
+                        new Rectangle2D.Float(left, top, right - left, bottom - top)));
+                mGraphics2D.setClip(newClip);
+
+            } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
+                Area newClip = new Area(
+                        new Rectangle2D.Float(left, top, right - left, bottom - top));
+                newClip.subtract(new Area(mGraphics2D.getClip()));
+                mGraphics2D.setClip(newClip);
+            } else if (regionOp == Region.Op.REPLACE.nativeInt) {
+                mGraphics2D.setClip((int) left, (int) top,
+                        (int) (right - left), (int) (bottom - top));
+            }
+
+            return mGraphics2D.getClip().getBounds().isEmpty() == false;
+        } else {
+            if (mClip == null) {
+                mClip = new Area();
+            }
+
+            if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
+                //FIXME
+            } else if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
+            } else if (regionOp == Region.Op.INTERSECT.nativeInt) {
+            } else if (regionOp == Region.Op.UNION.nativeInt) {
+            } else if (regionOp == Region.Op.XOR.nativeInt) {
+            } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
+            } else if (regionOp == Region.Op.REPLACE.nativeInt) {
+            }
+
+            return mClip.getBounds().isEmpty() == false;
+        }
+    }
+
+    public Shape getClip() {
+        if (mGraphics2D != null) {
+            return mGraphics2D.getClip();
+        } else {
+            if (mClip == null) {
+                mClip = new Area();
+            }
+            return mClip;
+        }
+    }
+
+    private GcSnapshot doRestoreTo(int size, int saveCount) {
+        if (size <= saveCount) {
+            return this;
+        }
+
+        // restore the current one first.
+        GcSnapshot previous = doRestore();
+
+        if (size == saveCount + 1) { // this was the only one that needed restore.
+            return previous;
+        } else {
+            return previous.doRestoreTo(size - 1, saveCount);
+        }
+    }
+
+    private GcSnapshot doRestore() {
+        // if this snapshot does not save everything, then set the previous snapshot
+        // to this snapshot content
+        if (mPrevious != null) {
+            // didn't save the matrix? set the current matrix on the previous snapshot
+            if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
+                mPrevious.mGraphics2D.setTransform(getTransform());
+            }
+
+            // didn't save the clip? set the current clip on the previous snapshot
+            if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
+                mPrevious.mGraphics2D.setClip(mGraphics2D.getClip());
+            }
+        }
+
+        if (mGraphics2D != null) {
+            mGraphics2D.dispose();
+        }
+
+        return mPrevious;
+    }
+
+}
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 0310420..030048f 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -37,19 +37,29 @@
     static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
     static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
     static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;
-    
+
     public native static String getErrorString(int errorCode);
 
     public native static boolean loadDriver();
 
     public native static boolean isDriverLoaded();
-    
+
     public native static boolean unloadDriver();
 
     public native static boolean startSupplicant();
-    
+
+    /* Does a graceful shutdown of supplicant.
+     *
+     * Note that underneath we use a harsh-sounding "terminate" supplicant command
+     * for a graceful stop and a mild-sounding "stop" interface
+     * to kill the process
+     */
     public native static boolean stopSupplicant();
 
+    /* Sends a kill signal to supplicant. To be used when we have lost connection
+       or when the supplicant is hung */
+    public native static boolean killSupplicant();
+
     public native static boolean connectToSupplicant();
 
     public native static void closeSupplicantConnection();
@@ -57,7 +67,7 @@
     public native static boolean pingCommand();
 
     public native static boolean scanCommand(boolean forceActive);
-    
+
     public native static boolean setScanModeCommand(boolean setActive);
 
     public native static String listNetworksCommand();
@@ -71,7 +81,7 @@
     public native static boolean removeNetworkCommand(int netId);
 
     public native static boolean enableNetworkCommand(int netId, boolean disableOthers);
-    
+
     public native static boolean disableNetworkCommand(int netId);
 
     public native static boolean reconnectCommand();
@@ -119,7 +129,7 @@
 
     /**
      * Sets the bluetooth coexistence mode.
-     * 
+     *
      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
@@ -136,7 +146,7 @@
      * @return {@code true} if the command succeeded, {@code false} otherwise.
      */
     public native static boolean setBluetoothCoexistenceScanModeCommand(boolean setCoexScanMode);
-    
+
     public native static boolean saveConfigCommand();
 
     public native static boolean reloadConfigCommand();
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 5474b3f..17a35c4 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -127,6 +127,18 @@
     private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
 
     /**
+     * Delay between supplicant restarts upon failure to establish connection
+     */
+    private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
+
+    /**
+     * Number of times we attempt to restart supplicant
+     */
+    private static final int SUPPLICANT_RESTART_TRIES = 5;
+
+    private int mSupplicantRestartCount = 0;
+
+    /**
      * Instance of the bluetooth headset helper. This needs to be created
      * early because there is a delay before it actually 'connects', as
      * noted by its javadoc. If we check before it is connected, it will be
@@ -365,10 +377,11 @@
     /* Driver loaded */
     private HierarchicalState mDriverLoadedState = new DriverLoadedState();
     /* Driver loaded, waiting for supplicant to start */
-    private HierarchicalState mWaitForSupState = new WaitForSupState();
-
+    private HierarchicalState mSupplicantStartingState = new SupplicantStartingState();
     /* Driver loaded and supplicant ready */
-    private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
+    private HierarchicalState mSupplicantStartedState = new SupplicantStartedState();
+    /* Waiting for supplicant to stop and monitor to exit */
+    private HierarchicalState mSupplicantStoppingState = new SupplicantStoppingState();
     /* Driver start issued, waiting for completed event */
     private HierarchicalState mDriverStartingState = new DriverStartingState();
     /* Driver started */
@@ -513,10 +526,10 @@
                 addState(mDriverFailedState, mDriverUnloadedState);
             addState(mDriverLoadingState, mDefaultState);
             addState(mDriverLoadedState, mDefaultState);
-                addState(mWaitForSupState, mDriverLoadedState);
-            addState(mDriverSupReadyState, mDefaultState);
-                addState(mDriverStartingState, mDriverSupReadyState);
-                addState(mDriverStartedState, mDriverSupReadyState);
+            addState(mSupplicantStartingState, mDefaultState);
+            addState(mSupplicantStartedState, mDefaultState);
+                addState(mDriverStartingState, mSupplicantStartedState);
+                addState(mDriverStartedState, mSupplicantStartedState);
                     addState(mScanModeState, mDriverStartedState);
                     addState(mConnectModeState, mDriverStartedState);
                         addState(mConnectingState, mConnectModeState);
@@ -524,8 +537,9 @@
                         addState(mDisconnectingState, mConnectModeState);
                         addState(mDisconnectedState, mConnectModeState);
                         addState(mWaitForWpsCompletionState, mConnectModeState);
-                addState(mDriverStoppingState, mDriverSupReadyState);
-                addState(mDriverStoppedState, mDriverSupReadyState);
+                addState(mDriverStoppingState, mSupplicantStartedState);
+                addState(mDriverStoppedState, mSupplicantStartedState);
+            addState(mSupplicantStoppingState, mDefaultState);
             addState(mSoftApStartedState, mDefaultState);
 
         setInitialState(mInitialState);
@@ -1742,7 +1756,7 @@
                         Log.d(TAG, "Supplicant start successful");
                         mWifiMonitor.startMonitoring();
                         setWifiState(WIFI_STATE_ENABLED);
-                        transitionTo(mWaitForSupState);
+                        transitionTo(mSupplicantStartingState);
                     } else {
                         Log.e(TAG, "Failed to start supplicant!");
                         sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
@@ -1888,7 +1902,7 @@
     }
 
 
-    class WaitForSupState extends HierarchicalState {
+    class SupplicantStartingState extends HierarchicalState {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1900,6 +1914,7 @@
             switch(message.what) {
                 case SUP_CONNECTION_EVENT:
                     Log.d(TAG, "Supplicant connection established");
+                    mSupplicantRestartCount = 0;
                     mSupplicantStateTracker.resetSupplicantState();
                     /* Initialize data structures */
                     mLastBssid = null;
@@ -1919,10 +1934,18 @@
                     transitionTo(mDriverStartedState);
                     break;
                 case SUP_DISCONNECTION_EVENT:
-                    Log.e(TAG, "Failed to setup control channel, restart supplicant");
-                    WifiNative.stopSupplicant();
-                    transitionTo(mDriverLoadedState);
-                    sendMessageAtFrontOfQueue(CMD_START_SUPPLICANT);
+                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
+                        Log.e(TAG, "Failed to setup control channel, restart supplicant");
+                        WifiNative.killSupplicant();
+                        transitionTo(mDriverLoadedState);
+                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
+                    } else {
+                        mSupplicantRestartCount = 0;
+                        Log.e(TAG, "Failed " + mSupplicantRestartCount +
+                                " times to start supplicant, unload driver");
+                        transitionTo(mDriverLoadedState);
+                        sendMessage(CMD_UNLOAD_DRIVER);
+                    }
                     break;
                 case CMD_LOAD_DRIVER:
                 case CMD_UNLOAD_DRIVER:
@@ -1951,7 +1974,7 @@
         }
     }
 
-    class DriverSupReadyState extends HierarchicalState {
+    class SupplicantStartedState extends HierarchicalState {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1966,23 +1989,26 @@
             switch(message.what) {
                 case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    Log.d(TAG, "Stop supplicant received");
-                    WifiNative.closeSupplicantConnection();
-                    WifiNative.stopSupplicant();
+                    Log.d(TAG, "stopping supplicant");
+                    if (!WifiNative.stopSupplicant()) {
+                        Log.e(TAG, "Failed to stop supplicant, issue kill");
+                        WifiNative.killSupplicant();
+                    }
                     handleNetworkDisconnect();
                     sendSupplicantConnectionChangedBroadcast(false);
                     mSupplicantStateTracker.resetSupplicantState();
-                    transitionTo(mDriverLoadedState);
+                    transitionTo(mSupplicantStoppingState);
                     break;
-                case SUP_DISCONNECTION_EVENT:  /* Supplicant died */
+                case SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    Log.e(TAG, "Supplicant died, restarting");
+                    Log.e(TAG, "Connection lost, restart supplicant");
+                    WifiNative.killSupplicant();
                     WifiNative.closeSupplicantConnection();
                     handleNetworkDisconnect();
                     sendSupplicantConnectionChangedBroadcast(false);
                     mSupplicantStateTracker.resetSupplicantState();
                     transitionTo(mDriverLoadedState);
-                    sendMessageAtFrontOfQueue(CMD_START_SUPPLICANT); /* restart */
+                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     break;
                 case SCAN_RESULTS_EVENT:
                     setScanResults(WifiNative.scanResultsCommand());
@@ -2058,6 +2084,51 @@
         }
     }
 
+    class SupplicantStoppingState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch(message.what) {
+                case SUP_CONNECTION_EVENT:
+                    Log.e(TAG, "Supplicant connection received while stopping");
+                    break;
+                case SUP_DISCONNECTION_EVENT:
+                    Log.d(TAG, "Supplicant connection lost");
+                    WifiNative.closeSupplicantConnection();
+                    transitionTo(mDriverLoadedState);
+                    break;
+                case CMD_LOAD_DRIVER:
+                case CMD_UNLOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_STOP_SUPPLICANT:
+                case CMD_START_AP:
+                case CMD_STOP_AP:
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_BLUETOOTH_COEXISTENCE:
+                case CMD_SET_BLUETOOTH_SCAN_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_SET_FREQUENCY_BAND:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
     class DriverStartingState extends HierarchicalState {
         @Override
         public void enter() {
@@ -2113,6 +2184,8 @@
             setCountryCode();
             /* set frequency band of operation */
             setFrequencyBand();
+            /* initialize network state */
+            setNetworkDetailedState(DetailedState.DISCONNECTED);
 
             if (mIsScanMode) {
                 WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);