Merge "Fix periodic notification timing error for AudioRecord"
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index 277b5db..d1d63d5 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -208,14 +208,11 @@
         return reply.readInt32();
     }
 
-    virtual status_t createStream(int width, int height, int format,
+    virtual status_t createStream(
                           const sp<IGraphicBufferProducer>& bufferProducer)
     {
         Parcel data, reply;
         data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
-        data.writeInt32(width);
-        data.writeInt32(height);
-        data.writeInt32(format);
 
         data.writeInt32(1); // marker that bufferProducer is not null
         data.writeString16(String16("unknown_name")); // name of surface
@@ -396,14 +393,6 @@
         } break;
         case CREATE_STREAM: {
             CHECK_INTERFACE(ICameraDeviceUser, data, reply);
-            int width, height, format;
-
-            width = data.readInt32();
-            ALOGV("%s: CREATE_STREAM: width = %d", __FUNCTION__, width);
-            height = data.readInt32();
-            ALOGV("%s: CREATE_STREAM: height = %d", __FUNCTION__, height);
-            format = data.readInt32();
-            ALOGV("%s: CREATE_STREAM: format = %d", __FUNCTION__, format);
 
             sp<IGraphicBufferProducer> bp;
             if (data.readInt32() != 0) {
@@ -419,7 +408,7 @@
             }
 
             status_t ret;
-            ret = createStream(width, height, format, bp);
+            ret = createStream(bp);
 
             reply->writeNoException();
             ALOGV("%s: CREATE_STREAM: write noException", __FUNCTION__);
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
index 4b2f980..ac1a547 100644
--- a/cmds/stagefright/SimplePlayer.cpp
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -59,14 +59,14 @@
     return err;
 }
 status_t SimplePlayer::setDataSource(const char *path) {
-    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
     msg->setString("path", path);
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t SimplePlayer::setSurface(const sp<IGraphicBufferProducer> &bufferProducer) {
-    sp<AMessage> msg = new AMessage(kWhatSetSurface, id());
+    sp<AMessage> msg = new AMessage(kWhatSetSurface, this);
 
     sp<Surface> surface;
     if (bufferProducer != NULL) {
@@ -81,25 +81,25 @@
 }
 
 status_t SimplePlayer::prepare() {
-    sp<AMessage> msg = new AMessage(kWhatPrepare, id());
+    sp<AMessage> msg = new AMessage(kWhatPrepare, this);
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t SimplePlayer::start() {
-    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    sp<AMessage> msg = new AMessage(kWhatStart, this);
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t SimplePlayer::stop() {
-    sp<AMessage> msg = new AMessage(kWhatStop, id());
+    sp<AMessage> msg = new AMessage(kWhatStop, this);
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t SimplePlayer::reset() {
-    sp<AMessage> msg = new AMessage(kWhatReset, id());
+    sp<AMessage> msg = new AMessage(kWhatReset, this);
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
@@ -116,7 +116,7 @@
                 mState = UNPREPARED;
             }
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             sp<AMessage> response = new AMessage;
@@ -139,7 +139,7 @@
                 err = OK;
             }
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             sp<AMessage> response = new AMessage;
@@ -161,7 +161,7 @@
                 }
             }
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             sp<AMessage> response = new AMessage;
@@ -194,7 +194,7 @@
                 }
             }
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             sp<AMessage> response = new AMessage;
@@ -217,7 +217,7 @@
                 }
             }
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             sp<AMessage> response = new AMessage;
@@ -240,7 +240,7 @@
                 mState = UNINITIALIZED;
             }
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             sp<AMessage> response = new AMessage;
@@ -382,7 +382,7 @@
 
     mStartTimeRealUs = -1ll;
 
-    sp<AMessage> msg = new AMessage(kWhatDoMoreStuff, id());
+    sp<AMessage> msg = new AMessage(kWhatDoMoreStuff, this);
     msg->setInt32("generation", ++mDoMoreStuffGeneration);
     msg->post();
 
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 0f729a3..172dc36 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -72,7 +72,7 @@
     }
 
     void startAsync() {
-        (new AMessage(kWhatStart, id()))->post();
+        (new AMessage(kWhatStart, this))->post();
     }
 
 protected:
@@ -100,7 +100,7 @@
         if (ctrlc) {
             printf("\n");
             printStatistics();
-            (new AMessage(kWhatStop, id()))->post();
+            (new AMessage(kWhatStop, this))->post();
             ctrlc = false;
         }
         switch (msg->what()) {
@@ -149,7 +149,7 @@
                 mDecodeLooper->registerHandler(mCodec);
 
                 mCodec->setNotificationMessage(
-                        new AMessage(kWhatCodecNotify, id()));
+                        new AMessage(kWhatCodecNotify, this));
 
                 sp<AMessage> format = makeFormat(mSource->getFormat());
 
@@ -168,7 +168,7 @@
                 mFinalResult = OK;
                 mSeekState = SEEK_NONE;
 
-                // (new AMessage(kWhatSeek, id()))->post(5000000ll);
+                // (new AMessage(kWhatSeek, this))->post(5000000ll);
                 break;
             }
 
@@ -225,12 +225,12 @@
                     printf((what == CodecBase::kWhatEOS) ? "$\n" : "E\n");
 
                     printStatistics();
-                    (new AMessage(kWhatStop, id()))->post();
+                    (new AMessage(kWhatStop, this))->post();
                 } else if (what == CodecBase::kWhatFlushCompleted) {
                     mSeekState = SEEK_FLUSH_COMPLETED;
                     mCodec->signalResume();
 
-                    (new AMessage(kWhatSeek, id()))->post(5000000ll);
+                    (new AMessage(kWhatSeek, this))->post(5000000ll);
                 } else if (what == CodecBase::kWhatOutputFormatChanged) {
                 } else if (what == CodecBase::kWhatShutdownCompleted) {
                     mDecodeLooper->unregisterHandler(mCodec->id());
diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h
index 35488bb..bfc2aa0 100644
--- a/include/camera/camera2/ICameraDeviceUser.h
+++ b/include/camera/camera2/ICameraDeviceUser.h
@@ -101,7 +101,6 @@
 
     virtual status_t        deleteStream(int streamId) = 0;
     virtual status_t        createStream(
-            int width, int height, int format,
             const sp<IGraphicBufferProducer>& bufferProducer) = 0;
 
     // Create a request object from a template.
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index f70d981..a68a9cb 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -88,8 +88,8 @@
      * user:    Pointer to context for use by the callback receiver.
      * info:    Pointer to optional parameter according to event type:
      *          - EVENT_MORE_DATA: pointer to AudioRecord::Buffer struct. The callback must not read
-     *            more bytes than indicated by 'size' field and update 'size' if fewer bytes are
-     *            consumed.
+     *                             more bytes than indicated by 'size' field and update 'size' if
+     *                             fewer bytes are consumed.
      *          - EVENT_OVERRUN: unused.
      *          - EVENT_MARKER: pointer to const uint32_t containing the marker position in frames.
      *          - EVENT_NEW_POS: pointer to const uint32_t containing the new position in frames.
@@ -106,6 +106,7 @@
      *  - BAD_VALUE: unsupported configuration
      * frameCount is guaranteed to be non-zero if status is NO_ERROR,
      * and is undefined otherwise.
+     * FIXME This API assumes a route, and so should be deprecated.
      */
 
      static status_t getMinFrameCount(size_t* frameCount,
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 2ab3dd6..ad5d6ed 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -98,10 +98,13 @@
 
     // Returned samplingRate and frameCount output values are guaranteed
     // to be non-zero if status == NO_ERROR
+    // FIXME This API assumes a route, and so should be deprecated.
     static status_t getOutputSamplingRate(uint32_t* samplingRate,
             audio_stream_type_t stream);
+    // FIXME This API assumes a route, and so should be deprecated.
     static status_t getOutputFrameCount(size_t* frameCount,
             audio_stream_type_t stream);
+    // FIXME This API assumes a route, and so should be deprecated.
     static status_t getOutputLatency(uint32_t* latency,
             audio_stream_type_t stream);
     static status_t getSamplingRate(audio_io_handle_t output,
@@ -110,19 +113,20 @@
     // audio_stream->get_buffer_size()/audio_stream_out_frame_size()
     static status_t getFrameCount(audio_io_handle_t output,
                                   size_t* frameCount);
-    // returns the audio output stream latency in ms. Corresponds to
+    // returns the audio output latency in ms. Corresponds to
     // audio_stream_out->get_latency()
     static status_t getLatency(audio_io_handle_t output,
                                uint32_t* latency);
 
     // return status NO_ERROR implies *buffSize > 0
+    // FIXME This API assumes a route, and so should deprecated.
     static status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
         audio_channel_mask_t channelMask, size_t* buffSize);
 
     static status_t setVoiceVolume(float volume);
 
     // return the number of audio frames written by AudioFlinger to audio HAL and
-    // audio dsp to DAC since the specified output I/O handle has exited standby.
+    // audio dsp to DAC since the specified output has exited standby.
     // returned status (from utils/Errors.h) can be:
     // - NO_ERROR: successful operation, halFrames and dspFrames point to valid data
     // - INVALID_OPERATION: Not supported on current hardware platform
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 2e1ed6c..3de0774 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -63,7 +63,7 @@
                                     // See AudioTimestamp for the information included with event.
     };
 
-    /* Client should declare Buffer on the stack and pass address to obtainBuffer()
+    /* Client should declare a Buffer and pass the address to obtainBuffer()
      * and releaseBuffer().  See also callback_t for EVENT_MORE_DATA.
      */
 
@@ -72,14 +72,20 @@
     public:
         // FIXME use m prefix
         size_t      frameCount;   // number of sample frames corresponding to size;
-                                  // on input it is the number of frames desired,
-                                  // on output is the number of frames actually filled
-                                  // (currently ignored, but will make the primary field in future)
+                                  // on input to obtainBuffer() it is the number of frames desired,
+                                  // on output from obtainBuffer() it is the number of available
+                                  //    [empty slots for] frames to be filled
+                                  // on input to releaseBuffer() it is currently ignored
 
         size_t      size;         // input/output in bytes == frameCount * frameSize
-                                  // on input it is unused
-                                  // on output is the number of bytes actually filled
-                                  // FIXME this is redundant with respect to frameCount.
+                                  // on input to obtainBuffer() it is ignored
+                                  // on output from obtainBuffer() it is the number of available
+                                  //    [empty slots for] bytes to be filled,
+                                  //    which is frameCount * frameSize
+                                  // on input to releaseBuffer() it is the number of bytes to
+                                  //    release
+                                  // FIXME This is redundant with respect to frameCount.  Consider
+                                  //    removing size and making frameCount the primary field.
 
         union {
             void*       raw;
@@ -484,10 +490,18 @@
      */
             status_t    attachAuxEffect(int effectId);
 
-    /* Obtains a buffer of up to "audioBuffer->frameCount" empty slots for frames.
+    /* Public API for TRANSFER_OBTAIN mode.
+     * Obtains a buffer of up to "audioBuffer->frameCount" empty slots for frames.
      * After filling these slots with data, the caller should release them with releaseBuffer().
      * If the track buffer is not full, obtainBuffer() returns as many contiguous
      * [empty slots for] frames as are available immediately.
+     *
+     * If nonContig is non-NULL, it is an output parameter that will be set to the number of
+     * additional non-contiguous frames that are predicted to be available immediately,
+     * if the client were to release the first frames and then call obtainBuffer() again.
+     * This value is only a prediction, and needs to be confirmed.
+     * It will be set to zero for an error return.
+     *
      * If the track buffer is full and track is stopped, obtainBuffer() returns WOULD_BLOCK
      * regardless of the value of waitCount.
      * If the track buffer is full and track is not stopped, obtainBuffer() blocks with a
@@ -496,7 +510,6 @@
      * is exhausted, at which point obtainBuffer() will either block
      * or return WOULD_BLOCK depending on the value of the "waitCount"
      * parameter.
-     * Each sample is 16-bit signed PCM.
      *
      * obtainBuffer() and releaseBuffer() are deprecated for direct use by applications,
      * which should use write() or callback EVENT_MORE_DATA instead.
@@ -508,24 +521,29 @@
      *
      * Buffer fields
      * On entry:
-     *  frameCount  number of frames requested
+     *  frameCount  number of [empty slots for] frames requested
+     *  size        ignored
+     *  raw         ignored
      * After error return:
      *  frameCount  0
      *  size        0
      *  raw         undefined
      * After successful return:
-     *  frameCount  actual number of frames available, <= number requested
+     *  frameCount  actual number of [empty slots for] frames available, <= number requested
      *  size        actual number of bytes available
      *  raw         pointer to the buffer
      */
-
     /* FIXME Deprecated public API for TRANSFER_OBTAIN mode */
-            status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
+            status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
+                                size_t *nonContig = NULL)
                                 __attribute__((__deprecated__));
 
 private:
     /* If nonContig is non-NULL, it is an output parameter that will be set to the number of
-     * additional non-contiguous frames that are available immediately.
+     * additional non-contiguous frames that are predicted to be available immediately,
+     * if the client were to release the first frames and then call obtainBuffer() again.
+     * This value is only a prediction, and needs to be confirmed.
+     * It will be set to zero for an error return.
      * FIXME We could pass an array of Buffers instead of only one Buffer to obtainBuffer(),
      * in case the requested amount of frames is in two or more non-contiguous regions.
      * FIXME requested and elapsed are both relative times.  Consider changing to absolute time.
@@ -534,9 +552,17 @@
                                      struct timespec *elapsed = NULL, size_t *nonContig = NULL);
 public:
 
-    /* Release a filled buffer of "audioBuffer->frameCount" frames for AudioFlinger to process. */
+    /* Public API for TRANSFER_OBTAIN mode.
+     * Release a filled buffer of frames for AudioFlinger to process.
+     *
+     * Buffer fields:
+     *  frameCount  currently ignored but recommend to set to actual number of frames filled
+     *  size        actual number of bytes filled, must be multiple of frameSize
+     *  raw         ignored
+     *
+     */
     // FIXME make private when obtainBuffer() for TRANSFER_OBTAIN is removed
-            void        releaseBuffer(Buffer* audioBuffer);
+            void        releaseBuffer(const Buffer* audioBuffer);
 
     /* As a convenience we provide a write() interface to the audio buffer.
      * Input parameter 'size' is in byte units.
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 31a14f0..f927a80 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -94,6 +94,8 @@
                                 sp<IMemory>& buffers,   // return value 0 means it follows cblk
                                 status_t *status) = 0;
 
+    // FIXME Surprisingly, sampleRate/format/frameCount/latency don't work for input handles
+
     /* query the audio hardware state. This state never changes,
      * and therefore can be cached.
      */
@@ -142,6 +144,7 @@
     virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
 
     // retrieve the audio recording buffer size
+    // FIXME This API assumes a route, and so should be deprecated.
     virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
             audio_channel_mask_t channelMask) const = 0;
 
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 442c861..aa91485 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -299,6 +299,8 @@
     status_t setupRawAudioFormat(
             OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels);
 
+    status_t setPriority(int32_t priority);
+
     status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
 
     status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
diff --git a/include/media/stagefright/BufferProducerWrapper.h b/include/media/stagefright/BufferProducerWrapper.h
index d8acf30..4caa2c6 100644
--- a/include/media/stagefright/BufferProducerWrapper.h
+++ b/include/media/stagefright/BufferProducerWrapper.h
@@ -19,6 +19,7 @@
 #define BUFFER_PRODUCER_WRAPPER_H_
 
 #include <gui/IGraphicBufferProducer.h>
+#include <media/stagefright/foundation/ABase.h>
 
 namespace android {
 
diff --git a/media/libmediaplayerservice/nuplayer/MediaClock.h b/include/media/stagefright/MediaClock.h
similarity index 100%
rename from media/libmediaplayerservice/nuplayer/MediaClock.h
rename to include/media/stagefright/MediaClock.h
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index d448097..8241e19 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -27,6 +27,7 @@
 
 struct ABuffer;
 struct AMessage;
+struct AReplyToken;
 struct AString;
 struct CodecBase;
 struct ICrypto;
@@ -222,7 +223,7 @@
     sp<ALooper> mCodecLooper;
     sp<CodecBase> mCodec;
     AString mComponentName;
-    uint32_t mReplyID;
+    sp<AReplyToken> mReplyID;
     uint32_t mFlags;
     status_t mStickyError;
     sp<Surface> mNativeWindow;
@@ -249,10 +250,10 @@
     Vector<BufferInfo> mPortBuffers[2];
 
     int32_t mDequeueInputTimeoutGeneration;
-    uint32_t mDequeueInputReplyID;
+    sp<AReplyToken> mDequeueInputReplyID;
 
     int32_t mDequeueOutputTimeoutGeneration;
-    uint32_t mDequeueOutputReplyID;
+    sp<AReplyToken> mDequeueOutputReplyID;
 
     sp<ICrypto> mCrypto;
 
@@ -267,7 +268,7 @@
     static status_t PostAndAwaitResponse(
             const sp<AMessage> &msg, sp<AMessage> *response);
 
-    static void PostReplyWithError(int32_t replyID, int32_t err);
+    static void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
 
     status_t init(const AString &name, bool nameIsType, bool encoder);
 
@@ -283,8 +284,8 @@
             size_t portIndex, size_t index,
             sp<ABuffer> *buffer, sp<AMessage> *format);
 
-    bool handleDequeueInputBuffer(uint32_t replyID, bool newRequest = false);
-    bool handleDequeueOutputBuffer(uint32_t replyID, bool newRequest = false);
+    bool handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest = false);
+    bool handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest = false);
     void cancelPendingDequeueOperations();
 
     void extractCSD(const sp<AMessage> &format);
diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h
index 0970b2b..7b8f59d 100644
--- a/include/media/stagefright/MediaCodecSource.h
+++ b/include/media/stagefright/MediaCodecSource.h
@@ -25,6 +25,7 @@
 
 class ALooper;
 class AMessage;
+struct AReplyToken;
 class IGraphicBufferProducer;
 class MediaCodec;
 class MetaData;
@@ -99,7 +100,7 @@
     sp<Puller> mPuller;
     sp<MediaCodec> mEncoder;
     uint32_t mFlags;
-    List<uint32_t> mStopReplyIDQueue;
+    List<sp<AReplyToken>> mStopReplyIDQueue;
     bool mIsVideo;
     bool mStarted;
     bool mStopping;
diff --git a/include/media/stagefright/ProcessInfo.h b/include/media/stagefright/ProcessInfo.h
new file mode 100644
index 0000000..ec0cdff
--- /dev/null
+++ b/include/media/stagefright/ProcessInfo.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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 PROCESS_INFO_H_
+
+#define PROCESS_INFO_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/ProcessInfoInterface.h>
+
+namespace android {
+
+struct ProcessInfo : public ProcessInfoInterface {
+    ProcessInfo();
+
+    virtual bool getPriority(int pid, int* priority);
+
+protected:
+    virtual ~ProcessInfo();
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo);
+};
+
+}  // namespace android
+
+#endif  // PROCESS_INFO_H_
diff --git a/include/media/stagefright/ProcessInfoInterface.h b/include/media/stagefright/ProcessInfoInterface.h
new file mode 100644
index 0000000..222f92d
--- /dev/null
+++ b/include/media/stagefright/ProcessInfoInterface.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 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 PROCESS_INFO_INTERFACE_H_
+#define PROCESS_INFO_INTERFACE_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ProcessInfoInterface : public RefBase {
+    virtual bool getPriority(int pid, int* priority) = 0;
+
+protected:
+    virtual ~ProcessInfoInterface() {}
+};
+
+}  // namespace android
+
+#endif  // PROCESS_INFO_INTERFACE_H_
diff --git a/include/media/stagefright/foundation/ABase.h b/include/media/stagefright/foundation/ABase.h
index 72e3d87..ef1e010 100644
--- a/include/media/stagefright/foundation/ABase.h
+++ b/include/media/stagefright/foundation/ABase.h
@@ -18,7 +18,9 @@
 
 #define A_BASE_H_
 
+#ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+#endif
 
 #define DISALLOW_EVIL_CONSTRUCTORS(name) \
     name(const name &); \
diff --git a/include/media/stagefright/foundation/AHandler.h b/include/media/stagefright/foundation/AHandler.h
index 41ade77..fe02a86 100644
--- a/include/media/stagefright/foundation/AHandler.h
+++ b/include/media/stagefright/foundation/AHandler.h
@@ -29,6 +29,7 @@
 struct AHandler : public RefBase {
     AHandler()
         : mID(0),
+          mVerboseStats(false),
           mMessageCounter(0) {
     }
 
@@ -36,23 +37,40 @@
         return mID;
     }
 
-    sp<ALooper> looper();
+    sp<ALooper> looper() const {
+        return mLooper.promote();
+    }
+
+    wp<ALooper> getLooper() const {
+        return mLooper;
+    }
+
+    wp<AHandler> getHandler() const {
+        // allow getting a weak reference to a const handler
+        return const_cast<AHandler *>(this);
+    }
 
 protected:
     virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
 
 private:
-    friend struct ALooperRoster;
+    friend struct AMessage;      // deliverMessage()
+    friend struct ALooperRoster; // setID()
 
     ALooper::handler_id mID;
+    wp<ALooper> mLooper;
 
-    void setID(ALooper::handler_id id) {
+    inline void setID(ALooper::handler_id id, wp<ALooper> looper) {
         mID = id;
+        mLooper = looper;
     }
 
+    bool mVerboseStats;
     uint32_t mMessageCounter;
     KeyedVector<uint32_t, uint32_t> mMessages;
 
+    void deliverMessage(const sp<AMessage> &msg);
+
     DISALLOW_EVIL_CONSTRUCTORS(AHandler);
 };
 
diff --git a/include/media/stagefright/foundation/ALooper.h b/include/media/stagefright/foundation/ALooper.h
index 70e0c5e..09c469b 100644
--- a/include/media/stagefright/foundation/ALooper.h
+++ b/include/media/stagefright/foundation/ALooper.h
@@ -30,6 +30,7 @@
 
 struct AHandler;
 struct AMessage;
+struct AReplyToken;
 
 struct ALooper : public RefBase {
     typedef int32_t event_id;
@@ -53,11 +54,15 @@
 
     static int64_t GetNowUs();
 
+    const char *getName() const {
+        return mName.c_str();
+    }
+
 protected:
     virtual ~ALooper();
 
 private:
-    friend struct ALooperRoster;
+    friend struct AMessage;       // post()
 
     struct Event {
         int64_t mWhenUs;
@@ -75,12 +80,32 @@
     sp<LooperThread> mThread;
     bool mRunningLocally;
 
+    // use a separate lock for reply handling, as it is always on another thread
+    // use a central lock, however, to avoid creating a mutex for each reply
+    Mutex mRepliesLock;
+    Condition mRepliesCondition;
+
+    // START --- methods used only by AMessage
+
+    // posts a message on this looper with the given timeout
     void post(const sp<AMessage> &msg, int64_t delayUs);
+
+    // creates a reply token to be used with this looper
+    sp<AReplyToken> createReplyToken();
+    // waits for a response for the reply token.  If status is OK, the response
+    // is stored into the supplied variable.  Otherwise, it is unchanged.
+    status_t awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response);
+    // posts a reply for a reply token.  If the reply could be successfully posted,
+    // it returns OK. Otherwise, it returns an error value.
+    status_t postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &msg);
+
+    // END --- methods used only by AMessage
+
     bool loop();
 
     DISALLOW_EVIL_CONSTRUCTORS(ALooper);
 };
 
-}  // namespace android
+} // namespace android
 
 #endif  // A_LOOPER_H_
diff --git a/include/media/stagefright/foundation/ALooperRoster.h b/include/media/stagefright/foundation/ALooperRoster.h
index a0be8eb..9912455 100644
--- a/include/media/stagefright/foundation/ALooperRoster.h
+++ b/include/media/stagefright/foundation/ALooperRoster.h
@@ -33,16 +33,6 @@
     void unregisterHandler(ALooper::handler_id handlerID);
     void unregisterStaleHandlers();
 
-    status_t postMessage(const sp<AMessage> &msg, int64_t delayUs = 0);
-    void deliverMessage(const sp<AMessage> &msg);
-
-    status_t postAndAwaitResponse(
-            const sp<AMessage> &msg, sp<AMessage> *response);
-
-    void postReply(uint32_t replyID, const sp<AMessage> &reply);
-
-    sp<ALooper> findLooper(ALooper::handler_id handlerID);
-
     void dump(int fd, const Vector<String16>& args);
 
 private:
@@ -54,10 +44,6 @@
     Mutex mLock;
     KeyedVector<ALooper::handler_id, HandlerInfo> mHandlers;
     ALooper::handler_id mNextHandlerID;
-    uint32_t mNextReplyID;
-    Condition mRepliesCondition;
-
-    KeyedVector<uint32_t, sp<AMessage> > mReplies;
 
     DISALLOW_EVIL_CONSTRUCTORS(ALooperRoster);
 };
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index a9e235b..4c6bd21 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -26,11 +26,41 @@
 namespace android {
 
 struct ABuffer;
+struct AHandler;
 struct AString;
 struct Parcel;
 
+struct AReplyToken : public RefBase {
+    AReplyToken(const sp<ALooper> &looper)
+        : mLooper(looper),
+          mReplied(false) {
+    }
+
+private:
+    friend struct AMessage;
+    friend struct ALooper;
+    wp<ALooper> mLooper;
+    sp<AMessage> mReply;
+    bool mReplied;
+
+    sp<ALooper> getLooper() const {
+        return mLooper.promote();
+    }
+    // if reply is not set, returns false; otherwise, it retrieves the reply and returns true
+    bool retrieveReply(sp<AMessage> *reply) {
+        if (mReplied) {
+            *reply = mReply;
+            mReply.clear();
+        }
+        return mReplied;
+    }
+    // sets the reply for this token. returns OK or error
+    status_t setReply(const sp<AMessage> &reply);
+};
+
 struct AMessage : public RefBase {
-    AMessage(uint32_t what = 0, ALooper::handler_id target = 0);
+    AMessage();
+    AMessage(uint32_t what, const sp<const AHandler> &handler);
 
     static sp<AMessage> FromParcel(const Parcel &parcel);
     void writeToParcel(Parcel *parcel) const;
@@ -38,8 +68,7 @@
     void setWhat(uint32_t what);
     uint32_t what() const;
 
-    void setTarget(ALooper::handler_id target);
-    ALooper::handler_id target() const;
+    void setTarget(const sp<const AHandler> &handler);
 
     void clear();
 
@@ -76,18 +105,22 @@
             const char *name,
             int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
 
-    void post(int64_t delayUs = 0);
+    status_t post(int64_t delayUs = 0);
 
     // Posts the message to its target and waits for a response (or error)
     // before returning.
     status_t postAndAwaitResponse(sp<AMessage> *response);
 
     // If this returns true, the sender of this message is synchronously
-    // awaiting a response, the "replyID" can be used to send the response
-    // via "postReply" below.
-    bool senderAwaitsResponse(uint32_t *replyID) const;
+    // awaiting a response and the reply token is consumed from the message
+    // and stored into replyID. The reply token must be used to send the response
+    // using "postReply" below.
+    bool senderAwaitsResponse(sp<AReplyToken> *replyID);
 
-    void postReply(uint32_t replyID);
+    // Posts the message as a response to a reply token.  A reply token can
+    // only be used once. Returns OK if the response could be posted; otherwise,
+    // an error.
+    status_t postReply(const sp<AReplyToken> &replyID);
 
     // Performs a deep-copy of "this", contained messages are in turn "dup'ed".
     // Warning: RefBase items, i.e. "objects" are _not_ copied but only have
@@ -117,9 +150,16 @@
     virtual ~AMessage();
 
 private:
+    friend struct ALooper; // deliver()
+
     uint32_t mWhat;
+
+    // used only for debugging
     ALooper::handler_id mTarget;
 
+    wp<AHandler> mHandler;
+    wp<ALooper> mLooper;
+
     struct Rect {
         int32_t mLeft, mTop, mRight, mBottom;
     };
@@ -157,6 +197,8 @@
 
     size_t findItemIndex(const char *name, size_t len) const;
 
+    void deliver();
+
     DISALLOW_EVIL_CONSTRUCTORS(AMessage);
 };
 
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 7143f1a..5644428 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -53,8 +53,8 @@
 struct AudioTrackSharedStreaming {
     // similar to NBAIO MonoPipe
     // in continuously incrementing frame units, take modulo buffer size, which must be a power of 2
-    volatile int32_t mFront;    // read by server
-    volatile int32_t mRear;     // write by client
+    volatile int32_t mFront;    // read by consumer (output: server, input: client)
+    volatile int32_t mRear;     // written by producer (output: client, input: server)
     volatile int32_t mFlush;    // incremented by client to indicate a request to flush;
                                 // server notices and discards all data between mFront and mRear
     volatile uint32_t mUnderrunFrames;  // server increments for each unavailable but desired frame
diff --git a/include/radio/IRadio.h b/include/radio/IRadio.h
new file mode 100644
index 0000000..1877f8f
--- /dev/null
+++ b/include/radio/IRadio.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_HARDWARE_IRADIO_H
+#define ANDROID_HARDWARE_IRADIO_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <system/radio.h>
+
+namespace android {
+
+class IRadio : public IInterface
+{
+public:
+
+    DECLARE_META_INTERFACE(Radio);
+
+    virtual void detach() = 0;
+
+    virtual status_t setConfiguration(const struct radio_band_config *config) = 0;
+
+    virtual status_t getConfiguration(struct radio_band_config *config) = 0;
+
+    virtual status_t setMute(bool mute) = 0;
+
+    virtual status_t getMute(bool *mute) = 0;
+
+    virtual status_t step(radio_direction_t direction, bool skipSubChannel) = 0;
+
+    virtual status_t scan(radio_direction_t direction, bool skipSubChannel) = 0;
+
+    virtual status_t tune(unsigned int channel, unsigned int subChannel) = 0;
+
+    virtual status_t cancel() = 0;
+
+    virtual status_t getProgramInformation(struct radio_program_info *info) = 0;
+
+    virtual status_t hasControl(bool *hasControl) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnRadio: public BnInterface<IRadio>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_IRADIO_H
diff --git a/include/radio/IRadioClient.h b/include/radio/IRadioClient.h
new file mode 100644
index 0000000..9062ad6
--- /dev/null
+++ b/include/radio/IRadioClient.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_HARDWARE_IRADIO_CLIENT_H
+#define ANDROID_HARDWARE_IRADIO_CLIENT_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IRadioClient : public IInterface
+{
+public:
+
+    DECLARE_META_INTERFACE(RadioClient);
+
+    virtual void onEvent(const sp<IMemory>& eventMemory) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnRadioClient : public BnInterface<IRadioClient>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_IRADIO_CLIENT_H
diff --git a/include/radio/IRadioService.h b/include/radio/IRadioService.h
new file mode 100644
index 0000000..a946dd5
--- /dev/null
+++ b/include/radio/IRadioService.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_HARDWARE_IRADIO_SERVICE_H
+#define ANDROID_HARDWARE_IRADIO_SERVICE_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <system/radio.h>
+
+namespace android {
+
+class IRadio;
+class IRadioClient;
+
+class IRadioService : public IInterface
+{
+public:
+
+    DECLARE_META_INTERFACE(RadioService);
+
+    virtual status_t listModules(struct radio_properties *properties,
+                                 uint32_t *numModules) = 0;
+
+    virtual status_t attach(const radio_handle_t handle,
+                            const sp<IRadioClient>& client,
+                            const struct radio_band_config *config,
+                            bool withAudio,
+                            sp<IRadio>& radio) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnRadioService: public BnInterface<IRadioService>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_IRADIO_SERVICE_H
diff --git a/include/radio/Radio.h b/include/radio/Radio.h
new file mode 100644
index 0000000..302bf16
--- /dev/null
+++ b/include/radio/Radio.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_HARDWARE_RADIO_H
+#define ANDROID_HARDWARE_RADIO_H
+
+#include <binder/IBinder.h>
+#include <utils/threads.h>
+#include <radio/RadioCallback.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioService.h>
+#include <radio/IRadioClient.h>
+#include <system/radio.h>
+
+namespace android {
+
+class MemoryDealer;
+
+class Radio : public BnRadioClient,
+                        public IBinder::DeathRecipient
+{
+public:
+
+    virtual ~Radio();
+
+    static  status_t listModules(struct radio_properties *properties,
+                                 uint32_t *numModules);
+    static  sp<Radio> attach(radio_handle_t handle,
+                             const struct radio_band_config *config,
+                             bool withAudio,
+                             const sp<RadioCallback>& callback);
+
+
+            void detach();
+
+            status_t setConfiguration(const struct radio_band_config *config);
+
+            status_t getConfiguration(struct radio_band_config *config);
+
+            status_t setMute(bool mute);
+
+            status_t getMute(bool *mute);
+
+            status_t step(radio_direction_t direction, bool skipSubChannel);
+
+            status_t scan(radio_direction_t direction, bool skipSubChannel);
+
+            status_t tune(unsigned int channel, unsigned int subChannel);
+
+            status_t cancel();
+
+            status_t getProgramInformation(struct radio_program_info *info);
+
+            status_t hasControl(bool *hasControl);
+
+            // BpRadioClient
+            virtual void onEvent(const sp<IMemory>& eventMemory);
+
+            //IBinder::DeathRecipient
+            virtual void binderDied(const wp<IBinder>& who);
+
+private:
+            Radio(radio_handle_t handle,
+                            const sp<RadioCallback>&);
+            static const sp<IRadioService>& getRadioService();
+
+            Mutex                   mLock;
+            sp<IRadio>              mIRadio;
+            const radio_handle_t    mHandle;
+            sp<RadioCallback>       mCallback;
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_RADIO_H
diff --git a/include/radio/RadioCallback.h b/include/radio/RadioCallback.h
new file mode 100644
index 0000000..4a7f1a6
--- /dev/null
+++ b/include/radio/RadioCallback.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_HARDWARE_RADIO_CALLBACK_H
+#define ANDROID_HARDWARE_RADIO_CALLBACK_H
+
+#include <utils/RefBase.h>
+#include <system/radio.h>
+
+namespace android {
+
+class RadioCallback : public RefBase
+{
+public:
+
+            RadioCallback() {}
+    virtual ~RadioCallback() {}
+
+    virtual void onEvent(struct radio_event *event) = 0;
+
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_RADIO_CALLBACK_H
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 1d5fc95..c775e7b 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -299,6 +299,9 @@
         ALOGV("Building AudioTrack with attributes: usage=%d content=%d flags=0x%x tags=[%s]",
                 mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags);
         mStreamType = AUDIO_STREAM_DEFAULT;
+        if ((mAttributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
+            flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
+        }
     }
 
     // these below should probably come from the audioFlinger too...
@@ -1002,7 +1005,9 @@
             // use case 1: shared buffer
             (mSharedBuffer != 0) ||
             // use case 2: callback transfer mode
-            (mTransfer == TRANSFER_CALLBACK)) &&
+            (mTransfer == TRANSFER_CALLBACK) ||
+            // use case 3: obtain/release mode
+            (mTransfer == TRANSFER_OBTAIN)) &&
             // matching sample rate
             (mSampleRate == afSampleRate))) {
         ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client");
@@ -1236,7 +1241,7 @@
     return status;
 }
 
-status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
+status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig)
 {
     if (audioBuffer == NULL) {
         return BAD_VALUE;
@@ -1263,7 +1268,7 @@
         ALOGE("%s invalid waitCount %d", __func__, waitCount);
         requested = NULL;
     }
-    return obtainBuffer(audioBuffer, requested);
+    return obtainBuffer(audioBuffer, requested, NULL /*elapsed*/, nonContig);
 }
 
 status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
@@ -1338,8 +1343,9 @@
     return status;
 }
 
-void AudioTrack::releaseBuffer(Buffer* audioBuffer)
+void AudioTrack::releaseBuffer(const Buffer* audioBuffer)
 {
+    // FIXME add error checking on mode, by adding an internal version
     if (mTransfer == TRANSFER_SHARED) {
         return;
     }
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 08241e2..6d5f1af 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -423,7 +423,6 @@
             goto end;
         }
         // check for obtainBuffer interrupted by client
-        // check for obtainBuffer interrupted by client
         if (flags & CBLK_INTERRUPT) {
             ALOGV("waitStreamEndDone() interrupted by client");
             status = -EINTR;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 9d8fe62..4b31715 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -10,6 +10,7 @@
     ActivityManager.cpp         \
     Crypto.cpp                  \
     Drm.cpp                     \
+    DrmSessionManager.cpp       \
     HDCP.cpp                    \
     MediaPlayerFactory.cpp      \
     MediaPlayerService.cpp      \
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index 73f1a2a..d4f6fab 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -23,6 +23,8 @@
 
 #include "Drm.h"
 
+#include "DrmSessionClientInterface.h"
+#include "DrmSessionManager.h"
 #include <media/drm/DrmAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AString.h>
@@ -33,6 +35,10 @@
 
 namespace android {
 
+static inline int getCallingPid() {
+    return IPCThreadState::self()->getCallingPid();
+}
+
 static bool checkPermission(const char* permissionString) {
 #ifndef HAVE_ANDROID_OS
     return true;
@@ -57,14 +63,41 @@
     return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0;
 }
 
+struct DrmSessionClient : public DrmSessionClientInterface {
+    DrmSessionClient(Drm* drm) : mDrm(drm) {}
+
+    virtual bool reclaimSession(const Vector<uint8_t>& sessionId) {
+        sp<Drm> drm = mDrm.promote();
+        if (drm == NULL) {
+            return true;
+        }
+        status_t err = drm->closeSession(sessionId);
+        if (err != OK) {
+            return false;
+        }
+        drm->sendEvent(DrmPlugin::kDrmPluginEventSessionReclaimed, 0, &sessionId, NULL);
+        return true;
+    }
+
+protected:
+    virtual ~DrmSessionClient() {}
+
+private:
+    wp<Drm> mDrm;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
+};
+
 Drm::Drm()
     : mInitCheck(NO_INIT),
+      mDrmSessionClient(new DrmSessionClient(this)),
       mListener(NULL),
       mFactory(NULL),
       mPlugin(NULL) {
 }
 
 Drm::~Drm() {
+    DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
     delete mPlugin;
     mPlugin = NULL;
     closeFactory();
@@ -289,7 +322,18 @@
         return -EINVAL;
     }
 
-    return mPlugin->openSession(sessionId);
+    status_t err = mPlugin->openSession(sessionId);
+    if (err == ERROR_DRM_RESOURCE_BUSY) {
+        bool retry = false;
+        retry = DrmSessionManager::Instance()->reclaimSession(getCallingPid());
+        if (retry) {
+            err = mPlugin->openSession(sessionId);
+        }
+    }
+    if (err == OK) {
+        DrmSessionManager::Instance()->addSession(getCallingPid(), mDrmSessionClient, sessionId);
+    }
+    return err;
 }
 
 status_t Drm::closeSession(Vector<uint8_t> const &sessionId) {
@@ -303,7 +347,11 @@
         return -EINVAL;
     }
 
-    return mPlugin->closeSession(sessionId);
+    status_t err = mPlugin->closeSession(sessionId);
+    if (err == OK) {
+        DrmSessionManager::Instance()->removeSession(sessionId);
+    }
+    return err;
 }
 
 status_t Drm::getKeyRequest(Vector<uint8_t> const &sessionId,
@@ -321,6 +369,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->getKeyRequest(sessionId, initData, mimeType, keyType,
                                   optionalParameters, request, defaultUrl);
 }
@@ -338,6 +388,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->provideKeyResponse(sessionId, response, keySetId);
 }
 
@@ -367,6 +419,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->restoreKeys(sessionId, keySetId);
 }
 
@@ -382,6 +436,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->queryKeyStatus(sessionId, infoMap);
 }
 
@@ -561,6 +617,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->setCipherAlgorithm(sessionId, algorithm);
 }
 
@@ -576,6 +634,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->setMacAlgorithm(sessionId, algorithm);
 }
 
@@ -594,6 +654,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->encrypt(sessionId, keyId, input, iv, output);
 }
 
@@ -612,6 +674,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->decrypt(sessionId, keyId, input, iv, output);
 }
 
@@ -629,6 +693,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->sign(sessionId, keyId, message, signature);
 }
 
@@ -647,6 +713,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->verify(sessionId, keyId, message, signature, match);
 }
 
@@ -669,6 +737,8 @@
         return -EPERM;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->signRSA(sessionId, algorithm, message, wrappedKey, signature);
 }
 
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
index 0e1eb2c..0cea639 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -28,6 +28,7 @@
 
 struct DrmFactory;
 struct DrmPlugin;
+struct DrmSessionClientInterface;
 
 struct Drm : public BnDrm,
              public IBinder::DeathRecipient,
@@ -138,6 +139,8 @@
 
     status_t mInitCheck;
 
+    sp<DrmSessionClientInterface> mDrmSessionClient;
+
     sp<IDrmClient> mListener;
     mutable Mutex mEventLock;
     mutable Mutex mNotifyLock;
diff --git a/media/libmediaplayerservice/DrmSessionClientInterface.h b/media/libmediaplayerservice/DrmSessionClientInterface.h
new file mode 100644
index 0000000..17faf08
--- /dev/null
+++ b/media/libmediaplayerservice/DrmSessionClientInterface.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 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 DRM_PROXY_INTERFACE_H_
+#define DRM_PROXY_INTERFACE_H_
+
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct DrmSessionClientInterface : public RefBase {
+    virtual bool reclaimSession(const Vector<uint8_t>& sessionId) = 0;
+
+protected:
+    virtual ~DrmSessionClientInterface() {}
+};
+
+}  // namespace android
+
+#endif  // DRM_PROXY_INTERFACE_H_
diff --git a/media/libmediaplayerservice/DrmSessionManager.cpp b/media/libmediaplayerservice/DrmSessionManager.cpp
new file mode 100644
index 0000000..641f881
--- /dev/null
+++ b/media/libmediaplayerservice/DrmSessionManager.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2015 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 "DrmSessionManager"
+#include <utils/Log.h>
+
+#include "DrmSessionManager.h"
+
+#include "DrmSessionClientInterface.h"
+#include <binder/IPCThreadState.h>
+#include <binder/IProcessInfoService.h>
+#include <binder/IServiceManager.h>
+#include <media/stagefright/ProcessInfo.h>
+#include <unistd.h>
+#include <utils/String8.h>
+
+namespace android {
+
+static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
+    String8 sessionIdStr;
+    for (size_t i = 0; i < sessionId.size(); ++i) {
+        sessionIdStr.appendFormat("%u ", sessionId[i]);
+    }
+    return sessionIdStr;
+}
+
+bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
+    if (sessionId1.size() != sessionId2.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < sessionId1.size(); ++i) {
+        if (sessionId1[i] != sessionId2[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+sp<DrmSessionManager> DrmSessionManager::Instance() {
+    static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager();
+    return drmSessionManager;
+}
+
+DrmSessionManager::DrmSessionManager()
+    : mProcessInfo(new ProcessInfo()),
+      mTime(0) {}
+
+DrmSessionManager::DrmSessionManager(sp<ProcessInfoInterface> processInfo)
+    : mProcessInfo(processInfo),
+      mTime(0) {}
+
+DrmSessionManager::~DrmSessionManager() {}
+
+void DrmSessionManager::addSession(
+        int pid, sp<DrmSessionClientInterface> drm, const Vector<uint8_t> &sessionId) {
+    ALOGV("addSession(pid %d, drm %p, sessionId %s)", pid, drm.get(),
+            GetSessionIdString(sessionId).string());
+
+    Mutex::Autolock lock(mLock);
+    SessionInfo info;
+    info.drm = drm;
+    info.sessionId = sessionId;
+    info.timeStamp = getTime_l();
+    ssize_t index = mSessionMap.indexOfKey(pid);
+    if (index < 0) {
+        // new pid
+        SessionInfos infosForPid;
+        infosForPid.push_back(info);
+        mSessionMap.add(pid, infosForPid);
+    } else {
+        mSessionMap.editValueAt(index).push_back(info);
+    }
+}
+
+void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) {
+    ALOGV("useSession(%s)", GetSessionIdString(sessionId).string());
+
+    Mutex::Autolock lock(mLock);
+    for (size_t i = 0; i < mSessionMap.size(); ++i) {
+        SessionInfos& infos = mSessionMap.editValueAt(i);
+        for (size_t j = 0; j < infos.size(); ++j) {
+            SessionInfo& info = infos.editItemAt(j);
+            if (isEqualSessionId(sessionId, info.sessionId)) {
+                info.timeStamp = getTime_l();
+                return;
+            }
+        }
+    }
+}
+
+void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) {
+    ALOGV("removeSession(%s)", GetSessionIdString(sessionId).string());
+
+    Mutex::Autolock lock(mLock);
+    for (size_t i = 0; i < mSessionMap.size(); ++i) {
+        SessionInfos& infos = mSessionMap.editValueAt(i);
+        for (size_t j = 0; j < infos.size(); ++j) {
+            if (isEqualSessionId(sessionId, infos[j].sessionId)) {
+                infos.removeAt(j);
+                return;
+            }
+        }
+    }
+}
+
+void DrmSessionManager::removeDrm(sp<DrmSessionClientInterface> drm) {
+    ALOGV("removeDrm(%p)", drm.get());
+
+    Mutex::Autolock lock(mLock);
+    bool found = false;
+    for (size_t i = 0; i < mSessionMap.size(); ++i) {
+        SessionInfos& infos = mSessionMap.editValueAt(i);
+        for (size_t j = 0; j < infos.size();) {
+            if (infos[j].drm == drm) {
+                ALOGV("removed session (%s)", GetSessionIdString(infos[j].sessionId).string());
+                j = infos.removeAt(j);
+                found = true;
+            } else {
+                ++j;
+            }
+        }
+        if (found) {
+            break;
+        }
+    }
+}
+
+bool DrmSessionManager::reclaimSession(int callingPid) {
+    ALOGV("reclaimSession(%d)", callingPid);
+
+    sp<DrmSessionClientInterface> drm;
+    Vector<uint8_t> sessionId;
+    int lowestPriorityPid;
+    int lowestPriority;
+    {
+        Mutex::Autolock lock(mLock);
+        int callingPriority;
+        if (!mProcessInfo->getPriority(callingPid, &callingPriority)) {
+            return false;
+        }
+        if (!getLowestPriority_l(&lowestPriorityPid, &lowestPriority)) {
+            return false;
+        }
+        if (lowestPriority <= callingPriority) {
+            return false;
+        }
+
+        if (!getLeastUsedSession_l(lowestPriorityPid, &drm, &sessionId)) {
+            return false;
+        }
+    }
+
+    if (drm == NULL) {
+        return false;
+    }
+
+    ALOGV("reclaim session(%s) opened by pid %d",
+            GetSessionIdString(sessionId).string(), lowestPriorityPid);
+
+    return drm->reclaimSession(sessionId);
+}
+
+int64_t DrmSessionManager::getTime_l() {
+    return mTime++;
+}
+
+bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) {
+    int pid = -1;
+    int priority = -1;
+    for (size_t i = 0; i < mSessionMap.size(); ++i) {
+        if (mSessionMap.valueAt(i).size() == 0) {
+            // no opened session by this process.
+            continue;
+        }
+        int tempPid = mSessionMap.keyAt(i);
+        int tempPriority;
+        if (!mProcessInfo->getPriority(tempPid, &tempPriority)) {
+            // shouldn't happen.
+            return false;
+        }
+        if (pid == -1) {
+            pid = tempPid;
+            priority = tempPriority;
+        } else {
+            if (tempPriority > priority) {
+                pid = tempPid;
+                priority = tempPriority;
+            }
+        }
+    }
+    if (pid != -1) {
+        *lowestPriorityPid = pid;
+        *lowestPriority = priority;
+    }
+    return (pid != -1);
+}
+
+bool DrmSessionManager::getLeastUsedSession_l(
+        int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId) {
+    ssize_t index = mSessionMap.indexOfKey(pid);
+    if (index < 0) {
+        return false;
+    }
+
+    int leastUsedIndex = -1;
+    int64_t minTs = LLONG_MAX;
+    const SessionInfos& infos = mSessionMap.valueAt(index);
+    for (size_t j = 0; j < infos.size(); ++j) {
+        if (leastUsedIndex == -1) {
+            leastUsedIndex = j;
+            minTs = infos[j].timeStamp;
+        } else {
+            if (infos[j].timeStamp < minTs) {
+                leastUsedIndex = j;
+                minTs = infos[j].timeStamp;
+            }
+        }
+    }
+    if (leastUsedIndex != -1) {
+        *drm = infos[leastUsedIndex].drm;
+        *sessionId = infos[leastUsedIndex].sessionId;
+    }
+    return (leastUsedIndex != -1);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/DrmSessionManager.h b/media/libmediaplayerservice/DrmSessionManager.h
new file mode 100644
index 0000000..ba5c268
--- /dev/null
+++ b/media/libmediaplayerservice/DrmSessionManager.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 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 DRM_SESSION_MANAGER_H_
+
+#define DRM_SESSION_MANAGER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class DrmSessionManagerTest;
+struct DrmSessionClientInterface;
+struct ProcessInfoInterface;
+
+bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2);
+
+struct SessionInfo {
+    sp<DrmSessionClientInterface> drm;
+    Vector<uint8_t> sessionId;
+    int64_t timeStamp;
+};
+
+typedef Vector<SessionInfo > SessionInfos;
+typedef KeyedVector<int, SessionInfos > PidSessionInfosMap;
+
+struct DrmSessionManager : public RefBase {
+    static sp<DrmSessionManager> Instance();
+
+    DrmSessionManager();
+    DrmSessionManager(sp<ProcessInfoInterface> processInfo);
+
+    void addSession(int pid, sp<DrmSessionClientInterface> drm, const Vector<uint8_t>& sessionId);
+    void useSession(const Vector<uint8_t>& sessionId);
+    void removeSession(const Vector<uint8_t>& sessionId);
+    void removeDrm(sp<DrmSessionClientInterface> drm);
+    bool reclaimSession(int callingPid);
+
+protected:
+    virtual ~DrmSessionManager();
+
+private:
+    friend class DrmSessionManagerTest;
+
+    int64_t getTime_l();
+    bool getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority);
+    bool getLeastUsedSession_l(
+            int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId);
+
+    sp<ProcessInfoInterface> mProcessInfo;
+    mutable Mutex mLock;
+    PidSessionInfosMap mSessionMap;
+    int64_t mTime;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DrmSessionManager);
+};
+
+}  // namespace android
+
+#endif  // DRM_SESSION_MANAGER_H_
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 5e5d099..f113e21 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -290,8 +290,9 @@
     const sp<IServiceManager> sm(defaultServiceManager());
     if (sm != NULL) {
         const String16 name("batterystats");
+        // use checkService() to avoid blocking if service is not up yet
         sp<IBatteryStats> batteryStats =
-                interface_cast<IBatteryStats>(sm->getService(name));
+                interface_cast<IBatteryStats>(sm->checkService(name));
         if (batteryStats != NULL) {
             batteryStats->noteResetVideo();
             batteryStats->noteResetAudio();
@@ -441,6 +442,9 @@
     const size_t SIZE = 256;
     char buffer[SIZE];
     String8 result;
+    SortedVector< sp<Client> > clients; //to serialise the mutex unlock & client destruction.
+    SortedVector< sp<MediaRecorderClient> > mediaRecorderClients;
+
     if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
         snprintf(buffer, SIZE, "Permission Denial: "
                 "can't dump MediaPlayerService from pid=%d, uid=%d\n",
@@ -452,6 +456,7 @@
         for (int i = 0, n = mClients.size(); i < n; ++i) {
             sp<Client> c = mClients[i].promote();
             if (c != 0) c->dump(fd, args);
+            clients.add(c);
         }
         if (mMediaRecorderClients.size() == 0) {
                 result.append(" No media recorder client\n\n");
@@ -464,6 +469,7 @@
                     write(fd, result.string(), result.size());
                     result = "\n";
                     c->dump(fd, args);
+                    mediaRecorderClients.add(c);
                 }
             }
         }
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index e2c72ed..6609874 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -4,7 +4,6 @@
 LOCAL_SRC_FILES:=                       \
         GenericSource.cpp               \
         HTTPLiveSource.cpp              \
-        MediaClock.cpp                  \
         NuPlayer.cpp                    \
         NuPlayerCCDecoder.cpp           \
         NuPlayerDecoder.cpp             \
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index cbc5e0d..a040343 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -65,12 +65,12 @@
       mUID(uid),
       mFd(-1),
       mDrmManagerClient(NULL),
-      mMetaDataSize(-1ll),
       mBitrate(-1ll),
       mPollBufferingGeneration(0),
       mPendingReadBufferTypes(0),
       mBuffering(false),
-      mPrepareBuffering(false) {
+      mPrepareBuffering(false),
+      mPrevBufferPercentage(-1) {
     resetDataSource();
     DataSource::RegisterDefaultSniffers();
 }
@@ -147,14 +147,11 @@
             return UNKNOWN_ERROR;
         }
     } else if (mIsStreaming) {
-        if (mSniffedMIME.empty()) {
-            if (!mDataSource->sniff(&mimeType, &confidence, &dummy)) {
-                return UNKNOWN_ERROR;
-            }
-            mSniffedMIME = mimeType.string();
+        if (!mDataSource->sniff(&mimeType, &confidence, &dummy)) {
+            return UNKNOWN_ERROR;
         }
         isWidevineStreaming = !strcasecmp(
-                mSniffedMIME.c_str(), MEDIA_MIMETYPE_CONTAINER_WVM);
+                mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM);
     }
 
     if (isWidevineStreaming) {
@@ -169,7 +166,7 @@
         extractor = mWVMExtractor;
     } else {
         extractor = MediaExtractor::Create(mDataSource,
-                mSniffedMIME.empty() ? NULL: mSniffedMIME.c_str());
+                mimeType.isEmpty() ? NULL : mimeType.string());
     }
 
     if (extractor == NULL) {
@@ -334,7 +331,7 @@
         mLooper->registerHandler(this);
     }
 
-    sp<AMessage> msg = new AMessage(kWhatPrepareAsync, id());
+    sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this);
     msg->post();
 }
 
@@ -347,6 +344,7 @@
 
         if (!mUri.empty()) {
             const char* uri = mUri.c_str();
+            String8 contentType;
             mIsWidevine = !strncasecmp(uri, "widevine://", 11);
 
             if (!strncasecmp("http://", uri, 7)
@@ -361,7 +359,7 @@
             }
 
             mDataSource = DataSource::CreateFromURI(
-                   mHTTPService, uri, &mUriHeaders, &mContentType,
+                   mHTTPService, uri, &mUriHeaders, &contentType,
                    static_cast<HTTPBase *>(mHttpSource.get()));
         } else {
             mIsWidevine = false;
@@ -389,20 +387,8 @@
         mIsStreaming = (mIsWidevine || mCachedSource != NULL);
     }
 
-    // check initial caching status
-    status_t err = prefillCacheIfNecessary();
-    if (err != OK) {
-        if (err == -EAGAIN) {
-            (new AMessage(kWhatPrepareAsync, id()))->post(200000);
-        } else {
-            ALOGE("Failed to prefill data cache!");
-            notifyPreparedAndCleanup(UNKNOWN_ERROR);
-        }
-        return;
-    }
-
-    // init extrator from data source
-    err = initFromDataSource();
+    // init extractor from data source
+    status_t err = initFromDataSource();
 
     if (err != OK) {
         ALOGE("Failed to init from data source!");
@@ -441,9 +427,6 @@
 
 void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
     if (err != OK) {
-        mMetaDataSize = -1ll;
-        mContentType = "";
-        mSniffedMIME = "";
         mDataSource.clear();
         mCachedSource.clear();
         mHttpSource.clear();
@@ -453,76 +436,6 @@
     notifyPrepared(err);
 }
 
-status_t NuPlayer::GenericSource::prefillCacheIfNecessary() {
-    CHECK(mDataSource != NULL);
-
-    if (mCachedSource == NULL) {
-        // no prefill if the data source is not cached
-        return OK;
-    }
-
-    // We're not doing this for streams that appear to be audio-only
-    // streams to ensure that even low bandwidth streams start
-    // playing back fairly instantly.
-    if (!strncasecmp(mContentType.string(), "audio/", 6)) {
-        return OK;
-    }
-
-    // We're going to prefill the cache before trying to instantiate
-    // the extractor below, as the latter is an operation that otherwise
-    // could block on the datasource for a significant amount of time.
-    // During that time we'd be unable to abort the preparation phase
-    // without this prefill.
-
-    // Initially make sure we have at least 192 KB for the sniff
-    // to complete without blocking.
-    static const size_t kMinBytesForSniffing = 192 * 1024;
-    static const size_t kDefaultMetaSize = 200000;
-
-    status_t finalStatus;
-
-    size_t cachedDataRemaining =
-            mCachedSource->approxDataRemaining(&finalStatus);
-
-    if (finalStatus != OK || (mMetaDataSize >= 0
-            && (off64_t)cachedDataRemaining >= mMetaDataSize)) {
-        ALOGV("stop caching, status %d, "
-                "metaDataSize %lld, cachedDataRemaining %zu",
-                finalStatus, mMetaDataSize, cachedDataRemaining);
-        return OK;
-    }
-
-    ALOGV("now cached %zu bytes of data", cachedDataRemaining);
-
-    if (mMetaDataSize < 0
-            && cachedDataRemaining >= kMinBytesForSniffing) {
-        String8 tmp;
-        float confidence;
-        sp<AMessage> meta;
-        if (!mCachedSource->sniff(&tmp, &confidence, &meta)) {
-            return UNKNOWN_ERROR;
-        }
-
-        // We successfully identified the file's extractor to
-        // be, remember this mime type so we don't have to
-        // sniff it again when we call MediaExtractor::Create()
-        mSniffedMIME = tmp.string();
-
-        if (meta == NULL
-                || !meta->findInt64("meta-data-size",
-                        reinterpret_cast<int64_t*>(&mMetaDataSize))) {
-            mMetaDataSize = kDefaultMetaSize;
-        }
-
-        if (mMetaDataSize < 0ll) {
-            ALOGE("invalid metaDataSize = %lld bytes", mMetaDataSize);
-            return UNKNOWN_ERROR;
-        }
-    }
-
-    return -EAGAIN;
-}
-
 void NuPlayer::GenericSource::start() {
     ALOGI("start");
 
@@ -538,7 +451,7 @@
     setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
     mStarted = true;
 
-    (new AMessage(kWhatStart, id()))->post();
+    (new AMessage(kWhatStart, this))->post();
 }
 
 void NuPlayer::GenericSource::stop() {
@@ -547,7 +460,7 @@
     mStarted = false;
     if (mIsWidevine || mIsSecure) {
         // For widevine or secure sources we need to prevent any further reads.
-        sp<AMessage> msg = new AMessage(kWhatStopWidevine, id());
+        sp<AMessage> msg = new AMessage(kWhatStopWidevine, this);
         sp<AMessage> response;
         (void) msg->postAndAwaitResponse(&response);
     }
@@ -564,7 +477,7 @@
     setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
     mStarted = true;
 
-    (new AMessage(kWhatResume, id()))->post();
+    (new AMessage(kWhatResume, this))->post();
 }
 
 void NuPlayer::GenericSource::disconnect() {
@@ -591,7 +504,7 @@
 }
 
 void NuPlayer::GenericSource::schedulePollBuffering() {
-    sp<AMessage> msg = new AMessage(kWhatPollBuffering, id());
+    sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
     msg->setInt32("generation", mPollBufferingGeneration);
     msg->post(1000000ll);
 }
@@ -599,6 +512,7 @@
 void NuPlayer::GenericSource::cancelPollBuffering() {
     mBuffering = false;
     ++mPollBufferingGeneration;
+    mPrevBufferPercentage = -1;
 }
 
 void NuPlayer::GenericSource::restartPollBuffering() {
@@ -608,7 +522,19 @@
     }
 }
 
-void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) {
+void NuPlayer::GenericSource::notifyBufferingUpdate(int32_t percentage) {
+    // Buffering percent could go backward as it's estimated from remaining
+    // data and last access time. This could cause the buffering position
+    // drawn on media control to jitter slightly. Remember previously reported
+    // percentage and don't allow it to go backward.
+    if (percentage < mPrevBufferPercentage) {
+        percentage = mPrevBufferPercentage;
+    } else if (percentage > 100) {
+        percentage = 100;
+    }
+
+    mPrevBufferPercentage = percentage;
+
     ALOGV("notifyBufferingUpdate: buffering %d%%", percentage);
 
     sp<AMessage> msg = dupNotify();
@@ -888,7 +814,7 @@
               mVideoTrack.mPackets->clear();
           }
           sp<AMessage> response = new AMessage;
-          uint32_t replyID;
+          sp<AReplyToken> replyID;
           CHECK(msg->senderAwaitsResponse(&replyID));
           response->postReply(replyID);
           break;
@@ -928,7 +854,7 @@
         const int64_t oneSecUs = 1000000ll;
         delayUs -= oneSecUs;
     }
-    sp<AMessage> msg2 = new AMessage(sendWhat, id());
+    sp<AMessage> msg2 = new AMessage(sendWhat, this);
     msg2->setInt32("generation", msgGeneration);
     msg2->post(delayUs < 0 ? 0 : delayUs);
 }
@@ -968,7 +894,7 @@
 }
 
 sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) {
-    sp<AMessage> msg = new AMessage(kWhatGetFormat, id());
+    sp<AMessage> msg = new AMessage(kWhatGetFormat, this);
     msg->setInt32("audio", audio);
 
     sp<AMessage> response;
@@ -990,7 +916,7 @@
     sp<MetaData> format = doGetFormatMeta(audio);
     response->setPointer("format", format.get());
 
-    uint32_t replyID;
+    sp<AReplyToken> replyID;
     CHECK(msg->senderAwaitsResponse(&replyID));
     response->postReply(replyID);
 }
@@ -1057,7 +983,7 @@
 
     if (mSubtitleTrack.mSource != NULL
             && !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) {
-        sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id());
+        sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
         msg->setInt64("timeUs", timeUs);
         msg->setInt32("generation", mFetchSubtitleDataGeneration);
         msg->post();
@@ -1065,7 +991,7 @@
 
     if (mTimedTextTrack.mSource != NULL
             && !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) {
-        sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id());
+        sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, this);
         msg->setInt64("timeUs", timeUs);
         msg->setInt32("generation", mFetchTimedTextDataGeneration);
         msg->post();
@@ -1130,7 +1056,7 @@
 }
 
 ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const {
-    sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id());
+    sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this);
     msg->setInt32("type", type);
 
     sp<AMessage> response;
@@ -1153,7 +1079,7 @@
     ssize_t index = doGetSelectedTrack(type);
     response->setInt32("index", index);
 
-    uint32_t replyID;
+    sp<AReplyToken> replyID;
     CHECK(msg->senderAwaitsResponse(&replyID));
     response->postReply(replyID);
 }
@@ -1186,7 +1112,7 @@
 
 status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select, int64_t timeUs) {
     ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex);
-    sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
+    sp<AMessage> msg = new AMessage(kWhatSelectTrack, this);
     msg->setInt32("trackIndex", trackIndex);
     msg->setInt32("select", select);
     msg->setInt64("timeUs", timeUs);
@@ -1211,7 +1137,7 @@
     status_t err = doSelectTrack(trackIndex, select, timeUs);
     response->setInt32("err", err);
 
-    uint32_t replyID;
+    sp<AReplyToken> replyID;
     CHECK(msg->senderAwaitsResponse(&replyID));
     response->postReply(replyID);
 }
@@ -1272,7 +1198,7 @@
         status_t eosResult; // ignored
         if (mSubtitleTrack.mSource != NULL
                 && !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) {
-            sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id());
+            sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
             msg->setInt64("timeUs", timeUs);
             msg->setInt32("generation", mFetchSubtitleDataGeneration);
             msg->post();
@@ -1280,7 +1206,7 @@
 
         if (mTimedTextTrack.mSource != NULL
                 && !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) {
-            sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id());
+            sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, this);
             msg->setInt64("timeUs", timeUs);
             msg->setInt32("generation", mFetchTimedTextDataGeneration);
             msg->post();
@@ -1294,7 +1220,7 @@
             return OK;
         }
 
-        sp<AMessage> msg = new AMessage(kWhatChangeAVSource, id());
+        sp<AMessage> msg = new AMessage(kWhatChangeAVSource, this);
         msg->setInt32("trackIndex", trackIndex);
         msg->post();
         return OK;
@@ -1304,7 +1230,7 @@
 }
 
 status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) {
-    sp<AMessage> msg = new AMessage(kWhatSeek, id());
+    sp<AMessage> msg = new AMessage(kWhatSeek, this);
     msg->setInt64("seekTimeUs", seekTimeUs);
 
     sp<AMessage> response;
@@ -1324,7 +1250,7 @@
     status_t err = doSeek(seekTimeUs);
     response->setInt32("err", err);
 
-    uint32_t replyID;
+    sp<AReplyToken> replyID;
     CHECK(msg->senderAwaitsResponse(&replyID));
     response->postReply(replyID);
 }
@@ -1444,7 +1370,7 @@
 
     if ((mPendingReadBufferTypes & (1 << trackType)) == 0) {
         mPendingReadBufferTypes |= (1 << trackType);
-        sp<AMessage> msg = new AMessage(kWhatReadBuffer, id());
+        sp<AMessage> msg = new AMessage(kWhatReadBuffer, this);
         msg->setInt32("trackType", trackType);
         msg->post();
     }
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 385d73a..5fc41ec 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -139,14 +139,13 @@
     sp<DecryptHandle> mDecryptHandle;
     bool mStarted;
     bool mStopRead;
-    String8 mContentType;
-    AString mSniffedMIME;
-    off64_t mMetaDataSize;
     int64_t mBitrate;
     int32_t mPollBufferingGeneration;
     uint32_t mPendingReadBufferTypes;
     bool mBuffering;
     bool mPrepareBuffering;
+    int32_t mPrevBufferPercentage;
+
     mutable Mutex mReadBufferLock;
 
     sp<ALooper> mLooper;
@@ -158,8 +157,6 @@
     int64_t getLastReadPosition();
     void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position);
 
-    status_t prefillCacheIfNecessary();
-
     void notifyPreparedAndCleanup(status_t err);
 
     void onGetFormatMeta(sp<AMessage> msg) const;
@@ -200,7 +197,7 @@
     void cancelPollBuffering();
     void restartPollBuffering();
     void onPollBuffering();
-    void notifyBufferingUpdate(int percentage);
+    void notifyBufferingUpdate(int32_t percentage);
     void startBufferingIfNecessary();
     void stopBufferingIfNecessary();
     void sendCacheStats();
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index a26ef9e..d01e83a 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -81,7 +81,7 @@
         mLiveLooper->registerHandler(this);
     }
 
-    sp<AMessage> notify = new AMessage(kWhatSessionNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatSessionNotify, this);
 
     mLiveSession = new LiveSession(
             notify,
@@ -153,7 +153,7 @@
     if (err == OK) {
         mFetchSubtitleDataGeneration++;
         if (select) {
-            sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id());
+            sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
             msg->setInt32("generation", mFetchSubtitleDataGeneration);
             msg->post();
         }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 1e88bfa..f4d3794 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -200,9 +200,9 @@
 }
 
 void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) {
-    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
 
-    sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
 
     msg->setObject("source", new StreamingSource(notify, source));
     msg->post();
@@ -230,10 +230,10 @@
         const char *url,
         const KeyedVector<String8, String8> *headers) {
 
-    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
     size_t len = strlen(url);
 
-    sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
 
     sp<Source> source;
     if (IsHTTPLiveURL(url)) {
@@ -267,9 +267,9 @@
 }
 
 void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
-    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
 
-    sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
 
     sp<GenericSource> source =
             new GenericSource(notify, mUIDValid, mUID);
@@ -286,12 +286,12 @@
 }
 
 void NuPlayer::prepareAsync() {
-    (new AMessage(kWhatPrepare, id()))->post();
+    (new AMessage(kWhatPrepare, this))->post();
 }
 
 void NuPlayer::setVideoSurfaceTextureAsync(
         const sp<IGraphicBufferProducer> &bufferProducer) {
-    sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id());
+    sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, this);
 
     if (bufferProducer == NULL) {
         msg->setObject("native-window", NULL);
@@ -306,23 +306,23 @@
 }
 
 void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
-    sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id());
+    sp<AMessage> msg = new AMessage(kWhatSetAudioSink, this);
     msg->setObject("sink", sink);
     msg->post();
 }
 
 void NuPlayer::start() {
-    (new AMessage(kWhatStart, id()))->post();
+    (new AMessage(kWhatStart, this))->post();
 }
 
 void NuPlayer::setPlaybackRate(float rate) {
-    sp<AMessage> msg = new AMessage(kWhatSetRate, id());
+    sp<AMessage> msg = new AMessage(kWhatSetRate, this);
     msg->setFloat("rate", rate);
     msg->post();
 }
 
 void NuPlayer::pause() {
-    (new AMessage(kWhatPause, id()))->post();
+    (new AMessage(kWhatPause, this))->post();
 }
 
 void NuPlayer::resetAsync() {
@@ -336,11 +336,11 @@
         mSource->disconnect();
     }
 
-    (new AMessage(kWhatReset, id()))->post();
+    (new AMessage(kWhatReset, this))->post();
 }
 
 void NuPlayer::seekToAsync(int64_t seekTimeUs, bool needNotify) {
-    sp<AMessage> msg = new AMessage(kWhatSeek, id());
+    sp<AMessage> msg = new AMessage(kWhatSeek, this);
     msg->setInt64("seekTimeUs", seekTimeUs);
     msg->setInt32("needNotify", needNotify);
     msg->post();
@@ -408,7 +408,7 @@
 
         case kWhatGetTrackInfo:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             Parcel* reply;
@@ -461,7 +461,7 @@
             sp<AMessage> response = new AMessage;
             response->setInt32("err", err);
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
             response->postReply(replyID);
             break;
@@ -469,7 +469,7 @@
 
         case kWhatSelectTrack:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             size_t trackIndex;
@@ -1061,7 +1061,7 @@
         flags |= Renderer::FLAG_OFFLOAD_AUDIO;
     }
 
-    sp<AMessage> notify = new AMessage(kWhatRendererNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatRendererNotify, this);
     ++mRendererGeneration;
     notify->setInt32("generation", mRendererGeneration);
     mRenderer = new Renderer(mAudioSink, notify, flags);
@@ -1177,7 +1177,7 @@
         return;
     }
 
-    sp<AMessage> msg = new AMessage(kWhatScanSources, id());
+    sp<AMessage> msg = new AMessage(kWhatScanSources, this);
     msg->setInt32("generation", mScanSourcesGeneration);
     msg->post();
 
@@ -1219,7 +1219,7 @@
         AString mime;
         CHECK(format->findString("mime", &mime));
 
-        sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, id());
+        sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, this);
         if (mCCDecoder == NULL) {
             mCCDecoder = new CCDecoder(ccNotify);
         }
@@ -1234,7 +1234,7 @@
     }
 
     if (audio) {
-        sp<AMessage> notify = new AMessage(kWhatAudioNotify, id());
+        sp<AMessage> notify = new AMessage(kWhatAudioNotify, this);
         ++mAudioDecoderGeneration;
         notify->setInt32("generation", mAudioDecoderGeneration);
 
@@ -1244,7 +1244,7 @@
             *decoder = new Decoder(notify, mSource, mRenderer);
         }
     } else {
-        sp<AMessage> notify = new AMessage(kWhatVideoNotify, id());
+        sp<AMessage> notify = new AMessage(kWhatVideoNotify, this);
         ++mVideoDecoderGeneration;
         notify->setInt32("generation", mVideoDecoderGeneration);
 
@@ -1435,7 +1435,7 @@
 }
 
 status_t NuPlayer::getTrackInfo(Parcel* reply) const {
-    sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, id());
+    sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, this);
     msg->setPointer("reply", reply);
 
     sp<AMessage> response;
@@ -1444,7 +1444,7 @@
 }
 
 status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const {
-    sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id());
+    sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this);
     msg->setPointer("reply", reply);
     msg->setInt32("type", type);
 
@@ -1457,7 +1457,7 @@
 }
 
 status_t NuPlayer::selectTrack(size_t trackIndex, bool select, int64_t timeUs) {
-    sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
+    sp<AMessage> msg = new AMessage(kWhatSelectTrack, this);
     msg->setSize("trackIndex", trackIndex);
     msg->setInt32("select", select);
     msg->setInt64("timeUs", timeUs);
@@ -1500,7 +1500,7 @@
 }
 
 void NuPlayer::schedulePollDuration() {
-    sp<AMessage> msg = new AMessage(kWhatPollDuration, id());
+    sp<AMessage> msg = new AMessage(kWhatPollDuration, this);
     msg->setInt32("generation", mPollDurationGeneration);
     msg->post();
 }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 33378db..04ac699 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -56,6 +56,7 @@
       mIsVideoAVC(false),
       mIsSecure(false),
       mFormatChangePending(false),
+      mTimeChangePending(false),
       mPaused(true),
       mResumePending(false),
       mComponentName("decoder") {
@@ -121,6 +122,7 @@
     CHECK(mCodec == NULL);
 
     mFormatChangePending = false;
+    mTimeChangePending = false;
 
     ++mBufferGeneration;
 
@@ -235,7 +237,7 @@
     }
 }
 
-void NuPlayer::Decoder::onFlush(bool notifyComplete) {
+void NuPlayer::Decoder::doFlush(bool notifyComplete) {
     if (mCCDecoder != NULL) {
         mCCDecoder->flush();
     }
@@ -259,13 +261,22 @@
         // we attempt to release the buffers even if flush fails.
     }
     releaseAndResetMediaBuffers();
+}
 
-    if (notifyComplete) {
-        sp<AMessage> notify = mNotify->dup();
-        notify->setInt32("what", kWhatFlushCompleted);
-        notify->post();
-        mPaused = true;
+void NuPlayer::Decoder::onFlush() {
+    doFlush(true);
+
+    if (isDiscontinuityPending()) {
+        // This could happen if the client starts seeking/shutdown
+        // after we queued an EOS for discontinuities.
+        // We can consider discontinuity handled.
+        finishHandleDiscontinuity(false /* flushOnTimeChange */);
     }
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatFlushCompleted);
+    notify->post();
+    mPaused = true;
 }
 
 void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
@@ -309,16 +320,17 @@
 }
 
 void NuPlayer::Decoder::doRequestBuffers() {
-    if (mFormatChangePending) {
+    if (isDiscontinuityPending()) {
         return;
     }
     status_t err = OK;
-    while (!mDequeuedInputBuffers.empty()) {
+    while (err == OK && !mDequeuedInputBuffers.empty()) {
         size_t bufferIx = *mDequeuedInputBuffers.begin();
         sp<AMessage> msg = new AMessage();
         msg->setSize("buffer-ix", bufferIx);
         err = fetchInputData(msg);
-        if (err != OK) {
+        if (err != OK && err != ERROR_END_OF_STREAM) {
+            // if EOS, need to queue EOS buffer
             break;
         }
         mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());
@@ -336,7 +348,7 @@
 }
 
 bool NuPlayer::Decoder::handleAnInputBuffer() {
-    if (mFormatChangePending) {
+    if (isDiscontinuityPending()) {
         return false;
     }
     size_t bufferIx = -1;
@@ -391,9 +403,6 @@
 }
 
 bool NuPlayer::Decoder::handleAnOutputBuffer() {
-    if (mFormatChangePending) {
-        return false;
-    }
     size_t bufferIx = -1;
     size_t offset;
     size_t size;
@@ -474,17 +483,20 @@
     buffer->setRange(offset, size);
     buffer->meta()->clear();
     buffer->meta()->setInt64("timeUs", timeUs);
-    if (flags & MediaCodec::BUFFER_FLAG_EOS) {
-        buffer->meta()->setInt32("eos", true);
-        notifyResumeCompleteIfNecessary();
-    }
+
+    bool eos = flags & MediaCodec::BUFFER_FLAG_EOS;
     // we do not expect CODECCONFIG or SYNCFRAME for decoder
 
-    sp<AMessage> reply = new AMessage(kWhatRenderBuffer, id());
+    sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
     reply->setSize("buffer-ix", bufferIx);
     reply->setInt32("generation", mBufferGeneration);
 
-    if (mSkipRenderingUntilMediaTimeUs >= 0) {
+    if (eos) {
+        ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video");
+
+        buffer->meta()->setInt32("eos", true);
+        reply->setInt32("eos", true);
+    } else if (mSkipRenderingUntilMediaTimeUs >= 0) {
         if (timeUs < mSkipRenderingUntilMediaTimeUs) {
             ALOGV("[%s] dropping buffer at time %lld as requested.",
                      mComponentName.c_str(), (long long)timeUs);
@@ -502,7 +514,7 @@
     if (mRenderer != NULL) {
         // send the buffer to renderer.
         mRenderer->queueBuffer(mIsAudio, buffer, reply);
-        if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+        if (eos && !isDiscontinuityPending()) {
             mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
         }
     }
@@ -533,11 +545,8 @@
 }
 
 void NuPlayer::Decoder::requestCodecNotification() {
-    if (mFormatChangePending) {
-        return;
-    }
     if (mCodec != NULL) {
-        sp<AMessage> reply = new AMessage(kWhatCodecNotify, id());
+        sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
         reply->setInt32("generation", mBufferGeneration);
         mCodec->requestActivityNotification(reply);
     }
@@ -582,27 +591,15 @@
                     formatChange = !seamlessFormatChange;
                 }
 
-                if (formatChange || timeChange) {
-                    sp<AMessage> msg = mNotify->dup();
-                    msg->setInt32("what", kWhatInputDiscontinuity);
-                    msg->setInt32("formatChange", formatChange);
-                    msg->post();
-                }
-
+                // For format or time change, return EOS to queue EOS input,
+                // then wait for EOS on output.
                 if (formatChange /* not seamless */) {
-                    // must change decoder
-                    // return EOS and wait to be killed
                     mFormatChangePending = true;
-                    return ERROR_END_OF_STREAM;
+                    err = ERROR_END_OF_STREAM;
                 } else if (timeChange) {
-                    // need to flush
-                    // TODO: Ideally we shouldn't need a flush upon time
-                    // discontinuity, flushing will cause loss of frames.
-                    // We probably should queue a time change marker to the
-                    // output queue, and handles it in renderer instead.
                     rememberCodecSpecificData(newFormat);
-                    onFlush(false /* notifyComplete */);
-                    continue;
+                    mTimeChangePending = true;
+                    err = ERROR_END_OF_STREAM;
                 } else if (seamlessFormatChange) {
                     // reuse existing decoder and don't flush
                     rememberCodecSpecificData(newFormat);
@@ -613,8 +610,12 @@
                 }
             }
 
+            // reply should only be returned without a buffer set
+            // when there is an error (including EOS)
+            CHECK(err != OK);
+
             reply->setInt32("err", err);
-            return OK;
+            return ERROR_END_OF_STREAM;
         }
 
         if (!mIsAudio) {
@@ -636,7 +637,7 @@
 #if 0
     int64_t mediaTimeUs;
     CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
-    ALOGV("feeding %s input buffer at media time %.2f secs",
+    ALOGV("[%s] feeding input buffer at media time %" PRId64,
          mIsAudio ? "audio" : "video",
          mediaTimeUs / 1E6);
 #endif
@@ -778,6 +779,7 @@
     status_t err;
     int32_t render;
     size_t bufferIx;
+    int32_t eos;
     CHECK(msg->findSize("buffer-ix", &bufferIx));
 
     if (!mIsAudio) {
@@ -802,6 +804,42 @@
                 mComponentName.c_str(), err);
         handleError(err);
     }
+    if (msg->findInt32("eos", &eos) && eos
+            && isDiscontinuityPending()) {
+        finishHandleDiscontinuity(true /* flushOnTimeChange */);
+    }
+}
+
+bool NuPlayer::Decoder::isDiscontinuityPending() const {
+    return mFormatChangePending || mTimeChangePending;
+}
+
+void NuPlayer::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) {
+    ALOGV("finishHandleDiscontinuity: format %d, time %d, flush %d",
+            mFormatChangePending, mTimeChangePending, flushOnTimeChange);
+
+    // If we have format change, pause and wait to be killed;
+    // If we have time change only, flush and restart fetching.
+
+    if (mFormatChangePending) {
+        mPaused = true;
+    } else if (mTimeChangePending) {
+        if (flushOnTimeChange) {
+            doFlush(false /*notifyComplete*/);
+        }
+
+        // restart fetching input
+        scheduleRequestBuffers();
+    }
+
+    // Notify NuPlayer to either shutdown decoder, or rescan sources
+    sp<AMessage> msg = mNotify->dup();
+    msg->setInt32("what", kWhatInputDiscontinuity);
+    msg->setInt32("formatChange", mFormatChangePending);
+    msg->post();
+
+    mFormatChangePending = false;
+    mTimeChangePending = false;
 }
 
 bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 1bfa94f..4aab2c6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -43,7 +43,7 @@
     virtual void onSetRenderer(const sp<Renderer> &renderer);
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
     virtual void onResume(bool notifyComplete);
-    virtual void onFlush(bool notifyComplete);
+    virtual void onFlush();
     virtual void onShutdown(bool notifyComplete);
     virtual void doRequestBuffers();
 
@@ -81,6 +81,7 @@
     bool mIsVideoAVC;
     bool mIsSecure;
     bool mFormatChangePending;
+    bool mTimeChangePending;
 
     bool mPaused;
     bool mResumePending;
@@ -93,6 +94,7 @@
     void requestCodecNotification();
     bool isStaleReply(const sp<AMessage> &msg);
 
+    void doFlush(bool notifyComplete);
     status_t fetchInputData(sp<AMessage> &reply);
     bool onInputBufferFetched(const sp<AMessage> &msg);
     void onRenderBuffer(const sp<AMessage> &msg);
@@ -100,6 +102,8 @@
     bool supportsSeamlessFormatChange(const sp<AMessage> &to) const;
     bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
     void rememberCodecSpecificData(const sp<AMessage> &format);
+    bool isDiscontinuityPending() const;
+    void finishHandleDiscontinuity(bool flushOnTimeChange);
 
     void notifyResumeCompleteIfNecessary();
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index d56fc4d..4636f0a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -61,7 +61,7 @@
 }
 
 void NuPlayer::DecoderBase::configure(const sp<AMessage> &format) {
-    sp<AMessage> msg = new AMessage(kWhatConfigure, id());
+    sp<AMessage> msg = new AMessage(kWhatConfigure, this);
     msg->setMessage("format", format);
     msg->post();
 }
@@ -71,13 +71,13 @@
 }
 
 void NuPlayer::DecoderBase::setRenderer(const sp<Renderer> &renderer) {
-    sp<AMessage> msg = new AMessage(kWhatSetRenderer, id());
+    sp<AMessage> msg = new AMessage(kWhatSetRenderer, this);
     msg->setObject("renderer", renderer);
     msg->post();
 }
 
 status_t NuPlayer::DecoderBase::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
-    sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id());
+    sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, this);
     msg->setPointer("buffers", buffers);
 
     sp<AMessage> response;
@@ -85,17 +85,17 @@
 }
 
 void NuPlayer::DecoderBase::signalFlush() {
-    (new AMessage(kWhatFlush, id()))->post();
+    (new AMessage(kWhatFlush, this))->post();
 }
 
 void NuPlayer::DecoderBase::signalResume(bool notifyComplete) {
-    sp<AMessage> msg = new AMessage(kWhatResume, id());
+    sp<AMessage> msg = new AMessage(kWhatResume, this);
     msg->setInt32("notifyComplete", notifyComplete);
     msg->post();
 }
 
 void NuPlayer::DecoderBase::initiateShutdown() {
-    (new AMessage(kWhatShutdown, id()))->post();
+    (new AMessage(kWhatShutdown, this))->post();
 }
 
 void NuPlayer::DecoderBase::onRequestInputBuffers() {
@@ -111,7 +111,7 @@
         return;
     }
     mRequestInputBuffersPending = true;
-    sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, id());
+    sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, this);
     msg->post(10 * 1000ll);
 }
 
@@ -136,7 +136,7 @@
 
         case kWhatGetInputBuffers:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             Vector<sp<ABuffer> > *dstBuffers;
@@ -157,7 +157,7 @@
 
         case kWhatFlush:
         {
-            onFlush(true);
+            onFlush();
             break;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 6732ff4..97e9269 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -65,7 +65,7 @@
     virtual void onSetRenderer(const sp<Renderer> &renderer) = 0;
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers) = 0;
     virtual void onResume(bool notifyComplete) = 0;
-    virtual void onFlush(bool notifyComplete) = 0;
+    virtual void onFlush() = 0;
     virtual void onShutdown(bool notifyComplete) = 0;
 
     void onRequestInputBuffers();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index 9f7f09a..29b4c26 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -247,7 +247,7 @@
                 }
 
                 if (timeChange) {
-                    onFlush(false /* notifyComplete */);
+                    doFlush(false /* notifyComplete */);
                     err = OK;
                 } else if (formatChange) {
                     // do seamless format change
@@ -333,7 +333,7 @@
         return;
     }
 
-    sp<AMessage> reply = new AMessage(kWhatBufferConsumed, id());
+    sp<AMessage> reply = new AMessage(kWhatBufferConsumed, this);
     reply->setInt32("generation", mBufferGeneration);
     reply->setInt32("size", bufferSize);
 
@@ -364,7 +364,7 @@
     }
 }
 
-void NuPlayer::DecoderPassThrough::onFlush(bool notifyComplete) {
+void NuPlayer::DecoderPassThrough::doFlush(bool notifyComplete) {
     ++mBufferGeneration;
     mSkipRenderingUntilMediaTimeUs = -1;
     mPendingAudioAccessUnit.clear();
@@ -376,18 +376,21 @@
         mRenderer->signalTimeDiscontinuity();
     }
 
-    if (notifyComplete) {
-        mPaused = true;
-        sp<AMessage> notify = mNotify->dup();
-        notify->setInt32("what", kWhatFlushCompleted);
-        notify->post();
-    }
-
     mPendingBuffersToDrain = 0;
     mCachedBytes = 0;
     mReachedEOS = false;
 }
 
+void NuPlayer::DecoderPassThrough::onFlush() {
+    doFlush(true /* notifyComplete */);
+
+    mPaused = true;
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatFlushCompleted);
+    notify->post();
+
+}
+
 void NuPlayer::DecoderPassThrough::onShutdown(bool notifyComplete) {
     ++mBufferGeneration;
     mSkipRenderingUntilMediaTimeUs = -1;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index a6e1faf..173cfbd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -43,7 +43,7 @@
     virtual void onSetRenderer(const sp<Renderer> &renderer);
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
     virtual void onResume(bool notifyComplete);
-    virtual void onFlush(bool notifyComplete);
+    virtual void onFlush();
     virtual void onShutdown(bool notifyComplete);
     virtual void doRequestBuffers();
 
@@ -77,6 +77,7 @@
     status_t dequeueAccessUnit(sp<ABuffer> *accessUnit);
     sp<ABuffer> aggregateBuffer(const sp<ABuffer> &accessUnit);
     status_t fetchInputData(sp<AMessage> &reply);
+    void doFlush(bool notifyComplete);
 
     void onInputBufferFetched(const sp<AMessage> &msg);
     void onBufferConsumed(int32_t size);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index e491a31..4bccfa8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -20,13 +20,12 @@
 
 #include "NuPlayerRenderer.h"
 
-#include "MediaClock.h"
-
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/foundation/AWakeLock.h>
+#include <media/stagefright/MediaClock.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
@@ -104,7 +103,7 @@
         bool audio,
         const sp<ABuffer> &buffer,
         const sp<AMessage> &notifyConsumed) {
-    sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatQueueBuffer, this);
     msg->setInt32("queueGeneration", getQueueGeneration(audio));
     msg->setInt32("audio", static_cast<int32_t>(audio));
     msg->setBuffer("buffer", buffer);
@@ -115,7 +114,7 @@
 void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) {
     CHECK_NE(finalResult, (status_t)OK);
 
-    sp<AMessage> msg = new AMessage(kWhatQueueEOS, id());
+    sp<AMessage> msg = new AMessage(kWhatQueueEOS, this);
     msg->setInt32("queueGeneration", getQueueGeneration(audio));
     msg->setInt32("audio", static_cast<int32_t>(audio));
     msg->setInt32("finalResult", finalResult);
@@ -123,7 +122,7 @@
 }
 
 void NuPlayer::Renderer::setPlaybackRate(float rate) {
-    sp<AMessage> msg = new AMessage(kWhatSetRate, id());
+    sp<AMessage> msg = new AMessage(kWhatSetRate, this);
     msg->setFloat("rate", rate);
     msg->post();
 }
@@ -147,7 +146,7 @@
         mSyncQueues = false;
     }
 
-    sp<AMessage> msg = new AMessage(kWhatFlush, id());
+    sp<AMessage> msg = new AMessage(kWhatFlush, this);
     msg->setInt32("audio", static_cast<int32_t>(audio));
     msg->post();
 }
@@ -156,23 +155,23 @@
 }
 
 void NuPlayer::Renderer::signalDisableOffloadAudio() {
-    (new AMessage(kWhatDisableOffloadAudio, id()))->post();
+    (new AMessage(kWhatDisableOffloadAudio, this))->post();
 }
 
 void NuPlayer::Renderer::signalEnableOffloadAudio() {
-    (new AMessage(kWhatEnableOffloadAudio, id()))->post();
+    (new AMessage(kWhatEnableOffloadAudio, this))->post();
 }
 
 void NuPlayer::Renderer::pause() {
-    (new AMessage(kWhatPause, id()))->post();
+    (new AMessage(kWhatPause, this))->post();
 }
 
 void NuPlayer::Renderer::resume() {
-    (new AMessage(kWhatResume, id()))->post();
+    (new AMessage(kWhatResume, this))->post();
 }
 
 void NuPlayer::Renderer::setVideoFrameRate(float fps) {
-    sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, id());
+    sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, this);
     msg->setFloat("frame-rate", fps);
     msg->post();
 }
@@ -216,7 +215,7 @@
         bool hasVideo,
         uint32_t flags,
         bool *isOffloaded) {
-    sp<AMessage> msg = new AMessage(kWhatOpenAudioSink, id());
+    sp<AMessage> msg = new AMessage(kWhatOpenAudioSink, this);
     msg->setMessage("format", format);
     msg->setInt32("offload-only", offloadOnly);
     msg->setInt32("has-video", hasVideo);
@@ -237,7 +236,7 @@
 }
 
 void NuPlayer::Renderer::closeAudioSink() {
-    sp<AMessage> msg = new AMessage(kWhatCloseAudioSink, id());
+    sp<AMessage> msg = new AMessage(kWhatCloseAudioSink, this);
 
     sp<AMessage> response;
     msg->postAndAwaitResponse(&response);
@@ -265,7 +264,7 @@
             response->setInt32("err", err);
             response->setInt32("offload", offloadingAudio());
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
             response->postReply(replyID);
 
@@ -274,7 +273,7 @@
 
         case kWhatCloseAudioSink:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             onCloseAudioSink();
@@ -447,7 +446,7 @@
     }
 
     mDrainAudioQueuePending = true;
-    sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
+    sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, this);
     msg->setInt32("drainGeneration", mAudioDrainGeneration);
     msg->post(delayUs);
 }
@@ -560,7 +559,7 @@
     }
 
     if (hasEOS) {
-        (new AMessage(kWhatStopAudioSink, id()))->post();
+        (new AMessage(kWhatStopAudioSink, this))->post();
     }
 
     return sizeCopied;
@@ -716,6 +715,7 @@
     int64_t nowUs = ALooper::GetNowUs();
     int64_t nowMediaUs = mediaTimeUs - getPendingAudioPlayoutDurationUs(nowUs);
     mMediaClock->updateAnchor(nowMediaUs, nowUs, mediaTimeUs);
+    mAnchorNumFramesWritten = mNumFramesWritten;
     mAnchorTimeMediaUs = mediaTimeUs;
 }
 
@@ -733,7 +733,7 @@
 
     QueueEntry &entry = *mVideoQueue.begin();
 
-    sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
+    sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, this);
     msg->setInt32("drainGeneration", getDrainGeneration(false /* audio */));
 
     if (entry.mBuffer == NULL) {
@@ -894,7 +894,7 @@
 }
 
 void NuPlayer::Renderer::notifyAudioOffloadTearDown() {
-    (new AMessage(kWhatAudioOffloadTearDown, id()))->post();
+    (new AMessage(kWhatAudioOffloadTearDown, this))->post();
 }
 
 void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
@@ -1322,7 +1322,7 @@
 void NuPlayer::Renderer::startAudioOffloadPauseTimeout() {
     if (offloadingAudio()) {
         mWakeLock->acquire();
-        sp<AMessage> msg = new AMessage(kWhatAudioOffloadPauseTimeout, id());
+        sp<AMessage> msg = new AMessage(kWhatAudioOffloadPauseTimeout, this);
         msg->setInt32("drainGeneration", mAudioOffloadPauseTimeoutGeneration);
         msg->post(kOffloadPauseMaxUs);
     }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
index 885ebe4..f53afbd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -29,9 +29,9 @@
 
 NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener(
         const sp<IStreamSource> &source,
-        ALooper::handler_id id)
+        const sp<AHandler> &targetHandler)
     : mSource(source),
-      mTargetID(id),
+      mTargetHandler(targetHandler),
       mEOS(false),
       mSendDataNotification(true) {
     mSource->setListener(this);
@@ -65,8 +65,8 @@
     if (mSendDataNotification) {
         mSendDataNotification = false;
 
-        if (mTargetID != 0) {
-            (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+        if (mTargetHandler != NULL) {
+            (new AMessage(kWhatMoreDataQueued, mTargetHandler))->post();
         }
     }
 }
@@ -86,8 +86,8 @@
     if (mSendDataNotification) {
         mSendDataNotification = false;
 
-        if (mTargetID != 0) {
-            (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+        if (mTargetHandler != NULL) {
+            (new AMessage(kWhatMoreDataQueued, mTargetHandler))->post();
         }
     }
 }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
index 1874d80..2de829b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -29,7 +29,7 @@
 struct NuPlayer::NuPlayerStreamListener : public BnStreamListener {
     NuPlayerStreamListener(
             const sp<IStreamSource> &source,
-            ALooper::handler_id targetID);
+            const sp<AHandler> &targetHandler);
 
     virtual void queueBuffer(size_t index, size_t size);
 
@@ -59,7 +59,7 @@
     Mutex mLock;
 
     sp<IStreamSource> mSource;
-    ALooper::handler_id mTargetID;
+    sp<AHandler> mTargetHandler;
     sp<MemoryDealer> mMemoryDealer;
     Vector<sp<IMemory> > mBuffers;
     List<QueueEntry> mQueue;
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 0282a9f..5210fc8 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -87,7 +87,7 @@
     CHECK(mHandler == NULL);
     CHECK(mSDPLoader == NULL);
 
-    sp<AMessage> notify = new AMessage(kWhatNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatNotify, this);
 
     CHECK_EQ(mState, (int)DISCONNECTED);
     mState = CONNECTING;
@@ -116,7 +116,7 @@
     if (mLooper == NULL) {
         return;
     }
-    sp<AMessage> msg = new AMessage(kWhatDisconnect, id());
+    sp<AMessage> msg = new AMessage(kWhatDisconnect, this);
 
     sp<AMessage> dummy;
     msg->postAndAwaitResponse(&dummy);
@@ -292,7 +292,7 @@
 }
 
 status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) {
-    sp<AMessage> msg = new AMessage(kWhatPerformSeek, id());
+    sp<AMessage> msg = new AMessage(kWhatPerformSeek, this);
     msg->setInt32("generation", ++mSeekGeneration);
     msg->setInt64("timeUs", seekTimeUs);
     msg->post(200000ll);
@@ -311,7 +311,7 @@
 
 void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
     if (msg->what() == kWhatDisconnect) {
-        uint32_t replyID;
+        sp<AReplyToken> replyID;
         CHECK(msg->senderAwaitsResponse(&replyID));
 
         mDisconnectReplyID = replyID;
@@ -600,7 +600,7 @@
             ALOGE("Unable to find url in SDP");
             err = UNKNOWN_ERROR;
         } else {
-            sp<AMessage> notify = new AMessage(kWhatNotify, id());
+            sp<AMessage> notify = new AMessage(kWhatNotify, this);
 
             mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID);
             mLooper->registerHandler(mHandler);
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index ac3299a..5f2cf33 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -25,6 +25,7 @@
 namespace android {
 
 struct ALooper;
+struct AReplyToken;
 struct AnotherPacketSource;
 struct MyHandler;
 struct SDPLoader;
@@ -96,7 +97,7 @@
     bool mIsSDP;
     State mState;
     status_t mFinalResult;
-    uint32_t mDisconnectReplyID;
+    sp<AReplyToken> mDisconnectReplyID;
     Mutex mBufferingLock;
     bool mBuffering;
 
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index b3f224d..0246b59 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -63,7 +63,7 @@
 }
 
 void NuPlayer::StreamingSource::start() {
-    mStreamListener = new NuPlayerStreamListener(mSource, 0);
+    mStreamListener = new NuPlayerStreamListener(mSource, NULL);
 
     uint32_t sourceFlags = mSource->flags();
 
@@ -163,7 +163,7 @@
         mBuffering = true;
     }
 
-    (new AMessage(kWhatReadBuffer, id()))->post();
+    (new AMessage(kWhatReadBuffer, this))->post();
     return OK;
 }
 
diff --git a/media/libmediaplayerservice/tests/Android.mk b/media/libmediaplayerservice/tests/Android.mk
new file mode 100644
index 0000000..7bc78ff
--- /dev/null
+++ b/media/libmediaplayerservice/tests/Android.mk
@@ -0,0 +1,24 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := DrmSessionManager_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+	DrmSessionManager_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+	liblog \
+	libmediaplayerservice \
+	libutils \
+
+LOCAL_C_INCLUDES := \
+	frameworks/av/include \
+	frameworks/av/media/libmediaplayerservice \
+
+LOCAL_32_BIT_ONLY := true
+
+include $(BUILD_NATIVE_TEST)
+
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
new file mode 100644
index 0000000..d3e760b
--- /dev/null
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2015 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 "DrmSessionManager_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include "Drm.h"
+#include "DrmSessionClientInterface.h"
+#include "DrmSessionManager.h"
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/ProcessInfoInterface.h>
+
+namespace android {
+
+struct FakeProcessInfo : public ProcessInfoInterface {
+    FakeProcessInfo() {}
+    virtual ~FakeProcessInfo() {}
+
+    virtual bool getPriority(int pid, int* priority) {
+        // For testing, use pid as priority.
+        // Lower the value higher the priority.
+        *priority = pid;
+        return true;
+    }
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(FakeProcessInfo);
+};
+
+struct FakeDrm : public DrmSessionClientInterface {
+    FakeDrm() {}
+    virtual ~FakeDrm() {}
+
+    virtual bool reclaimSession(const Vector<uint8_t>& sessionId) {
+        mReclaimedSessions.push_back(sessionId);
+        return true;
+    }
+
+    const Vector<Vector<uint8_t> >& reclaimedSessions() const {
+        return mReclaimedSessions;
+    }
+
+private:
+    Vector<Vector<uint8_t> > mReclaimedSessions;
+
+    DISALLOW_EVIL_CONSTRUCTORS(FakeDrm);
+};
+
+static const int kTestPid1 = 30;
+static const int kTestPid2 = 20;
+static const uint8_t kTestSessionId1[] = {1, 2, 3};
+static const uint8_t kTestSessionId2[] = {4, 5, 6, 7, 8};
+static const uint8_t kTestSessionId3[] = {9, 0};
+
+class DrmSessionManagerTest : public ::testing::Test {
+public:
+    DrmSessionManagerTest()
+        : mDrmSessionManager(new DrmSessionManager(new FakeProcessInfo())),
+          mTestDrm1(new FakeDrm()),
+          mTestDrm2(new FakeDrm()) {
+        GetSessionId(kTestSessionId1, ARRAY_SIZE(kTestSessionId1), &mSessionId1);
+        GetSessionId(kTestSessionId2, ARRAY_SIZE(kTestSessionId2), &mSessionId2);
+        GetSessionId(kTestSessionId3, ARRAY_SIZE(kTestSessionId3), &mSessionId3);
+    }
+
+protected:
+    static void GetSessionId(const uint8_t* ids, size_t num, Vector<uint8_t>* sessionId) {
+        for (size_t i = 0; i < num; ++i) {
+            sessionId->push_back(ids[i]);
+        }
+    }
+
+    static void ExpectEqSessionInfo(const SessionInfo& info, sp<DrmSessionClientInterface> drm,
+            const Vector<uint8_t>& sessionId, int64_t timeStamp) {
+        EXPECT_EQ(drm, info.drm);
+        EXPECT_TRUE(isEqualSessionId(sessionId, info.sessionId));
+        EXPECT_EQ(timeStamp, info.timeStamp);
+    }
+
+    void addSession() {
+        mDrmSessionManager->addSession(kTestPid1, mTestDrm1, mSessionId1);
+        mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId2);
+        mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId3);
+        const PidSessionInfosMap& map = sessionMap();
+        EXPECT_EQ(2, map.size());
+        ssize_t index1 = map.indexOfKey(kTestPid1);
+        ASSERT_GE(index1, 0);
+        const SessionInfos& infos1 = map[index1];
+        EXPECT_EQ(1, infos1.size());
+        ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 0);
+
+        ssize_t index2 = map.indexOfKey(kTestPid2);
+        ASSERT_GE(index2, 0);
+        const SessionInfos& infos2 = map[index2];
+        EXPECT_EQ(2, infos2.size());
+        ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId2, 1);
+        ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 2);
+    }
+
+    const PidSessionInfosMap& sessionMap() {
+        return mDrmSessionManager->mSessionMap;
+    }
+
+    void testGetLowestPriority() {
+        int pid;
+        int priority;
+        EXPECT_FALSE(mDrmSessionManager->getLowestPriority_l(&pid, &priority));
+
+        addSession();
+        EXPECT_TRUE(mDrmSessionManager->getLowestPriority_l(&pid, &priority));
+
+        EXPECT_EQ(kTestPid1, pid);
+        FakeProcessInfo processInfo;
+        int priority1;
+        processInfo.getPriority(kTestPid1, &priority1);
+        EXPECT_EQ(priority1, priority);
+    }
+
+    void testGetLeastUsedSession() {
+        sp<DrmSessionClientInterface> drm;
+        Vector<uint8_t> sessionId;
+        EXPECT_FALSE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId));
+
+        addSession();
+
+        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId));
+        EXPECT_EQ(mTestDrm1, drm);
+        EXPECT_TRUE(isEqualSessionId(mSessionId1, sessionId));
+
+        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId));
+        EXPECT_EQ(mTestDrm2, drm);
+        EXPECT_TRUE(isEqualSessionId(mSessionId2, sessionId));
+
+        // mSessionId2 is no longer the least used session.
+        mDrmSessionManager->useSession(mSessionId2);
+        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId));
+        EXPECT_EQ(mTestDrm2, drm);
+        EXPECT_TRUE(isEqualSessionId(mSessionId3, sessionId));
+    }
+
+    sp<DrmSessionManager> mDrmSessionManager;
+    sp<FakeDrm> mTestDrm1;
+    sp<FakeDrm> mTestDrm2;
+    Vector<uint8_t> mSessionId1;
+    Vector<uint8_t> mSessionId2;
+    Vector<uint8_t> mSessionId3;
+};
+
+TEST_F(DrmSessionManagerTest, addSession) {
+    addSession();
+}
+
+TEST_F(DrmSessionManagerTest, useSession) {
+    addSession();
+
+    mDrmSessionManager->useSession(mSessionId1);
+    mDrmSessionManager->useSession(mSessionId3);
+
+    const PidSessionInfosMap& map = sessionMap();
+    const SessionInfos& infos1 = map.valueFor(kTestPid1);
+    const SessionInfos& infos2 = map.valueFor(kTestPid2);
+    ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 3);
+    ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 4);
+}
+
+TEST_F(DrmSessionManagerTest, removeSession) {
+    addSession();
+
+    mDrmSessionManager->removeSession(mSessionId2);
+
+    const PidSessionInfosMap& map = sessionMap();
+    EXPECT_EQ(2, map.size());
+    const SessionInfos& infos1 = map.valueFor(kTestPid1);
+    const SessionInfos& infos2 = map.valueFor(kTestPid2);
+    EXPECT_EQ(1, infos1.size());
+    EXPECT_EQ(1, infos2.size());
+    // mSessionId2 has been removed.
+    ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId3, 2);
+}
+
+TEST_F(DrmSessionManagerTest, removeDrm) {
+    addSession();
+
+    sp<FakeDrm> drm = new FakeDrm;
+    const uint8_t ids[] = {123};
+    Vector<uint8_t> sessionId;
+    GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
+    mDrmSessionManager->addSession(kTestPid2, drm, sessionId);
+
+    mDrmSessionManager->removeDrm(mTestDrm2);
+
+    const PidSessionInfosMap& map = sessionMap();
+    const SessionInfos& infos2 = map.valueFor(kTestPid2);
+    EXPECT_EQ(1, infos2.size());
+    // mTestDrm2 has been removed.
+    ExpectEqSessionInfo(infos2[0], drm, sessionId, 3);
+}
+
+TEST_F(DrmSessionManagerTest, reclaimSession) {
+    EXPECT_FALSE(mDrmSessionManager->reclaimSession(kTestPid1));
+    addSession();
+
+    // calling pid priority is too low
+    EXPECT_FALSE(mDrmSessionManager->reclaimSession(50));
+
+    EXPECT_TRUE(mDrmSessionManager->reclaimSession(10));
+    EXPECT_EQ(1, mTestDrm1->reclaimedSessions().size());
+    EXPECT_TRUE(isEqualSessionId(mSessionId1, mTestDrm1->reclaimedSessions()[0]));
+
+    mDrmSessionManager->removeSession(mSessionId1);
+
+    // add a session from a higher priority process.
+    sp<FakeDrm> drm = new FakeDrm;
+    const uint8_t ids[] = {1, 3, 5};
+    Vector<uint8_t> sessionId;
+    GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
+    mDrmSessionManager->addSession(15, drm, sessionId);
+
+    EXPECT_TRUE(mDrmSessionManager->reclaimSession(18));
+    EXPECT_EQ(1, mTestDrm2->reclaimedSessions().size());
+    // mSessionId2 is reclaimed.
+    EXPECT_TRUE(isEqualSessionId(mSessionId2, mTestDrm2->reclaimedSessions()[0]));
+}
+
+TEST_F(DrmSessionManagerTest, getLowestPriority) {
+    testGetLowestPriority();
+}
+
+TEST_F(DrmSessionManagerTest, getLeastUsedSession_l) {
+    testGetLeastUsedSession();
+}
+
+} // namespace android
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 7d313e0..31e10ce 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -452,61 +452,61 @@
 
 void ACodec::initiateSetup(const sp<AMessage> &msg) {
     msg->setWhat(kWhatSetup);
-    msg->setTarget(id());
+    msg->setTarget(this);
     msg->post();
 }
 
 void ACodec::signalSetParameters(const sp<AMessage> &params) {
-    sp<AMessage> msg = new AMessage(kWhatSetParameters, id());
+    sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
     msg->setMessage("params", params);
     msg->post();
 }
 
 void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) {
     msg->setWhat(kWhatAllocateComponent);
-    msg->setTarget(id());
+    msg->setTarget(this);
     msg->post();
 }
 
 void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) {
     msg->setWhat(kWhatConfigureComponent);
-    msg->setTarget(id());
+    msg->setTarget(this);
     msg->post();
 }
 
 void ACodec::initiateCreateInputSurface() {
-    (new AMessage(kWhatCreateInputSurface, id()))->post();
+    (new AMessage(kWhatCreateInputSurface, this))->post();
 }
 
 void ACodec::signalEndOfInputStream() {
-    (new AMessage(kWhatSignalEndOfInputStream, id()))->post();
+    (new AMessage(kWhatSignalEndOfInputStream, this))->post();
 }
 
 void ACodec::initiateStart() {
-    (new AMessage(kWhatStart, id()))->post();
+    (new AMessage(kWhatStart, this))->post();
 }
 
 void ACodec::signalFlush() {
     ALOGV("[%s] signalFlush", mComponentName.c_str());
-    (new AMessage(kWhatFlush, id()))->post();
+    (new AMessage(kWhatFlush, this))->post();
 }
 
 void ACodec::signalResume() {
-    (new AMessage(kWhatResume, id()))->post();
+    (new AMessage(kWhatResume, this))->post();
 }
 
 void ACodec::initiateShutdown(bool keepComponentAllocated) {
-    sp<AMessage> msg = new AMessage(kWhatShutdown, id());
+    sp<AMessage> msg = new AMessage(kWhatShutdown, this);
     msg->setInt32("keepComponentAllocated", keepComponentAllocated);
     msg->post();
     if (!keepComponentAllocated) {
         // ensure shutdown completes in 3 seconds
-        (new AMessage(kWhatReleaseCodecInstance, id()))->post(3000000);
+        (new AMessage(kWhatReleaseCodecInstance, this))->post(3000000);
     }
 }
 
 void ACodec::signalRequestIDRFrame() {
-    (new AMessage(kWhatRequestIDRFrame, id()))->post();
+    (new AMessage(kWhatRequestIDRFrame, this))->post();
 }
 
 // *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED ***
@@ -517,7 +517,7 @@
 void ACodec::signalSubmitOutputMetaDataBufferIfEOS_workaround() {
     if (mPortEOS[kPortIndexInput] && !mPortEOS[kPortIndexOutput] &&
             mMetaDataBuffersToSubmit > 0) {
-        (new AMessage(kWhatSubmitOutputMetaDataBufferIfEOS, id()))->post();
+        (new AMessage(kWhatSubmitOutputMetaDataBufferIfEOS, this))->post();
     }
 }
 
@@ -1676,6 +1676,11 @@
         err = setMinBufferSize(kPortIndexInput, 8192);  // XXX
     }
 
+    int32_t priority;
+    if (msg->findInt32("priority", &priority)) {
+        err = setPriority(priority);
+    }
+
     mBaseOutputFormat = outputFormat;
 
     CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK);
@@ -1686,6 +1691,22 @@
     return err;
 }
 
+status_t ACodec::setPriority(int32_t priority) {
+    if (priority < 0) {
+        return BAD_VALUE;
+    }
+    OMX_PARAM_U32TYPE config;
+    InitOMXParams(&config);
+    config.nU32 = (OMX_U32)priority;
+    status_t temp = mOMX->setConfig(
+            mNode, (OMX_INDEXTYPE)OMX_IndexConfigPriority,
+            &config, sizeof(config));
+    if (temp != OK) {
+        ALOGI("codec does not support config priority (err %d)", temp);
+    }
+    return OK;
+}
+
 status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
     OMX_PARAM_PORTDEFINITIONTYPE def;
     InitOMXParams(&def);
@@ -4298,7 +4319,7 @@
     info->mData->meta()->clear();
     notify->setBuffer("buffer", info->mData);
 
-    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
+    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec);
     reply->setInt32("buffer-id", info->mBufferID);
 
     notify->setMessage("reply", reply);
@@ -4558,7 +4579,7 @@
             }
 
             sp<AMessage> reply =
-                new AMessage(kWhatOutputBufferDrained, mCodec->id());
+                new AMessage(kWhatOutputBufferDrained, mCodec);
 
             if (!mCodec->mSentFormat && rangeLength > 0) {
                 mCodec->sendFormatChange(reply);
@@ -4834,7 +4855,7 @@
 
     sp<IOMX> omx = client.interface();
 
-    sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec->id());
+    sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec);
 
     mDeathNotifier = new DeathNotifier(notify);
     if (IInterface::asBinder(omx)->linkToDeath(mDeathNotifier) != OK) {
@@ -4909,7 +4930,7 @@
         return false;
     }
 
-    notify = new AMessage(kWhatOMXMessage, mCodec->id());
+    notify = new AMessage(kWhatOMXMessage, mCodec);
     observer->setNotificationMessage(notify);
 
     mCodec->mComponentName = componentName;
@@ -6000,7 +6021,7 @@
 
         case OMX_EventPortSettingsChanged:
         {
-            sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec->id());
+            sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec);
             msg->setInt32("type", omx_message::EVENT);
             msg->setInt32("node", mCodec->mNode);
             msg->setInt32("event", event);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 6d9bbae..177293d 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -31,6 +31,7 @@
         MediaAdapter.cpp                  \
         MediaBuffer.cpp                   \
         MediaBufferGroup.cpp              \
+        MediaClock.cpp                    \
         MediaCodec.cpp                    \
         MediaCodecList.cpp                \
         MediaCodecSource.cpp              \
@@ -46,6 +47,7 @@
         OMXClient.cpp                     \
         OMXCodec.cpp                      \
         OggExtractor.cpp                  \
+        ProcessInfo.cpp                   \
         SampleIterator.cpp                \
         SampleTable.cpp                   \
         SkipCutBuffer.cpp                 \
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
index 0c2ff15..77a652a 100644
--- a/media/libstagefright/HTTPBase.cpp
+++ b/media/libstagefright/HTTPBase.cpp
@@ -75,7 +75,11 @@
 bool HTTPBase::estimateBandwidth(int32_t *bandwidth_bps) {
     Mutex::Autolock autoLock(mLock);
 
-    if (mNumBandwidthHistoryItems < 2) {
+    // Do not do bandwidth estimation if we don't have enough samples, or
+    // total bytes download are too small (<64K).
+    // Bandwidth estimation from these samples can often shoot up and cause
+    // unwanted bw adaption behaviors.
+    if (mNumBandwidthHistoryItems < 2 || mTotalTransferBytes < 65536) {
         return false;
     }
 
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 4359fb9..ef07aa0 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -135,7 +135,7 @@
 
     mNotify = notify;
 
-    (new AMessage(kWhatStart, id()))->post();
+    (new AMessage(kWhatStart, this))->post();
 }
 
 void MPEG2TSWriter::SourceInfo::stop() {
@@ -361,7 +361,7 @@
 }
 
 void MPEG2TSWriter::SourceInfo::readMore() {
-    (new AMessage(kWhatRead, id()))->post();
+    (new AMessage(kWhatRead, this))->post();
 }
 
 void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) {
@@ -552,7 +552,7 @@
 
     for (size_t i = 0; i < mSources.size(); ++i) {
         sp<AMessage> notify =
-            new AMessage(kWhatSourceNotify, mReflector->id());
+            new AMessage(kWhatSourceNotify, mReflector);
 
         notify->setInt32("source-index", i);
 
diff --git a/media/libmediaplayerservice/nuplayer/MediaClock.cpp b/media/libstagefright/MediaClock.cpp
similarity index 98%
rename from media/libmediaplayerservice/nuplayer/MediaClock.cpp
rename to media/libstagefright/MediaClock.cpp
index 9152da1..38db5e4 100644
--- a/media/libmediaplayerservice/nuplayer/MediaClock.cpp
+++ b/media/libstagefright/MediaClock.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "MediaClock"
 #include <utils/Log.h>
 
-#include "MediaClock.h"
+#include <media/stagefright/MediaClock.h>
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 50e6bd0..0597f1d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -174,7 +174,7 @@
 }
 
 // static
-void MediaCodec::PostReplyWithError(int32_t replyID, int32_t err) {
+void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) {
     sp<AMessage> response = new AMessage;
     response->setInt32("err", err);
     response->postReply(replyID);
@@ -237,9 +237,9 @@
 
     mLooper->registerHandler(this);
 
-    mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, id()));
+    mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, this));
 
-    sp<AMessage> msg = new AMessage(kWhatInit, id());
+    sp<AMessage> msg = new AMessage(kWhatInit, this);
     msg->setString("name", name);
     msg->setInt32("nameIsType", nameIsType);
 
@@ -252,7 +252,7 @@
 }
 
 status_t MediaCodec::setCallback(const sp<AMessage> &callback) {
-    sp<AMessage> msg = new AMessage(kWhatSetCallback, id());
+    sp<AMessage> msg = new AMessage(kWhatSetCallback, this);
     msg->setMessage("callback", callback);
 
     sp<AMessage> response;
@@ -264,7 +264,7 @@
         const sp<Surface> &nativeWindow,
         const sp<ICrypto> &crypto,
         uint32_t flags) {
-    sp<AMessage> msg = new AMessage(kWhatConfigure, id());
+    sp<AMessage> msg = new AMessage(kWhatConfigure, this);
 
     msg->setMessage("format", format);
     msg->setInt32("flags", flags);
@@ -298,7 +298,7 @@
 
 status_t MediaCodec::createInputSurface(
         sp<IGraphicBufferProducer>* bufferProducer) {
-    sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, id());
+    sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, this);
 
     sp<AMessage> response;
     status_t err = PostAndAwaitResponse(msg, &response);
@@ -317,21 +317,21 @@
 }
 
 status_t MediaCodec::start() {
-    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    sp<AMessage> msg = new AMessage(kWhatStart, this);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t MediaCodec::stop() {
-    sp<AMessage> msg = new AMessage(kWhatStop, id());
+    sp<AMessage> msg = new AMessage(kWhatStop, this);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t MediaCodec::release() {
-    sp<AMessage> msg = new AMessage(kWhatRelease, id());
+    sp<AMessage> msg = new AMessage(kWhatRelease, this);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
@@ -383,7 +383,7 @@
         errorDetailMsg->clear();
     }
 
-    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
     msg->setSize("index", index);
     msg->setSize("offset", offset);
     msg->setSize("size", size);
@@ -410,7 +410,7 @@
         errorDetailMsg->clear();
     }
 
-    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
     msg->setSize("index", index);
     msg->setSize("offset", offset);
     msg->setPointer("subSamples", (void *)subSamples);
@@ -429,7 +429,7 @@
 }
 
 status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
-    sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);
     msg->setInt64("timeoutUs", timeoutUs);
 
     sp<AMessage> response;
@@ -450,7 +450,7 @@
         int64_t *presentationTimeUs,
         uint32_t *flags,
         int64_t timeoutUs) {
-    sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, this);
     msg->setInt64("timeoutUs", timeoutUs);
 
     sp<AMessage> response;
@@ -469,7 +469,7 @@
 }
 
 status_t MediaCodec::renderOutputBufferAndRelease(size_t index) {
-    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this);
     msg->setSize("index", index);
     msg->setInt32("render", true);
 
@@ -478,7 +478,7 @@
 }
 
 status_t MediaCodec::renderOutputBufferAndRelease(size_t index, int64_t timestampNs) {
-    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this);
     msg->setSize("index", index);
     msg->setInt32("render", true);
     msg->setInt64("timestampNs", timestampNs);
@@ -488,7 +488,7 @@
 }
 
 status_t MediaCodec::releaseOutputBuffer(size_t index) {
-    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this);
     msg->setSize("index", index);
 
     sp<AMessage> response;
@@ -496,14 +496,14 @@
 }
 
 status_t MediaCodec::signalEndOfInputStream() {
-    sp<AMessage> msg = new AMessage(kWhatSignalEndOfInputStream, id());
+    sp<AMessage> msg = new AMessage(kWhatSignalEndOfInputStream, this);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
-    sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id());
+    sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, this);
 
     sp<AMessage> response;
     status_t err;
@@ -517,7 +517,7 @@
 }
 
 status_t MediaCodec::getInputFormat(sp<AMessage> *format) const {
-    sp<AMessage> msg = new AMessage(kWhatGetInputFormat, id());
+    sp<AMessage> msg = new AMessage(kWhatGetInputFormat, this);
 
     sp<AMessage> response;
     status_t err;
@@ -531,7 +531,7 @@
 }
 
 status_t MediaCodec::getName(AString *name) const {
-    sp<AMessage> msg = new AMessage(kWhatGetName, id());
+    sp<AMessage> msg = new AMessage(kWhatGetName, this);
 
     sp<AMessage> response;
     status_t err;
@@ -545,7 +545,7 @@
 }
 
 status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
-    sp<AMessage> msg = new AMessage(kWhatGetBuffers, id());
+    sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
     msg->setInt32("portIndex", kPortIndexInput);
     msg->setPointer("buffers", buffers);
 
@@ -554,7 +554,7 @@
 }
 
 status_t MediaCodec::getOutputBuffers(Vector<sp<ABuffer> > *buffers) const {
-    sp<AMessage> msg = new AMessage(kWhatGetBuffers, id());
+    sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
     msg->setInt32("portIndex", kPortIndexOutput);
     msg->setPointer("buffers", buffers);
 
@@ -612,20 +612,20 @@
 }
 
 status_t MediaCodec::flush() {
-    sp<AMessage> msg = new AMessage(kWhatFlush, id());
+    sp<AMessage> msg = new AMessage(kWhatFlush, this);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t MediaCodec::requestIDRFrame() {
-    (new AMessage(kWhatRequestIDRFrame, id()))->post();
+    (new AMessage(kWhatRequestIDRFrame, this))->post();
 
     return OK;
 }
 
 void MediaCodec::requestActivityNotification(const sp<AMessage> &notify) {
-    sp<AMessage> msg = new AMessage(kWhatRequestActivityNotification, id());
+    sp<AMessage> msg = new AMessage(kWhatRequestActivityNotification, this);
     msg->setMessage("notify", notify);
     msg->post();
 }
@@ -650,7 +650,7 @@
     }
 }
 
-bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) {
+bool MediaCodec::handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
     if (!isExecuting() || (mFlags & kFlagIsAsync)
             || (newRequest && (mFlags & kFlagDequeueInputPending))) {
         PostReplyWithError(replyID, INVALID_OPERATION);
@@ -674,7 +674,7 @@
     return true;
 }
 
-bool MediaCodec::handleDequeueOutputBuffer(uint32_t replyID, bool newRequest) {
+bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
     sp<AMessage> response = new AMessage;
 
     if (!isExecuting() || (mFlags & kFlagIsAsync)
@@ -1198,7 +1198,7 @@
 
         case kWhatInit:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != UNINITIALIZED) {
@@ -1234,7 +1234,7 @@
 
         case kWhatSetCallback:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState == UNINITIALIZED
@@ -1266,7 +1266,7 @@
 
         case kWhatConfigure:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != INITIALIZED) {
@@ -1323,7 +1323,7 @@
 
         case kWhatCreateInputSurface:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             // Must be configured, but can't have been started yet.
@@ -1339,7 +1339,7 @@
 
         case kWhatStart:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState == FLUSHED) {
@@ -1365,7 +1365,7 @@
             State targetState =
                 (msg->what() == kWhatStop) ? INITIALIZED : UNINITIALIZED;
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (!((mFlags & kFlagIsComponentAllocated) && targetState == UNINITIALIZED) // See 1
@@ -1413,7 +1413,7 @@
 
         case kWhatDequeueInputBuffer:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mFlags & kFlagIsAsync) {
@@ -1445,7 +1445,7 @@
 
             if (timeoutUs > 0ll) {
                 sp<AMessage> timeoutMsg =
-                    new AMessage(kWhatDequeueInputTimedOut, id());
+                    new AMessage(kWhatDequeueInputTimedOut, this);
                 timeoutMsg->setInt32(
                         "generation", ++mDequeueInputTimeoutGeneration);
                 timeoutMsg->post(timeoutUs);
@@ -1474,7 +1474,7 @@
 
         case kWhatQueueInputBuffer:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (!isExecuting()) {
@@ -1493,7 +1493,7 @@
 
         case kWhatDequeueOutputBuffer:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mFlags & kFlagIsAsync) {
@@ -1519,7 +1519,7 @@
 
             if (timeoutUs > 0ll) {
                 sp<AMessage> timeoutMsg =
-                    new AMessage(kWhatDequeueOutputTimedOut, id());
+                    new AMessage(kWhatDequeueOutputTimedOut, this);
                 timeoutMsg->setInt32(
                         "generation", ++mDequeueOutputTimeoutGeneration);
                 timeoutMsg->post(timeoutUs);
@@ -1548,7 +1548,7 @@
 
         case kWhatReleaseOutputBuffer:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (!isExecuting()) {
@@ -1567,7 +1567,7 @@
 
         case kWhatSignalEndOfInputStream:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (!isExecuting()) {
@@ -1585,7 +1585,7 @@
 
         case kWhatGetBuffers:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (!isExecuting() || (mFlags & kFlagIsAsync)) {
@@ -1619,7 +1619,7 @@
 
         case kWhatFlush:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (!isExecuting()) {
@@ -1645,7 +1645,7 @@
             sp<AMessage> format =
                 (msg->what() == kWhatGetOutputFormat ? mOutputFormat : mInputFormat);
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if ((mState != CONFIGURED && mState != STARTING &&
@@ -1682,7 +1682,7 @@
 
         case kWhatGetName:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mComponentName.empty()) {
@@ -1698,7 +1698,7 @@
 
         case kWhatSetParameters:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             sp<AMessage> params;
@@ -1752,7 +1752,7 @@
 
     AString errorDetailMsg;
 
-    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id());
+    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
     msg->setSize("index", bufferIndex);
     msg->setSize("offset", 0);
     msg->setSize("size", csd->size());
@@ -2207,7 +2207,7 @@
 }
 
 status_t MediaCodec::setParameters(const sp<AMessage> &params) {
-    sp<AMessage> msg = new AMessage(kWhatSetParameters, id());
+    sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
     msg->setMessage("params", params);
 
     sp<AMessage> response;
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index c26e909..b6fa810 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -121,7 +121,7 @@
     mLooper->registerHandler(this);
     mNotify = notify;
 
-    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    sp<AMessage> msg = new AMessage(kWhatStart, this);
     msg->setObject("meta", meta);
     return postSynchronouslyAndReturnError(msg);
 }
@@ -137,19 +137,19 @@
     mSource->stop();
     ALOGV("source (%s) stopped", mIsAudio ? "audio" : "video");
 
-    (new AMessage(kWhatStop, id()))->post();
+    (new AMessage(kWhatStop, this))->post();
 }
 
 void MediaCodecSource::Puller::pause() {
-    (new AMessage(kWhatPause, id()))->post();
+    (new AMessage(kWhatPause, this))->post();
 }
 
 void MediaCodecSource::Puller::resume() {
-    (new AMessage(kWhatResume, id()))->post();
+    (new AMessage(kWhatResume, this))->post();
 }
 
 void MediaCodecSource::Puller::schedulePull() {
-    sp<AMessage> msg = new AMessage(kWhatPull, id());
+    sp<AMessage> msg = new AMessage(kWhatPull, this);
     msg->setInt32("generation", mPullGeneration);
     msg->post();
 }
@@ -182,7 +182,7 @@
             sp<AMessage> response = new AMessage;
             response->setInt32("err", err);
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
             response->postReply(replyID);
             break;
@@ -269,13 +269,13 @@
 }
 
 status_t MediaCodecSource::start(MetaData* params) {
-    sp<AMessage> msg = new AMessage(kWhatStart, mReflector->id());
+    sp<AMessage> msg = new AMessage(kWhatStart, mReflector);
     msg->setObject("meta", params);
     return postSynchronouslyAndReturnError(msg);
 }
 
 status_t MediaCodecSource::stop() {
-    sp<AMessage> msg = new AMessage(kWhatStop, mReflector->id());
+    sp<AMessage> msg = new AMessage(kWhatStop, mReflector);
     status_t err = postSynchronouslyAndReturnError(msg);
 
     // mPuller->stop() needs to be done outside MediaCodecSource's looper,
@@ -294,7 +294,7 @@
 }
 
 status_t MediaCodecSource::pause() {
-    (new AMessage(kWhatPause, mReflector->id()))->post();
+    (new AMessage(kWhatPause, mReflector))->post();
     return OK;
 }
 
@@ -422,8 +422,7 @@
         }
     }
 
-    mEncoderActivityNotify = new AMessage(
-            kWhatEncoderActivity, mReflector->id());
+    mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
     mEncoder->setCallback(mEncoderActivityNotify);
 
     err = mEncoder->start();
@@ -492,7 +491,7 @@
     if (mStopping && mEncoderReachedEOS) {
         ALOGI("encoder (%s) stopped", mIsVideo ? "video" : "audio");
         // posting reply to everyone that's waiting
-        List<uint32_t>::iterator it;
+        List<sp<AReplyToken>>::iterator it;
         for (it = mStopReplyIDQueue.begin();
                 it != mStopReplyIDQueue.end(); it++) {
             (new AMessage)->postReply(*it);
@@ -620,8 +619,7 @@
         resume(startTimeUs);
     } else {
         CHECK(mPuller != NULL);
-        sp<AMessage> notify = new AMessage(
-                kWhatPullerNotify, mReflector->id());
+        sp<AMessage> notify = new AMessage(kWhatPullerNotify, mReflector);
         err = mPuller->start(params, notify);
         if (err != OK) {
             return err;
@@ -768,7 +766,7 @@
     }
     case kWhatStart:
     {
-        uint32_t replyID;
+        sp<AReplyToken> replyID;
         CHECK(msg->senderAwaitsResponse(&replyID));
 
         sp<RefBase> obj;
@@ -784,7 +782,7 @@
     {
         ALOGI("encoder (%s) stopping", mIsVideo ? "video" : "audio");
 
-        uint32_t replyID;
+        sp<AReplyToken> replyID;
         CHECK(msg->senderAwaitsResponse(&replyID));
 
         if (mEncoderReachedEOS) {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 7d7d631..8d70e50 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -226,7 +226,7 @@
     mLooper->start(false /* runOnCallingThread */, true /* canCallJava */);
 
     Mutex::Autolock autoLock(mLock);
-    (new AMessage(kWhatFetchMore, mReflector->id()))->post();
+    (new AMessage(kWhatFetchMore, mReflector))->post();
 }
 
 NuCachedSource2::~NuCachedSource2() {
@@ -433,7 +433,7 @@
         delayUs = 100000ll;
     }
 
-    (new AMessage(kWhatFetchMore, mReflector->id()))->post(delayUs);
+    (new AMessage(kWhatFetchMore, mReflector))->post(delayUs);
 }
 
 void NuCachedSource2::onRead(const sp<AMessage> &msg) {
@@ -522,7 +522,7 @@
         return size;
     }
 
-    sp<AMessage> msg = new AMessage(kWhatRead, mReflector->id());
+    sp<AMessage> msg = new AMessage(kWhatRead, mReflector);
     msg->setInt64("offset", offset);
     msg->setPointer("data", data);
     msg->setSize("size", size);
diff --git a/media/libstagefright/ProcessInfo.cpp b/media/libstagefright/ProcessInfo.cpp
new file mode 100644
index 0000000..b4172b3
--- /dev/null
+++ b/media/libstagefright/ProcessInfo.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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 "ProcessInfo"
+#include <utils/Log.h>
+
+#include <media/stagefright/ProcessInfo.h>
+
+#include <binder/IProcessInfoService.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+ProcessInfo::ProcessInfo() {}
+
+bool ProcessInfo::getPriority(int pid, int* priority) {
+    sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo"));
+    sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder);
+
+    size_t length = 1;
+    int32_t states;
+    status_t err = service->getProcessStatesFromPids(length, &pid, &states);
+    if (err != OK) {
+        ALOGE("getProcessStatesFromPids failed");
+        return false;
+    }
+    ALOGV("pid %d states %d", pid, states);
+    if (states < 0) {
+        return false;
+    }
+
+    // Use process state as the priority. Lower the value, higher the priority.
+    *priority = states;
+    return true;
+}
+
+ProcessInfo::~ProcessInfo() {}
+
+}  // namespace android
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 530383b..e8abf48 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -26,6 +26,7 @@
 #include <media/hardware/MetadataBufferType.h>
 
 #include <ui/GraphicBuffer.h>
+#include <gui/BufferItem.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/IGraphicBufferAlloc.h>
 #include <OMX_Component.h>
@@ -290,7 +291,7 @@
     // TODO: mCurrentSlot can be made a bufferstate since there
     // can be more than one "current" slots.
 
-    BufferQueue::BufferItem item;
+    BufferItem item;
     // If the recording has started and the queue is empty, then just
     // wait here till the frames come in from the client side
     while (mStarted) {
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 5ec3438..8ef2dca 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -26,6 +26,7 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
+#include <utils/misc.h>
 
 namespace android {
 
@@ -186,17 +187,31 @@
             if (aspect_ratio_idc == 255 /* extendedSAR */) {
                 sar_width = br.getBits(16);
                 sar_height = br.getBits(16);
-            } else if (aspect_ratio_idc > 0 && aspect_ratio_idc < 14) {
-                static const int32_t kFixedSARWidth[] = {
-                    1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160
+            } else {
+                static const struct { unsigned width, height; } kFixedSARs[] = {
+                        {   0,  0 }, // Invalid
+                        {   1,  1 },
+                        {  12, 11 },
+                        {  10, 11 },
+                        {  16, 11 },
+                        {  40, 33 },
+                        {  24, 11 },
+                        {  20, 11 },
+                        {  32, 11 },
+                        {  80, 33 },
+                        {  18, 11 },
+                        {  15, 11 },
+                        {  64, 33 },
+                        { 160, 99 },
+                        {   4,  3 },
+                        {   3,  2 },
+                        {   2,  1 },
                 };
 
-                static const int32_t kFixedSARHeight[] = {
-                    1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99
-                };
-
-                sar_width = kFixedSARWidth[aspect_ratio_idc - 1];
-                sar_height = kFixedSARHeight[aspect_ratio_idc - 1];
+                if (aspect_ratio_idc > 0 && aspect_ratio_idc < NELEM(kFixedSARs)) {
+                    sar_width = kFixedSARs[aspect_ratio_idc].width;
+                    sar_height = kFixedSARs[aspect_ratio_idc].height;
+                }
             }
         }
 
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 1505f08..10937ec 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -975,6 +975,7 @@
         mBufferSizes.clear();
         mDecodedSizes.clear();
         mLastInHeader = NULL;
+        mEndOfInput = false;
     } else {
         int avail;
         while ((avail = outputDelayRingBufferSamplesAvailable()) > 0) {
@@ -989,6 +990,7 @@
             mOutputBufferCount++;
         }
         mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos;
+        mEndOfOutput = false;
     }
 }
 
diff --git a/media/libstagefright/filters/GraphicBufferListener.cpp b/media/libstagefright/filters/GraphicBufferListener.cpp
index fa38192..66374ba 100644
--- a/media/libstagefright/filters/GraphicBufferListener.cpp
+++ b/media/libstagefright/filters/GraphicBufferListener.cpp
@@ -21,6 +21,8 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaErrors.h>
 
+#include <gui/BufferItem.h>
+
 #include "GraphicBufferListener.h"
 
 namespace android {
@@ -83,8 +85,8 @@
     // nothing to do
 }
 
-BufferQueue::BufferItem GraphicBufferListener::getBufferItem() {
-    BufferQueue::BufferItem item;
+BufferItem GraphicBufferListener::getBufferItem() {
+    BufferItem item;
 
     {
         Mutex::Autolock autoLock(mMutex);
@@ -124,8 +126,7 @@
     return item;
 }
 
-sp<GraphicBuffer> GraphicBufferListener::getBuffer(
-        BufferQueue::BufferItem item) {
+sp<GraphicBuffer> GraphicBufferListener::getBuffer(BufferItem item) {
     sp<GraphicBuffer> buf;
     if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) {
         ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf);
@@ -138,8 +139,7 @@
     return buf;
 }
 
-status_t GraphicBufferListener::releaseBuffer(
-        BufferQueue::BufferItem item) {
+status_t GraphicBufferListener::releaseBuffer(BufferItem item) {
     if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) {
         ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf);
         return ERROR_OUT_OF_RANGE;
diff --git a/media/libstagefright/filters/GraphicBufferListener.h b/media/libstagefright/filters/GraphicBufferListener.h
index b3e0ee3..586bf65 100644
--- a/media/libstagefright/filters/GraphicBufferListener.h
+++ b/media/libstagefright/filters/GraphicBufferListener.h
@@ -41,9 +41,9 @@
         return mProducer;
     }
 
-    BufferQueue::BufferItem getBufferItem();
-    sp<GraphicBuffer> getBuffer(BufferQueue::BufferItem item);
-    status_t releaseBuffer(BufferQueue::BufferItem item);
+    BufferItem getBufferItem();
+    sp<GraphicBuffer> getBuffer(BufferItem item);
+    status_t releaseBuffer(BufferItem item);
 
     enum {
         kWhatFrameAvailable = 'frav',
diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp
index c5289b6..0a09575 100644
--- a/media/libstagefright/filters/MediaFilter.cpp
+++ b/media/libstagefright/filters/MediaFilter.cpp
@@ -31,6 +31,8 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaFilter.h>
 
+#include <gui/BufferItem.h>
+
 #include "ColorConvert.h"
 #include "GraphicBufferListener.h"
 #include "IntrinsicBlurFilter.h"
@@ -60,36 +62,36 @@
 
 void MediaFilter::initiateAllocateComponent(const sp<AMessage> &msg) {
     msg->setWhat(kWhatAllocateComponent);
-    msg->setTarget(id());
+    msg->setTarget(this);
     msg->post();
 }
 
 void MediaFilter::initiateConfigureComponent(const sp<AMessage> &msg) {
     msg->setWhat(kWhatConfigureComponent);
-    msg->setTarget(id());
+    msg->setTarget(this);
     msg->post();
 }
 
 void MediaFilter::initiateCreateInputSurface() {
-    (new AMessage(kWhatCreateInputSurface, id()))->post();
+    (new AMessage(kWhatCreateInputSurface, this))->post();
 }
 
 void MediaFilter::initiateStart() {
-    (new AMessage(kWhatStart, id()))->post();
+    (new AMessage(kWhatStart, this))->post();
 }
 
 void MediaFilter::initiateShutdown(bool keepComponentAllocated) {
-    sp<AMessage> msg = new AMessage(kWhatShutdown, id());
+    sp<AMessage> msg = new AMessage(kWhatShutdown, this);
     msg->setInt32("keepComponentAllocated", keepComponentAllocated);
     msg->post();
 }
 
 void MediaFilter::signalFlush() {
-    (new AMessage(kWhatFlush, id()))->post();
+    (new AMessage(kWhatFlush, this))->post();
 }
 
 void MediaFilter::signalResume() {
-    (new AMessage(kWhatResume, id()))->post();
+    (new AMessage(kWhatResume, this))->post();
 }
 
 // nothing to do
@@ -98,13 +100,13 @@
 }
 
 void MediaFilter::signalSetParameters(const sp<AMessage> &params) {
-    sp<AMessage> msg = new AMessage(kWhatSetParameters, id());
+    sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
     msg->setMessage("params", params);
     msg->post();
 }
 
 void MediaFilter::signalEndOfInputStream() {
-    (new AMessage(kWhatSignalEndOfInputStream, id()))->post();
+    (new AMessage(kWhatSignalEndOfInputStream, this))->post();
 }
 
 void MediaFilter::onMessageReceived(const sp<AMessage> &msg) {
@@ -208,7 +210,7 @@
 //////////////////// HELPER FUNCTIONS //////////////////////////////////////////
 
 void MediaFilter::signalProcessBuffers() {
-    (new AMessage(kWhatProcessBuffers, id()))->post();
+    (new AMessage(kWhatProcessBuffers, this))->post();
 }
 
 void MediaFilter::signalError(status_t error) {
@@ -309,7 +311,7 @@
     info->mData->meta()->clear();
     notify->setBuffer("buffer", info->mData);
 
-    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
+    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, this);
     reply->setInt32("buffer-id", info->mBufferID);
 
     notify->setMessage("reply", reply);
@@ -329,7 +331,7 @@
     notify->setInt32("flags", info->mOutputFlags);
     notify->setBuffer("buffer", info->mData);
 
-    sp<AMessage> reply = new AMessage(kWhatOutputBufferDrained, id());
+    sp<AMessage> reply = new AMessage(kWhatOutputBufferDrained, this);
     reply->setInt32("buffer-id", info->mBufferID);
 
     notify->setMessage("reply", reply);
@@ -729,7 +731,7 @@
     mGraphicBufferListener = new GraphicBufferListener;
 
     sp<AMessage> notify = new AMessage();
-    notify->setTarget(id());
+    notify->setTarget(this);
     status_t err = mGraphicBufferListener->init(
             notify, mStride, mSliceHeight, kBufferCountActual);
 
@@ -749,7 +751,7 @@
 }
 
 void MediaFilter::onInputFrameAvailable() {
-    BufferQueue::BufferItem item = mGraphicBufferListener->getBufferItem();
+    BufferItem item = mGraphicBufferListener->getBufferItem();
     sp<GraphicBuffer> buf = mGraphicBufferListener->getBuffer(item);
 
     // get pointer to graphic buffer
diff --git a/media/libstagefright/foundation/AHandler.cpp b/media/libstagefright/foundation/AHandler.cpp
index bd5f7e9..7dbbe54 100644
--- a/media/libstagefright/foundation/AHandler.cpp
+++ b/media/libstagefright/foundation/AHandler.cpp
@@ -19,15 +19,23 @@
 #include <utils/Log.h>
 
 #include <media/stagefright/foundation/AHandler.h>
-
-#include <media/stagefright/foundation/ALooperRoster.h>
+#include <media/stagefright/foundation/AMessage.h>
 
 namespace android {
 
-sp<ALooper> AHandler::looper() {
-    extern ALooperRoster gLooperRoster;
+void AHandler::deliverMessage(const sp<AMessage> &msg) {
+    onMessageReceived(msg);
+    mMessageCounter++;
 
-    return gLooperRoster.findLooper(id());
+    if (mVerboseStats) {
+        uint32_t what = msg->what();
+        ssize_t idx = mMessages.indexOfKey(what);
+        if (idx < 0) {
+            mMessages.add(what, 1);
+        } else {
+            mMessages.editValueAt(idx)++;
+        }
+    }
 }
 
 }  // namespace android
diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp
index 88b1c92..90b5f68 100644
--- a/media/libstagefright/foundation/ALooper.cpp
+++ b/media/libstagefright/foundation/ALooper.cpp
@@ -16,6 +16,9 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "ALooper"
+
+#include <media/stagefright/foundation/ADebug.h>
+
 #include <utils/Log.h>
 
 #include <sys/time.h>
@@ -210,7 +213,7 @@
         mEventQueue.erase(mEventQueue.begin());
     }
 
-    gLooperRoster.deliverMessage(event.mMessage);
+    event.mMessage->deliver();
 
     // NOTE: It's important to note that at this point our "ALooper" object
     // may no longer exist (its final reference may have gone away while
@@ -220,4 +223,29 @@
     return true;
 }
 
+// to be called by AMessage::postAndAwaitResponse only
+sp<AReplyToken> ALooper::createReplyToken() {
+    return new AReplyToken(this);
+}
+
+// to be called by AMessage::postAndAwaitResponse only
+status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) {
+    // return status in case we want to handle an interrupted wait
+    Mutex::Autolock autoLock(mRepliesLock);
+    CHECK(replyToken != NULL);
+    while (!replyToken->retrieveReply(response)) {
+        mRepliesCondition.wait(mRepliesLock);
+    }
+    return OK;
+}
+
+status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {
+    Mutex::Autolock autoLock(mRepliesLock);
+    status_t err = replyToken->setReply(reply);
+    if (err == OK) {
+        mRepliesCondition.broadcast();
+    }
+    return err;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp
index 2d57aee..473ce1b 100644
--- a/media/libstagefright/foundation/ALooperRoster.cpp
+++ b/media/libstagefright/foundation/ALooperRoster.cpp
@@ -30,8 +30,7 @@
 static bool verboseStats = false;
 
 ALooperRoster::ALooperRoster()
-    : mNextHandlerID(1),
-      mNextReplyID(1) {
+    : mNextHandlerID(1) {
 }
 
 ALooper::handler_id ALooperRoster::registerHandler(
@@ -49,7 +48,7 @@
     ALooper::handler_id handlerID = mNextHandlerID++;
     mHandlers.add(handlerID, info);
 
-    handler->setID(handlerID);
+    handler->setID(handlerID, looper);
 
     return handlerID;
 }
@@ -68,7 +67,7 @@
     sp<AHandler> handler = info.mHandler.promote();
 
     if (handler != NULL) {
-        handler->setID(0);
+        handler->setID(0, NULL);
     }
 
     mHandlers.removeItemsAt(index);
@@ -100,116 +99,6 @@
     }
 }
 
-status_t ALooperRoster::postMessage(
-        const sp<AMessage> &msg, int64_t delayUs) {
-
-    sp<ALooper> looper = findLooper(msg->target());
-
-    if (looper == NULL) {
-        return -ENOENT;
-    }
-    looper->post(msg, delayUs);
-    return OK;
-}
-
-void ALooperRoster::deliverMessage(const sp<AMessage> &msg) {
-    sp<AHandler> handler;
-
-    {
-        Mutex::Autolock autoLock(mLock);
-
-        ssize_t index = mHandlers.indexOfKey(msg->target());
-
-        if (index < 0) {
-            ALOGW("failed to deliver message. Target handler not registered.");
-            return;
-        }
-
-        const HandlerInfo &info = mHandlers.valueAt(index);
-        handler = info.mHandler.promote();
-
-        if (handler == NULL) {
-            ALOGW("failed to deliver message. "
-                 "Target handler %d registered, but object gone.",
-                 msg->target());
-
-            mHandlers.removeItemsAt(index);
-            return;
-        }
-    }
-
-    handler->onMessageReceived(msg);
-    handler->mMessageCounter++;
-
-    if (verboseStats) {
-        uint32_t what = msg->what();
-        ssize_t idx = handler->mMessages.indexOfKey(what);
-        if (idx < 0) {
-            handler->mMessages.add(what, 1);
-        } else {
-            handler->mMessages.editValueAt(idx)++;
-        }
-    }
-}
-
-sp<ALooper> ALooperRoster::findLooper(ALooper::handler_id handlerID) {
-    Mutex::Autolock autoLock(mLock);
-
-    ssize_t index = mHandlers.indexOfKey(handlerID);
-
-    if (index < 0) {
-        return NULL;
-    }
-
-    sp<ALooper> looper = mHandlers.valueAt(index).mLooper.promote();
-
-    if (looper == NULL) {
-        mHandlers.removeItemsAt(index);
-        return NULL;
-    }
-
-    return looper;
-}
-
-status_t ALooperRoster::postAndAwaitResponse(
-        const sp<AMessage> &msg, sp<AMessage> *response) {
-    sp<ALooper> looper = findLooper(msg->target());
-
-    if (looper == NULL) {
-        ALOGW("failed to post message. "
-                "Target handler %d still registered, but object gone.",
-                msg->target());
-        response->clear();
-        return -ENOENT;
-    }
-
-    Mutex::Autolock autoLock(mLock);
-
-    uint32_t replyID = mNextReplyID++;
-
-    msg->setInt32("replyID", replyID);
-
-    looper->post(msg, 0 /* delayUs */);
-
-    ssize_t index;
-    while ((index = mReplies.indexOfKey(replyID)) < 0) {
-        mRepliesCondition.wait(mLock);
-    }
-
-    *response = mReplies.valueAt(index);
-    mReplies.removeItemsAt(index);
-
-    return OK;
-}
-
-void ALooperRoster::postReply(uint32_t replyID, const sp<AMessage> &reply) {
-    Mutex::Autolock autoLock(mLock);
-
-    CHECK(mReplies.indexOfKey(replyID) < 0);
-    mReplies.add(replyID, reply);
-    mRepliesCondition.broadcast();
-}
-
 static void makeFourCC(uint32_t fourcc, char *s) {
     s[0] = (fourcc >> 24) & 0xff;
     if (s[0]) {
@@ -225,7 +114,7 @@
 void ALooperRoster::dump(int fd, const Vector<String16>& args) {
     bool clear = false;
     bool oldVerbose = verboseStats;
-    for (size_t i = 0;i < args.size(); i++) {
+    for (size_t i = 0; i < args.size(); i++) {
         if (args[i] == String16("-c")) {
             clear = true;
         } else if (args[i] == String16("-von")) {
@@ -241,22 +130,23 @@
 
     Mutex::Autolock autoLock(mLock);
     size_t n = mHandlers.size();
-    s.appendFormat(" %zd registered handlers:\n", n);
+    s.appendFormat(" %zu registered handlers:\n", n);
 
     for (size_t i = 0; i < n; i++) {
-        s.appendFormat("  %zd: ", i);
+        s.appendFormat("  %d: ", mHandlers.keyAt(i));
         HandlerInfo &info = mHandlers.editValueAt(i);
         sp<ALooper> looper = info.mLooper.promote();
         if (looper != NULL) {
-            s.append(looper->mName.c_str());
+            s.append(looper->getName());
             sp<AHandler> handler = info.mHandler.promote();
             if (handler != NULL) {
+                handler->mVerboseStats = verboseStats;
                 s.appendFormat(": %u messages processed", handler->mMessageCounter);
                 if (verboseStats) {
                     for (size_t j = 0; j < handler->mMessages.size(); j++) {
                         char fourcc[15];
                         makeFourCC(handler->mMessages.keyAt(j), fourcc);
-                        s.appendFormat("\n    %s: %d",
+                        s.appendFormat("\n    %s: %u",
                                 fourcc,
                                 handler->mMessages.valueAt(j));
                     }
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 1f46bc9..e549ff6 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -27,6 +27,7 @@
 #include "ABuffer.h"
 #include "ADebug.h"
 #include "ALooperRoster.h"
+#include "AHandler.h"
 #include "AString.h"
 
 #include <binder/Parcel.h>
@@ -36,12 +37,29 @@
 
 extern ALooperRoster gLooperRoster;
 
-AMessage::AMessage(uint32_t what, ALooper::handler_id target)
-    : mWhat(what),
-      mTarget(target),
+status_t AReplyToken::setReply(const sp<AMessage> &reply) {
+    if (mReplied) {
+        ALOGE("trying to post a duplicate reply");
+        return -EBUSY;
+    }
+    CHECK(mReply == NULL);
+    mReply = reply;
+    mReplied = true;
+    return OK;
+}
+
+AMessage::AMessage(void)
+    : mWhat(0),
+      mTarget(0),
       mNumItems(0) {
 }
 
+AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler)
+    : mWhat(what),
+      mNumItems(0) {
+    setTarget(handler);
+}
+
 AMessage::~AMessage() {
     clear();
 }
@@ -54,12 +72,16 @@
     return mWhat;
 }
 
-void AMessage::setTarget(ALooper::handler_id handlerID) {
-    mTarget = handlerID;
-}
-
-ALooper::handler_id AMessage::target() const {
-    return mTarget;
+void AMessage::setTarget(const sp<const AHandler> &handler) {
+    if (handler == NULL) {
+        mTarget = 0;
+        mHandler.clear();
+        mLooper.clear();
+    } else {
+        mTarget = handler->id();
+        mHandler = handler->getHandler();
+        mLooper = handler->getLooper();
+    }
 }
 
 void AMessage::clear() {
@@ -322,33 +344,76 @@
     return true;
 }
 
-void AMessage::post(int64_t delayUs) {
-    gLooperRoster.postMessage(this, delayUs);
+void AMessage::deliver() {
+    sp<AHandler> handler = mHandler.promote();
+    if (handler == NULL) {
+        ALOGW("failed to deliver message as target handler %d is gone.", mTarget);
+        return;
+    }
+
+    handler->deliverMessage(this);
+}
+
+status_t AMessage::post(int64_t delayUs) {
+    sp<ALooper> looper = mLooper.promote();
+    if (looper == NULL) {
+        ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
+        return -ENOENT;
+    }
+
+    looper->post(this, delayUs);
+    return OK;
 }
 
 status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
-    return gLooperRoster.postAndAwaitResponse(this, response);
+    sp<ALooper> looper = mLooper.promote();
+    if (looper == NULL) {
+        ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
+        return -ENOENT;
+    }
+
+    sp<AReplyToken> token = looper->createReplyToken();
+    if (token == NULL) {
+        ALOGE("failed to create reply token");
+        return -ENOMEM;
+    }
+    setObject("replyID", token);
+
+    looper->post(this, 0 /* delayUs */);
+    return looper->awaitResponse(token, response);
 }
 
-void AMessage::postReply(uint32_t replyID) {
-    gLooperRoster.postReply(replyID, this);
+status_t AMessage::postReply(const sp<AReplyToken> &replyToken) {
+    if (replyToken == NULL) {
+        ALOGW("failed to post reply to a NULL token");
+        return -ENOENT;
+    }
+    sp<ALooper> looper = replyToken->getLooper();
+    if (looper == NULL) {
+        ALOGW("failed to post reply as target looper is gone.");
+        return -ENOENT;
+    }
+    return looper->postReply(replyToken, this);
 }
 
-bool AMessage::senderAwaitsResponse(uint32_t *replyID) const {
-    int32_t tmp;
-    bool found = findInt32("replyID", &tmp);
+bool AMessage::senderAwaitsResponse(sp<AReplyToken> *replyToken) {
+    sp<RefBase> tmp;
+    bool found = findObject("replyID", &tmp);
 
     if (!found) {
         return false;
     }
 
-    *replyID = static_cast<uint32_t>(tmp);
+    *replyToken = static_cast<AReplyToken *>(tmp.get());
+    tmp.clear();
+    setObject("replyID", tmp);
+    // TODO: delete Object instead of setting it to NULL
 
-    return true;
+    return *replyToken != NULL;
 }
 
 sp<AMessage> AMessage::dup() const {
-    sp<AMessage> msg = new AMessage(mWhat, mTarget);
+    sp<AMessage> msg = new AMessage(mWhat, mHandler.promote());
     msg->mNumItems = mNumItems;
 
 #ifdef DUMP_STATS
@@ -532,7 +597,8 @@
 // static
 sp<AMessage> AMessage::FromParcel(const Parcel &parcel) {
     int32_t what = parcel.readInt32();
-    sp<AMessage> msg = new AMessage(what);
+    sp<AMessage> msg = new AMessage();
+    msg->setWhat(what);
 
     msg->mNumItems = static_cast<size_t>(parcel.readInt32());
     for (size_t i = 0; i < msg->mNumItems; ++i) {
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index d0f3bc2..a94754b 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -49,8 +49,13 @@
 
 namespace android {
 
+// static
 // Number of recently-read bytes to use for bandwidth estimation
 const size_t LiveSession::kBandwidthHistoryBytes = 200 * 1024;
+// High water mark to start up switch or report prepared)
+const int64_t LiveSession::kHighWaterMark = 8000000ll;
+const int64_t LiveSession::kMidWaterMark = 5000000ll;
+const int64_t LiveSession::kLowWaterMark = 3000000ll;
 
 LiveSession::LiveSession(
         const sp<AMessage> &notify, uint32_t flags,
@@ -71,21 +76,18 @@
       mRealTimeBaseUs(0ll),
       mReconfigurationInProgress(false),
       mSwitchInProgress(false),
-      mDisconnectReplyID(0),
-      mSeekReplyID(0),
       mFirstTimeUsValid(false),
       mFirstTimeUs(0),
-      mLastSeekTimeUs(0) {
+      mLastSeekTimeUs(0),
+      mPollBufferingGeneration(0) {
 
     mStreams[kAudioIndex] = StreamItem("audio");
     mStreams[kVideoIndex] = StreamItem("video");
     mStreams[kSubtitleIndex] = StreamItem("subtitles");
 
     for (size_t i = 0; i < kMaxStreams; ++i) {
-        mDiscontinuities.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
         mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
         mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
-        mBuffering[i] = false;
     }
 
     size_t numHistoryItems = kBandwidthHistoryBytes /
@@ -97,6 +99,9 @@
 }
 
 LiveSession::~LiveSession() {
+    if (mFetcherLooper != NULL) {
+        mFetcherLooper->stop();
+    }
 }
 
 sp<ABuffer> LiveSession::createFormatChangeBuffer(bool swap) {
@@ -125,36 +130,30 @@
         return -EWOULDBLOCK;
     }
 
-    status_t finalResult;
-    sp<AnotherPacketSource> discontinuityQueue  = mDiscontinuities.valueFor(stream);
-    if (discontinuityQueue->hasBufferAvailable(&finalResult)) {
-        discontinuityQueue->dequeueAccessUnit(accessUnit);
-        // seeking, track switching
-        sp<AMessage> extra;
-        int64_t timeUs;
-        if ((*accessUnit)->meta()->findMessage("extra", &extra)
-                && extra != NULL
-                && extra->findInt64("timeUs", &timeUs)) {
-            // seeking only
-            mLastSeekTimeUs = timeUs;
-            mDiscontinuityOffsetTimesUs.clear();
-            mDiscontinuityAbsStartTimesUs.clear();
-        }
-        return INFO_DISCONTINUITY;
-    }
-
+    status_t finalResult = OK;
     sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
 
     ssize_t idx = typeToIndex(stream);
     if (!packetSource->hasBufferAvailable(&finalResult)) {
         if (finalResult == OK) {
-            mBuffering[idx] = true;
             return -EAGAIN;
         } else {
             return finalResult;
         }
     }
 
+    // Do not let client pull data if we don't have format yet.
+    // We might only have a format discontinuity queued without actual data.
+    // When NuPlayerDecoder dequeues the format discontinuity, it will
+    // immediately try to getFormat. If we return NULL, NuPlayerDecoder
+    // thinks it can do seamless change, so will not shutdown decoder.
+    // When the actual format arrives, it can't handle it and get stuck.
+    // TODO: We need a method to check if the packet source has any
+    //       data packets available, dequeuing should only start then.
+    sp<MetaData> format = packetSource->getFormat();
+    if (format == NULL) {
+        return -EAGAIN;
+    }
     int32_t targetDuration = 0;
     sp<AMessage> meta = packetSource->getLatestEnqueuedMeta();
     if (meta != NULL) {
@@ -169,18 +168,6 @@
         targetDurationUs = PlaylistFetcher::kMinBufferedDurationUs;
     }
 
-    if (mBuffering[idx]) {
-        if (mSwitchInProgress
-                || packetSource->isFinished(0)
-                || packetSource->getEstimatedDurationUs() > targetDurationUs) {
-            mBuffering[idx] = false;
-        }
-    }
-
-    if (mBuffering[idx]) {
-        return -EAGAIN;
-    }
-
     // wait for counterpart
     sp<AnotherPacketSource> otherSource;
     uint32_t mask = mNewStreamMask & mStreamMask;
@@ -244,7 +231,7 @@
                 Mutex::Autolock lock(mSwapMutex);
                 if (switchGeneration == mSwitchGeneration) {
                     swapPacketSource(stream);
-                    sp<AMessage> msg = new AMessage(kWhatSwapped, id());
+                    sp<AMessage> msg = new AMessage(kWhatSwapped, this);
                     msg->setInt32("stream", stream);
                     msg->setInt32("switchGeneration", switchGeneration);
                     msg->post();
@@ -349,7 +336,7 @@
 
 void LiveSession::connectAsync(
         const char *url, const KeyedVector<String8, String8> *headers) {
-    sp<AMessage> msg = new AMessage(kWhatConnect, id());
+    sp<AMessage> msg = new AMessage(kWhatConnect, this);
     msg->setString("url", url);
 
     if (headers != NULL) {
@@ -362,7 +349,7 @@
 }
 
 status_t LiveSession::disconnect() {
-    sp<AMessage> msg = new AMessage(kWhatDisconnect, id());
+    sp<AMessage> msg = new AMessage(kWhatDisconnect, this);
 
     sp<AMessage> response;
     status_t err = msg->postAndAwaitResponse(&response);
@@ -371,7 +358,7 @@
 }
 
 status_t LiveSession::seekTo(int64_t timeUs) {
-    sp<AMessage> msg = new AMessage(kWhatSeek, id());
+    sp<AMessage> msg = new AMessage(kWhatSeek, this);
     msg->setInt64("timeUs", timeUs);
 
     sp<AMessage> response;
@@ -402,7 +389,7 @@
 
         case kWhatSeek:
         {
-            uint32_t seekReplyID;
+            sp<AReplyToken> seekReplyID;
             CHECK(msg->senderAwaitsResponse(&seekReplyID));
             mSeekReplyID = seekReplyID;
             mSeekReply = new AMessage;
@@ -429,11 +416,16 @@
                     if (what == PlaylistFetcher::kWhatStopped) {
                         AString uri;
                         CHECK(msg->findString("uri", &uri));
-                        if (mFetcherInfos.removeItem(uri) < 0) {
+                        ssize_t index = mFetcherInfos.indexOfKey(uri);
+                        if (index < 0) {
                             // ignore duplicated kWhatStopped messages.
                             break;
                         }
 
+                        mFetcherLooper->unregisterHandler(
+                                mFetcherInfos[index].mFetcher->id());
+                        mFetcherInfos.removeItemsAt(index);
+
                         if (mSwitchInProgress) {
                             tryToFinishBandwidthSwitch();
                         }
@@ -443,14 +435,6 @@
                         CHECK_GT(mContinuationCounter, 0);
                         if (--mContinuationCounter == 0) {
                             mContinuation->post();
-
-                            if (mSeekReplyID != 0) {
-                                CHECK(mSeekReply != NULL);
-                                mSeekReply->setInt32("err", OK);
-                                mSeekReply->postReply(mSeekReplyID);
-                                mSeekReplyID = 0;
-                                mSeekReply.clear();
-                            }
                         }
                     }
                     break;
@@ -464,8 +448,11 @@
                     int64_t durationUs;
                     CHECK(msg->findInt64("durationUs", &durationUs));
 
-                    FetcherInfo *info = &mFetcherInfos.editValueFor(uri);
-                    info->mDurationUs = durationUs;
+                    ssize_t index = mFetcherInfos.indexOfKey(uri);
+                    if (index >= 0) {
+                        FetcherInfo *info = &mFetcherInfos.editValueFor(uri);
+                        info->mDurationUs = durationUs;
+                    }
                     break;
                 }
 
@@ -513,34 +500,6 @@
                     break;
                 }
 
-                case PlaylistFetcher::kWhatTemporarilyDoneFetching:
-                {
-                    AString uri;
-                    CHECK(msg->findString("uri", &uri));
-
-                    if (mFetcherInfos.indexOfKey(uri) < 0) {
-                        ALOGE("couldn't find uri");
-                        break;
-                    }
-                    FetcherInfo *info = &mFetcherInfos.editValueFor(uri);
-                    info->mIsPrepared = true;
-
-                    if (mInPreparationPhase) {
-                        bool allFetchersPrepared = true;
-                        for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
-                            if (!mFetcherInfos.valueAt(i).mIsPrepared) {
-                                allFetchersPrepared = false;
-                                break;
-                            }
-                        }
-
-                        if (allFetchersPrepared) {
-                            postPrepared(OK);
-                        }
-                    }
-                    break;
-                }
-
                 case PlaylistFetcher::kWhatStartedAt:
                 {
                     int32_t switchGeneration;
@@ -569,19 +528,6 @@
             break;
         }
 
-        case kWhatCheckBandwidth:
-        {
-            int32_t generation;
-            CHECK(msg->findInt32("generation", &generation));
-
-            if (generation != mCheckBandwidthGeneration) {
-                break;
-            }
-
-            onCheckBandwidth(msg);
-            break;
-        }
-
         case kWhatChangeConfiguration:
         {
             onChangeConfiguration(msg);
@@ -612,15 +558,13 @@
             break;
         }
 
-        case kWhatCheckSwitchDown:
+        case kWhatPollBuffering:
         {
-            onCheckSwitchDown();
-            break;
-        }
-
-        case kWhatSwitchDown:
-        {
-            onSwitchDown();
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation == mPollBufferingGeneration) {
+                onPollBuffering();
+            }
             break;
         }
 
@@ -691,6 +635,14 @@
         return;
     }
 
+    // create looper for fetchers
+    if (mFetcherLooper == NULL) {
+        mFetcherLooper = new ALooper();
+
+        mFetcherLooper->setName("Fetcher");
+        mFetcherLooper->start(false, false);
+    }
+
     // We trust the content provider to make a reasonable choice of preferred
     // initial bandwidth by listing it first in the variant playlist.
     // At startup we really don't have a good estimate on the available
@@ -739,25 +691,26 @@
     mPlaylist->pickRandomMediaItems();
     changeConfiguration(
             0ll /* timeUs */, initialBandwidthIndex, false /* pickTrack */);
+
+    schedulePollBuffering();
 }
 
 void LiveSession::finishDisconnect() {
     // No reconfiguration is currently pending, make sure none will trigger
     // during disconnection either.
-    cancelCheckBandwidthEvent();
 
     // Protect mPacketSources from a swapPacketSource race condition through disconnect.
     // (finishDisconnect, onFinishDisconnect2)
     cancelBandwidthSwitch();
 
-    // cancel switch down monitor
-    mSwitchDownMonitor.clear();
+    // cancel buffer polling
+    cancelPollBuffering();
 
     for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
         mFetcherInfos.valueAt(i).mFetcher->stopAsync();
     }
 
-    sp<AMessage> msg = new AMessage(kWhatFinishDisconnect2, id());
+    sp<AMessage> msg = new AMessage(kWhatFinishDisconnect2, this);
 
     mContinuationCounter = mFetcherInfos.size();
     mContinuation = msg;
@@ -780,7 +733,7 @@
     response->setInt32("err", OK);
 
     response->postReply(mDisconnectReplyID);
-    mDisconnectReplyID = 0;
+    mDisconnectReplyID.clear();
 }
 
 sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) {
@@ -790,7 +743,7 @@
         return NULL;
     }
 
-    sp<AMessage> notify = new AMessage(kWhatFetcherNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatFetcherNotify, this);
     notify->setString("uri", uri);
     notify->setInt32("switchGeneration", mSwitchGeneration);
 
@@ -799,7 +752,7 @@
     info.mDurationUs = -1ll;
     info.mIsPrepared = false;
     info.mToBeRemoved = false;
-    looper()->registerHandler(info.mFetcher);
+    mFetcherLooper->registerHandler(info.mFetcher);
 
     mFetcherInfos.add(uri, info);
 
@@ -1185,7 +1138,7 @@
     ++mSubtitleGeneration;
     status_t err = mPlaylist->selectTrack(index, select);
     if (err == OK) {
-        sp<AMessage> msg = new AMessage(kWhatChangeConfiguration, id());
+        sp<AMessage> msg = new AMessage(kWhatChangeConfiguration, this);
         msg->setInt32("bandwidthIndex", mCurBandwidthIndex);
         msg->setInt32("pickTrack", select);
         msg->post();
@@ -1201,19 +1154,6 @@
     }
 }
 
-bool LiveSession::canSwitchUp() {
-    // Allow upwards bandwidth switch when a stream has buffered at least 10 seconds.
-    status_t err = OK;
-    for (size_t i = 0; i < mPacketSources.size(); ++i) {
-        sp<AnotherPacketSource> source = mPacketSources.valueAt(i);
-        int64_t dur = source->getBufferedDurationUs(&err);
-        if (err == OK && dur > 10000000) {
-            return true;
-        }
-    }
-    return false;
-}
-
 void LiveSession::changeConfiguration(
         int64_t timeUs, size_t bandwidthIndex, bool pickTrack) {
     // Protect mPacketSources from a swapPacketSource race condition through reconfiguration.
@@ -1248,34 +1188,36 @@
 
         bool discardFetcher = true;
 
-        // If we're seeking all current fetchers are discarded.
         if (timeUs < 0ll) {
             // delay fetcher removal if not picking tracks
             discardFetcher = pickTrack;
 
-            for (size_t j = 0; j < kMaxStreams; ++j) {
-                StreamType type = indexToType(j);
-                if ((streamMask & type) && uri == URIs[j]) {
-                    resumeMask |= type;
-                    streamMask &= ~type;
-                    discardFetcher = false;
-                }
+        }
+
+        for (size_t j = 0; j < kMaxStreams; ++j) {
+            StreamType type = indexToType(j);
+            if ((streamMask & type) && uri == URIs[j]) {
+                resumeMask |= type;
+                streamMask &= ~type;
+                discardFetcher = false;
             }
         }
 
         if (discardFetcher) {
             mFetcherInfos.valueAt(i).mFetcher->stopAsync();
         } else {
-            mFetcherInfos.valueAt(i).mFetcher->pauseAsync();
+            // if we're seeking, pause immediately (no need to finish the segment)
+            bool immediate = (timeUs >= 0ll);
+            mFetcherInfos.valueAt(i).mFetcher->pauseAsync(immediate);
         }
     }
 
     sp<AMessage> msg;
     if (timeUs < 0ll) {
         // skip onChangeConfiguration2 (decoder destruction) if not seeking.
-        msg = new AMessage(kWhatChangeConfiguration3, id());
+        msg = new AMessage(kWhatChangeConfiguration3, this);
     } else {
-        msg = new AMessage(kWhatChangeConfiguration2, id());
+        msg = new AMessage(kWhatChangeConfiguration2, this);
     }
     msg->setInt32("streamMask", streamMask);
     msg->setInt32("resumeMask", resumeMask);
@@ -1296,14 +1238,6 @@
 
     if (mContinuationCounter == 0) {
         msg->post();
-
-        if (mSeekReplyID != 0) {
-            CHECK(mSeekReply != NULL);
-            mSeekReply->setInt32("err", OK);
-            mSeekReply->postReply(mSeekReplyID);
-            mSeekReplyID = 0;
-            mSeekReply.clear();
-        }
     }
 }
 
@@ -1323,13 +1257,34 @@
 
     // All fetchers are either suspended or have been removed now.
 
+    // If we're seeking, clear all packet sources before we report
+    // seek complete, to prevent decoder from pulling stale data.
+    int64_t timeUs;
+    CHECK(msg->findInt64("timeUs", &timeUs));
+
+    if (timeUs >= 0) {
+        mLastSeekTimeUs = timeUs;
+
+        for (size_t i = 0; i < mPacketSources.size(); i++) {
+            mPacketSources.editValueAt(i)->clear();
+        }
+
+        mDiscontinuityOffsetTimesUs.clear();
+        mDiscontinuityAbsStartTimesUs.clear();
+
+        if (mSeekReplyID != NULL) {
+            CHECK(mSeekReply != NULL);
+            mSeekReply->setInt32("err", OK);
+            mSeekReply->postReply(mSeekReplyID);
+            mSeekReplyID.clear();
+            mSeekReply.clear();
+        }
+    }
+
     uint32_t streamMask, resumeMask;
     CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
     CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask));
 
-    // currently onChangeConfiguration2 is only called for seeking;
-    // remove the following CHECK if using it else where.
-    CHECK_EQ(resumeMask, 0);
     streamMask |= resumeMask;
 
     AString URIs[kMaxStreams];
@@ -1341,17 +1296,25 @@
         }
     }
 
-    // Determine which decoders to shutdown on the player side,
-    // a decoder has to be shutdown if either
-    // 1) its streamtype was active before but now longer isn't.
-    // or
-    // 2) its streamtype was already active and still is but the URI
-    //    has changed.
     uint32_t changedMask = 0;
     for (size_t i = 0; i < kMaxStreams && i != kSubtitleIndex; ++i) {
-        if (((mStreamMask & streamMask & indexToType(i))
-                && !(URIs[i] == mStreams[i].mUri))
-                || (mStreamMask & ~streamMask & indexToType(i))) {
+        // stream URI could change even if onChangeConfiguration2 is only
+        // used for seek. Seek could happen during a bw switch, in this
+        // case bw switch will be cancelled, but the seekTo position will
+        // fetch from the new URI.
+        if ((mStreamMask & streamMask & indexToType(i))
+                && !mStreams[i].mUri.empty()
+                && !(URIs[i] == mStreams[i].mUri)) {
+            ALOGV("stream %zu changed: oldURI %s, newURI %s", i,
+                    mStreams[i].mUri.c_str(), URIs[i].c_str());
+            sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i));
+            source->queueDiscontinuity(
+                    ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+        }
+        // Determine which decoders to shutdown on the player side,
+        // a decoder has to be shutdown if its streamtype was active
+        // before but now longer isn't.
+        if ((mStreamMask & ~streamMask & indexToType(i))) {
             changedMask |= indexToType(i);
         }
     }
@@ -1372,7 +1335,7 @@
     notify->setInt32("changedMask", changedMask);
 
     msg->setWhat(kWhatChangeConfiguration3);
-    msg->setTarget(id());
+    msg->setTarget(this);
 
     notify->setMessage("reply", msg);
     notify->post();
@@ -1428,24 +1391,13 @@
         for (size_t j = 0; j < kMaxStreams; ++j) {
             if ((resumeMask & indexToType(j)) && uri == mStreams[j].mUri) {
                 sources[j] = mPacketSources.valueFor(indexToType(j));
-
-                if (j != kSubtitleIndex) {
-                    ALOGV("queueing dummy discontinuity for stream type %d", indexToType(j));
-                    sp<AnotherPacketSource> discontinuityQueue;
-                    discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
-                    discontinuityQueue->queueDiscontinuity(
-                            ATSParser::DISCONTINUITY_NONE,
-                            NULL,
-                            true);
-                }
             }
         }
-
         FetcherInfo &info = mFetcherInfos.editValueAt(i);
         if (sources[kAudioIndex] != NULL || sources[kVideoIndex] != NULL
                 || sources[kSubtitleIndex] != NULL) {
             info.mFetcher->startAsync(
-                    sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex]);
+                    sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex], timeUs);
         } else {
             info.mToBeRemoved = true;
         }
@@ -1486,15 +1438,7 @@
                 sources[j] = mPacketSources.valueFor(indexToType(j));
 
                 if (timeUs >= 0) {
-                    sources[j]->clear();
                     startTimeUs = timeUs;
-
-                    sp<AnotherPacketSource> discontinuityQueue;
-                    sp<AMessage> extra = new AMessage;
-                    extra->setInt64("timeUs", timeUs);
-                    discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
-                    discontinuityQueue->queueDiscontinuity(
-                            ATSParser::DISCONTINUITY_TIME, extra, true);
                 } else {
                     int32_t type;
                     sp<AMessage> meta;
@@ -1532,9 +1476,10 @@
                         if (j == kSubtitleIndex) {
                             break;
                         }
-                        sp<AnotherPacketSource> discontinuityQueue;
-                        discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
-                        discontinuityQueue->queueDiscontinuity(
+
+                        ALOGV("stream[%zu]: queue format change", j);
+
+                        sources[j]->queueDiscontinuity(
                                 ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
                     } else {
                         // adapting, queue discontinuities after resume
@@ -1564,9 +1509,6 @@
     // All fetchers have now been started, the configuration change
     // has completed.
 
-    cancelCheckBandwidthEvent();
-    scheduleCheckBandwidthEvent();
-
     ALOGV("XXX configuration change completed.");
     mReconfigurationInProgress = false;
     if (switching) {
@@ -1575,7 +1517,7 @@
         mStreamMask = mNewStreamMask;
     }
 
-    if (mDisconnectReplyID != 0) {
+    if (mDisconnectReplyID != NULL) {
         finishDisconnect();
     }
 }
@@ -1623,47 +1565,35 @@
     tryToFinishBandwidthSwitch();
 }
 
-void LiveSession::onCheckSwitchDown() {
-    if (mSwitchDownMonitor == NULL) {
-        return;
-    }
+void LiveSession::schedulePollBuffering() {
+    sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
+    msg->setInt32("generation", mPollBufferingGeneration);
+    msg->post(1000000ll);
+}
 
-    if (mSwitchInProgress || mReconfigurationInProgress) {
-        ALOGV("Switch/Reconfig in progress, defer switch down");
-        mSwitchDownMonitor->post(1000000ll);
-        return;
-    }
+void LiveSession::cancelPollBuffering() {
+    ++mPollBufferingGeneration;
+}
 
-    for (size_t i = 0; i < kMaxStreams; ++i) {
-        int32_t targetDuration;
-        sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(indexToType(i));
-        sp<AMessage> meta = packetSource->getLatestDequeuedMeta();
+void LiveSession::onPollBuffering() {
+    ALOGV("onPollBuffering: mSwitchInProgress %d, mReconfigurationInProgress %d, "
+            "mInPreparationPhase %d, mStreamMask 0x%x",
+        mSwitchInProgress, mReconfigurationInProgress,
+        mInPreparationPhase, mStreamMask);
 
-        if (meta != NULL && meta->findInt32("targetDuration", &targetDuration) ) {
-            int64_t bufferedDurationUs = packetSource->getEstimatedDurationUs();
-            int64_t targetDurationUs = targetDuration * 1000000ll;
+    bool low, mid, high;
+    if (checkBuffering(low, mid, high)) {
+        if (mInPreparationPhase && mid) {
+            postPrepared(OK);
+        }
 
-            if (bufferedDurationUs < targetDurationUs / 3) {
-                (new AMessage(kWhatSwitchDown, id()))->post();
-                break;
-            }
+        // don't switch before we report prepared
+        if (!mInPreparationPhase && (low || high)) {
+            switchBandwidthIfNeeded(high);
         }
     }
 
-    mSwitchDownMonitor->post(1000000ll);
-}
-
-void LiveSession::onSwitchDown() {
-    if (mReconfigurationInProgress || mSwitchInProgress || mCurBandwidthIndex == 0) {
-        return;
-    }
-
-    ssize_t bandwidthIndex = getBandwidthIndex();
-    if (bandwidthIndex < mCurBandwidthIndex) {
-        changeConfiguration(-1, bandwidthIndex, false);
-        return;
-    }
-
+    schedulePollBuffering();
 }
 
 // Mark switch done when:
@@ -1688,16 +1618,6 @@
     }
 }
 
-void LiveSession::scheduleCheckBandwidthEvent() {
-    sp<AMessage> msg = new AMessage(kWhatCheckBandwidth, id());
-    msg->setInt32("generation", mCheckBandwidthGeneration);
-    msg->post(10000000ll);
-}
-
-void LiveSession::cancelCheckBandwidthEvent() {
-    ++mCheckBandwidthGeneration;
-}
-
 void LiveSession::cancelBandwidthSwitch() {
     Mutex::Autolock lock(mSwapMutex);
     mSwitchGeneration++;
@@ -1727,33 +1647,69 @@
     }
 }
 
-bool LiveSession::canSwitchBandwidthTo(size_t bandwidthIndex) {
-    if (mReconfigurationInProgress || mSwitchInProgress) {
+bool LiveSession::checkBuffering(bool &low, bool &mid, bool &high) {
+    low = mid = high = false;
+
+    if (mSwitchInProgress || mReconfigurationInProgress) {
+        ALOGV("Switch/Reconfig in progress, defer buffer polling");
         return false;
     }
 
-    if (mCurBandwidthIndex < 0) {
+    // TODO: Fine tune low/high mark.
+    //       We also need to pause playback if buffering is too low.
+    //       Currently during underflow, we depend on decoder to starve
+    //       to pause, but A/V could have different buffering left,
+    //       they're not paused together.
+    // TODO: Report buffering level to NuPlayer for BUFFERING_UPDATE
+
+    // Switch down if any of the fetchers are below low mark;
+    // Switch up   if all of the fetchers are over high mark.
+    size_t activeCount, lowCount, midCount, highCount;
+    activeCount = lowCount = midCount = highCount = 0;
+    for (size_t i = 0; i < mPacketSources.size(); ++i) {
+        // we don't check subtitles for buffering level
+        if (!(mStreamMask & mPacketSources.keyAt(i)
+                & (STREAMTYPE_AUDIO | STREAMTYPE_VIDEO))) {
+            continue;
+        }
+        // ignore streams that never had any packet queued.
+        // (it's possible that the variant only has audio or video)
+        sp<AMessage> meta = mPacketSources[i]->getLatestEnqueuedMeta();
+        if (meta == NULL) {
+            continue;
+        }
+
+        ++activeCount;
+        int64_t bufferedDurationUs =
+                mPacketSources[i]->getEstimatedDurationUs();
+        ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs);
+        if (bufferedDurationUs < kLowWaterMark) {
+            ++lowCount;
+            break;
+        } else if (bufferedDurationUs > kHighWaterMark) {
+            ++midCount;
+            ++highCount;
+        } else if (bufferedDurationUs > kMidWaterMark) {
+            ++midCount;
+        }
+    }
+
+    if (activeCount > 0) {
+        high = (highCount == activeCount);
+        mid = (midCount == activeCount);
+        low = (lowCount > 0);
         return true;
     }
 
-    if (bandwidthIndex == (size_t)mCurBandwidthIndex) {
-        return false;
-    } else if (bandwidthIndex > (size_t)mCurBandwidthIndex) {
-        return canSwitchUp();
-    } else {
-        return true;
-    }
+    return false;
 }
 
-void LiveSession::onCheckBandwidth(const sp<AMessage> &msg) {
-    size_t bandwidthIndex = getBandwidthIndex();
-    if (canSwitchBandwidthTo(bandwidthIndex)) {
-        changeConfiguration(-1ll /* timeUs */, bandwidthIndex);
-    } else {
-        // Come back and check again 10 seconds later in case there is nothing to do now.
-        // If we DO change configuration, once that completes it'll schedule a new
-        // check bandwidth event with an incremented mCheckBandwidthGeneration.
-        msg->post(10000000ll);
+void LiveSession::switchBandwidthIfNeeded(bool canSwitchUp) {
+    ssize_t bandwidthIndex = getBandwidthIndex();
+
+    if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
+            || (!canSwitchUp && bandwidthIndex < mCurBandwidthIndex)) {
+        changeConfiguration(-1, bandwidthIndex, false);
     }
 }
 
@@ -1771,10 +1727,8 @@
     notify->post();
 
     mInPreparationPhase = false;
-
-    mSwitchDownMonitor = new AMessage(kWhatCheckSwitchDown, id());
-    mSwitchDownMonitor->post();
 }
 
+
 }  // namespace android
 
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 2d3a25a..3b0a9a4 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -26,6 +26,7 @@
 namespace android {
 
 struct ABuffer;
+struct AReplyToken;
 struct AnotherPacketSource;
 struct DataSource;
 struct HTTPBase;
@@ -39,10 +40,6 @@
         // Don't log any URLs.
         kFlagIncognito = 1,
     };
-    LiveSession(
-            const sp<AMessage> &notify,
-            uint32_t flags,
-            const sp<IMediaHTTPService> &httpService);
 
     enum StreamIndex {
         kAudioIndex    = 0,
@@ -56,6 +53,12 @@
         STREAMTYPE_VIDEO        = 1 << kVideoIndex,
         STREAMTYPE_SUBTITLES    = 1 << kSubtitleIndex,
     };
+
+    LiveSession(
+            const sp<AMessage> &notify,
+            uint32_t flags,
+            const sp<IMediaHTTPService> &httpService);
+
     status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit);
 
     status_t getStreamFormat(StreamType stream, sp<AMessage> *format);
@@ -109,11 +112,13 @@
         kWhatChangeConfiguration3       = 'chC3',
         kWhatFinishDisconnect2          = 'fin2',
         kWhatSwapped                    = 'swap',
-        kWhatCheckSwitchDown            = 'ckSD',
-        kWhatSwitchDown                 = 'sDwn',
+        kWhatPollBuffering              = 'poll',
     };
 
     static const size_t kBandwidthHistoryBytes;
+    static const int64_t kHighWaterMark;
+    static const int64_t kMidWaterMark;
+    static const int64_t kLowWaterMark;
 
     struct BandwidthItem {
         size_t mPlaylistIndex;
@@ -168,6 +173,7 @@
 
     sp<M3UParser> mPlaylist;
 
+    sp<ALooper> mFetcherLooper;
     KeyedVector<AString, FetcherInfo> mFetcherInfos;
     uint32_t mStreamMask;
 
@@ -180,7 +186,6 @@
     // we use this to track reconfiguration progress.
     uint32_t mSwapMask;
 
-    KeyedVector<StreamType, sp<AnotherPacketSource> > mDiscontinuities;
     KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources;
     // A second set of packet sources that buffer content for the variant we're switching to.
     KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources2;
@@ -203,16 +208,17 @@
 
     bool mReconfigurationInProgress;
     bool mSwitchInProgress;
-    uint32_t mDisconnectReplyID;
-    uint32_t mSeekReplyID;
+    sp<AReplyToken> mDisconnectReplyID;
+    sp<AReplyToken> mSeekReplyID;
 
     bool mFirstTimeUsValid;
     int64_t mFirstTimeUs;
     int64_t mLastSeekTimeUs;
-    sp<AMessage> mSwitchDownMonitor;
     KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs;
     KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs;
 
+    int32_t mPollBufferingGeneration;
+
     sp<PlaylistFetcher> addFetcher(const char *uri);
 
     void onConnect(const sp<AMessage> &msg);
@@ -256,27 +262,24 @@
     void onChangeConfiguration2(const sp<AMessage> &msg);
     void onChangeConfiguration3(const sp<AMessage> &msg);
     void onSwapped(const sp<AMessage> &msg);
-    void onCheckSwitchDown();
-    void onSwitchDown();
     void tryToFinishBandwidthSwitch();
 
-    void scheduleCheckBandwidthEvent();
-    void cancelCheckBandwidthEvent();
-
     // cancelBandwidthSwitch is atomic wrt swapPacketSource; call it to prevent packet sources
     // from being swapped out on stale discontinuities while manipulating
     // mPacketSources/mPacketSources2.
     void cancelBandwidthSwitch();
 
-    bool canSwitchBandwidthTo(size_t bandwidthIndex);
-    void onCheckBandwidth(const sp<AMessage> &msg);
+    void schedulePollBuffering();
+    void cancelPollBuffering();
+    void onPollBuffering();
+    bool checkBuffering(bool &low, bool &mid, bool &high);
+    void switchBandwidthIfNeeded(bool canSwitchUp);
 
     void finishDisconnect();
 
     void postPrepared(status_t err);
 
     void swapPacketSource(StreamType stream);
-    bool canSwitchUp();
 
     DISALLOW_EVIL_CONSTRUCTORS(LiveSession);
 };
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 997b694..3c5d7cf 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -251,6 +251,7 @@
       mIsComplete(false),
       mIsEvent(false),
       mDiscontinuitySeq(0),
+      mDiscontinuityCount(0),
       mSelectedIndex(-1) {
     mInitCheck = parse(data, size);
 }
@@ -582,6 +583,7 @@
                     itemMeta = new AMessage;
                 }
                 itemMeta->setInt32("discontinuity", true);
+                ++mDiscontinuityCount;
             } else if (line.startsWith("#EXT-X-STREAM-INF")) {
                 if (mMeta != NULL) {
                     return ERROR_MALFORMED;
@@ -609,6 +611,9 @@
             } else if (line.startsWith("#EXT-X-MEDIA")) {
                 err = parseMedia(line);
             } else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE")) {
+                if (mIsVariantPlaylist) {
+                    return ERROR_MALFORMED;
+                }
                 size_t seq;
                 err = parseDiscontinuitySequence(line, &seq);
                 if (err == OK) {
@@ -628,6 +633,7 @@
                         || !itemMeta->findInt64("durationUs", &durationUs)) {
                     return ERROR_MALFORMED;
                 }
+                itemMeta->setInt32("discontinuity-sequence", mDiscontinuitySeq + mDiscontinuityCount);
             }
 
             mItems.push();
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index 1cad060..d475683 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -70,6 +70,7 @@
     bool mIsComplete;
     bool mIsEvent;
     size_t mDiscontinuitySeq;
+    int32_t mDiscontinuityCount;
 
     sp<AMessage> mMeta;
     Vector<Item> mItems;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 1227600..7f818a8 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -49,6 +49,7 @@
 // static
 const int64_t PlaylistFetcher::kMinBufferedDurationUs = 10000000ll;
 const int64_t PlaylistFetcher::kMaxMonitorDelayUs = 3000000ll;
+const int64_t PlaylistFetcher::kFetcherResumeThreshold = 100000ll;
 // LCM of 188 (size of a TS packet) & 1k works well
 const int32_t PlaylistFetcher::kDownloadBlockSize = 47 * 1024;
 const int32_t PlaylistFetcher::kNumSkipFrames = 5;
@@ -59,7 +60,6 @@
         const char *uri,
         int32_t subtitleGeneration)
     : mNotify(notify),
-      mStartTimeUsNotify(notify->dup()),
       mSession(session),
       mURI(uri),
       mStreamTypeMask(0),
@@ -73,16 +73,16 @@
       mStartup(true),
       mAdaptive(false),
       mPrepared(false),
+      mTimeChangeSignaled(false),
       mNextPTSTimeUs(-1ll),
       mMonitorQueueGeneration(0),
       mSubtitleGeneration(subtitleGeneration),
+      mLastDiscontinuitySeq(-1ll),
+      mStopping(false),
       mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
       mFirstPTSValid(false),
-      mAbsoluteTimeAnchorUs(0ll),
       mVideoBuffer(new AnotherPacketSource(NULL)) {
     memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
-    mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
-    mStartTimeUsNotify->setInt32("streamMask", 0);
 }
 
 PlaylistFetcher::~PlaylistFetcher() {
@@ -325,7 +325,7 @@
         ALOGV("Need to refresh playlist in %" PRId64 , maxDelayUs);
         delayUs = maxDelayUs;
     }
-    sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id());
+    sp<AMessage> msg = new AMessage(kWhatMonitorQueue, this);
     msg->setInt32("generation", mMonitorQueueGeneration);
     msg->post(delayUs);
 }
@@ -334,6 +334,11 @@
     ++mMonitorQueueGeneration;
 }
 
+void PlaylistFetcher::setStopping(bool stopping) {
+    AutoMutex _l(mStoppingLock);
+    mStopping = stopping;
+}
+
 void PlaylistFetcher::startAsync(
         const sp<AnotherPacketSource> &audioSource,
         const sp<AnotherPacketSource> &videoSource,
@@ -342,7 +347,7 @@
         int64_t segmentStartTimeUs,
         int32_t startDiscontinuitySeq,
         bool adaptive) {
-    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    sp<AMessage> msg = new AMessage(kWhatStart, this);
 
     uint32_t streamTypeMask = 0ul;
 
@@ -369,18 +374,23 @@
     msg->post();
 }
 
-void PlaylistFetcher::pauseAsync() {
-    (new AMessage(kWhatPause, id()))->post();
+void PlaylistFetcher::pauseAsync(bool immediate) {
+    if (immediate) {
+        setStopping(true);
+    }
+    (new AMessage(kWhatPause, this))->post();
 }
 
 void PlaylistFetcher::stopAsync(bool clear) {
-    sp<AMessage> msg = new AMessage(kWhatStop, id());
+    setStopping(true);
+
+    sp<AMessage> msg = new AMessage(kWhatStop, this);
     msg->setInt32("clear", clear);
     msg->post();
 }
 
 void PlaylistFetcher::resumeUntilAsync(const sp<AMessage> &params) {
-    AMessage* msg = new AMessage(kWhatResumeUntil, id());
+    AMessage* msg = new AMessage(kWhatResumeUntil, this);
     msg->setMessage("params", params);
     msg->post();
 }
@@ -450,6 +460,10 @@
 
 status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) {
     mPacketSources.clear();
+    mStopParams.clear();
+    mStartTimeUsNotify = mNotify->dup();
+    mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+    mStartTimeUsNotify->setInt32("streamMask", 0);
 
     uint32_t streamTypeMask;
     CHECK(msg->findInt32("streamTypeMask", (int32_t *)&streamTypeMask));
@@ -495,12 +509,18 @@
     mSegmentStartTimeUs = segmentStartTimeUs;
     mDiscontinuitySeq = startDiscontinuitySeq;
 
+    mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY;
+
     if (startTimeUs >= 0) {
         mStartTimeUs = startTimeUs;
+        mFirstPTSValid = false;
         mSeqNumber = -1;
         mStartup = true;
         mPrepared = false;
+        mIDRFound = false;
+        mTimeChangeSignaled = false;
         mAdaptive = adaptive;
+        mVideoBuffer->clear();
     }
 
     postMonitorQueue();
@@ -510,6 +530,9 @@
 
 void PlaylistFetcher::onPause() {
     cancelMonitorQueue();
+    mLastDiscontinuitySeq = mDiscontinuitySeq;
+
+    setStopping(false);
 }
 
 void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
@@ -526,6 +549,8 @@
 
     mPacketSources.clear();
     mStreamTypeMask = 0;
+
+    setStopping(false);
 }
 
 // Resume until we have reached the boundary timestamps listed in `msg`; when
@@ -535,12 +560,19 @@
     sp<AMessage> params;
     CHECK(msg->findMessage("params", &params));
 
-    bool stop = false;
+    size_t stopCount = 0;
     for (size_t i = 0; i < mPacketSources.size(); i++) {
         sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
 
         const char *stopKey;
         int streamType = mPacketSources.keyAt(i);
+
+        if (streamType == LiveSession::STREAMTYPE_SUBTITLES) {
+            // the subtitle track can always be stopped
+            ++stopCount;
+            continue;
+        }
+
         switch (streamType) {
         case LiveSession::STREAMTYPE_VIDEO:
             stopKey = "timeUsVideo";
@@ -550,15 +582,11 @@
             stopKey = "timeUsAudio";
             break;
 
-        case LiveSession::STREAMTYPE_SUBTITLES:
-            stopKey = "timeUsSubtitle";
-            break;
-
         default:
             TRESPASS();
         }
 
-        // Don't resume if we would stop within a resume threshold.
+        // check if this stream has too little data left to be resumed
         int32_t discontinuitySeq;
         int64_t latestTimeUs = 0, stopTimeUs = 0;
         sp<AMessage> latestMeta = packetSource->getLatestEnqueuedMeta();
@@ -567,12 +595,13 @@
                 && discontinuitySeq == mDiscontinuitySeq
                 && latestMeta->findInt64("timeUs", &latestTimeUs)
                 && params->findInt64(stopKey, &stopTimeUs)
-                && stopTimeUs - latestTimeUs < resumeThreshold(latestMeta)) {
-            stop = true;
+                && stopTimeUs - latestTimeUs < kFetcherResumeThreshold) {
+            ++stopCount;
         }
     }
 
-    if (stop) {
+    // Don't resume if all streams are within a resume threshold
+    if (stopCount == mPacketSources.size()) {
         for (size_t i = 0; i < mPacketSources.size(); i++) {
             mPacketSources.valueAt(i)->queueAccessUnit(mSession->createFormatChangeBuffer());
         }
@@ -581,7 +610,7 @@
     }
 
     mStopParams = params;
-    postMonitorQueue();
+    onDownloadNext();
 
     return OK;
 }
@@ -619,11 +648,7 @@
         targetDurationUs = targetDurationSecs * 1000000ll;
     }
 
-    // buffer at least 3 times the target duration, or up to 10 seconds
-    int64_t durationToBufferUs = targetDurationUs * 3;
-    if (durationToBufferUs > kMinBufferedDurationUs)  {
-        durationToBufferUs = kMinBufferedDurationUs;
-    }
+    int64_t durationToBufferUs = kMinBufferedDurationUs;
 
     int64_t bufferedDurationUs = 0ll;
     status_t finalResult = NOT_ENOUGH_DATA;
@@ -660,9 +685,6 @@
 
         ALOGV("prepared, buffered=%" PRId64 " > %" PRId64 "",
                 bufferedDurationUs, targetDurationUs);
-        sp<AMessage> msg = mNotify->dup();
-        msg->setInt32("what", kWhatTemporarilyDoneFetching);
-        msg->post();
     }
 
     if (finalResult == OK && downloadMore) {
@@ -671,16 +693,11 @@
         // delay the next download slightly; hopefully this gives other concurrent fetchers
         // a better chance to run.
         // onDownloadNext();
-        sp<AMessage> msg = new AMessage(kWhatDownloadNext, id());
+        sp<AMessage> msg = new AMessage(kWhatDownloadNext, this);
         msg->setInt32("generation", mMonitorQueueGeneration);
         msg->post(1000l);
     } else {
         // Nothing to do yet, try again in a second.
-
-        sp<AMessage> msg = mNotify->dup();
-        msg->setInt32("what", kWhatTemporarilyDoneFetching);
-        msg->post();
-
         int64_t delayUs = mPrepared ? kMaxMonitorDelayUs : targetDurationUs / 2;
         ALOGV("pausing for %" PRId64 ", buffered=%" PRId64 " > %" PRId64 "",
                 delayUs, bufferedDurationUs, durationToBufferUs);
@@ -877,11 +894,22 @@
                 &uri,
                 &itemMeta));
 
+    CHECK(itemMeta->findInt32("discontinuity-sequence", &mDiscontinuitySeq));
+
     int32_t val;
     if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
-        mDiscontinuitySeq++;
+        discontinuity = true;
+    } else if (mLastDiscontinuitySeq >= 0
+            && mDiscontinuitySeq != mLastDiscontinuitySeq) {
+        // Seek jumped to a new discontinuity sequence. We need to signal
+        // a format change to decoder. Decoder needs to shutdown and be
+        // created again if seamless format change is unsupported.
+        ALOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
+                "mDiscontinuitySeq %d, mStartTimeUs %lld",
+            mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
         discontinuity = true;
     }
+    mLastDiscontinuitySeq = -1;
 
     int64_t range_offset, range_length;
     if (!itemMeta->findInt64("range-offset", &range_offset)
@@ -910,8 +938,52 @@
         }
     }
 
+    if ((mStartup && !mTimeChangeSignaled) || discontinuity) {
+        // We need to signal a time discontinuity to ATSParser on the
+        // first segment after start, or on a discontinuity segment.
+        // Setting mNextPTSTimeUs informs extractAndQueueAccessUnitsXX()
+        // to send the time discontinuity.
+        if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
+            // If this was a live event this made no sense since
+            // we don't have access to all the segment before the current
+            // one.
+            mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber);
+        }
+
+        // Setting mTimeChangeSignaled to true, so that if start time
+        // searching goes into 2nd segment (without a discontinuity),
+        // we don't reset time again. It causes corruption when pending
+        // data in ATSParser is cleared.
+        mTimeChangeSignaled = true;
+    }
+
+    if (discontinuity) {
+        ALOGI("queueing discontinuity (explicit=%d)", discontinuity);
+
+        // Signal a format discontinuity to ATSParser to clear partial data
+        // from previous streams. Not doing this causes bitstream corruption.
+        mTSParser->signalDiscontinuity(
+                ATSParser::DISCONTINUITY_FORMATCHANGE, NULL /* extra */);
+
+        queueDiscontinuity(
+                ATSParser::DISCONTINUITY_FORMATCHANGE,
+                NULL /* extra */);
+
+        if (mStartup && mStartTimeUsRelative && mFirstPTSValid) {
+            // This means we guessed mStartTimeUs to be in the previous
+            // segment (likely very close to the end), but either video or
+            // audio has not found start by the end of that segment.
+            //
+            // If this new segment is not a discontinuity, keep searching.
+            //
+            // If this new segment even got a discontinuity marker, just
+            // set mStartTimeUs=0, and take all samples from now on.
+            mStartTimeUs = 0;
+            mFirstPTSValid = false;
+        }
+    }
+
     // block-wise download
-    bool startup = mStartup;
     ssize_t bytesRead;
     do {
         bytesRead = mSession->fetchFile(
@@ -941,29 +1013,6 @@
             return;
         }
 
-        if (startup || discontinuity) {
-            // Signal discontinuity.
-
-            if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
-                // If this was a live event this made no sense since
-                // we don't have access to all the segment before the current
-                // one.
-                mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber);
-            }
-
-            if (discontinuity) {
-                ALOGI("queueing discontinuity (explicit=%d)", discontinuity);
-
-                queueDiscontinuity(
-                        ATSParser::DISCONTINUITY_FORMATCHANGE,
-                        NULL /* extra */);
-
-                discontinuity = false;
-            }
-
-            startup = false;
-        }
-
         err = OK;
         if (bufferStartsWithTsSyncByte(buffer)) {
             // Incremental extraction is only supported for MPEG2 transport streams.
@@ -998,7 +1047,7 @@
             return;
         }
 
-    } while (bytesRead != 0);
+    } while (bytesRead != 0 && !mStopping);
 
     if (bufferStartsWithTsSyncByte(buffer)) {
         // If we don't see a stream in the program table after fetching a full ts segment
@@ -1200,9 +1249,7 @@
         mTSParser->signalDiscontinuity(
                 ATSParser::DISCONTINUITY_TIME, extra);
 
-        mAbsoluteTimeAnchorUs = mNextPTSTimeUs;
         mNextPTSTimeUs = -1ll;
-        mFirstPTSValid = false;
     }
 
     size_t offset = 0;
@@ -1255,12 +1302,17 @@
             continue;
         }
 
-        int64_t timeUs;
+        const char *mime;
+        sp<MetaData> format  = source->getFormat();
+        bool isAvc = format != NULL && format->findCString(kKeyMIMEType, &mime)
+                && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+
         sp<ABuffer> accessUnit;
         status_t finalResult;
         while (source->hasBufferAvailable(&finalResult)
                 && source->dequeueAccessUnit(&accessUnit) == OK) {
 
+            int64_t timeUs;
             CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
 
             if (mStartup) {
@@ -1275,30 +1327,25 @@
                     }
                 }
 
-                if (timeUs < mStartTimeUs) {
+                if (timeUs < mStartTimeUs || (isAvc && !mIDRFound)) {
                     // buffer up to the closest preceding IDR frame
                     ALOGV("timeUs %" PRId64 " us < mStartTimeUs %" PRId64 " us",
                             timeUs, mStartTimeUs);
-                    const char *mime;
-                    sp<MetaData> format  = source->getFormat();
-                    bool isAvc = false;
-                    if (format != NULL && format->findCString(kKeyMIMEType, &mime)
-                            && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
-                        isAvc = true;
-                    }
-                    if (isAvc && IsIDR(accessUnit)) {
-                        mVideoBuffer->clear();
-                    }
                     if (isAvc) {
-                        mVideoBuffer->queueAccessUnit(accessUnit);
+                        if (IsIDR(accessUnit)) {
+                            mVideoBuffer->clear();
+                            mIDRFound = true;
+                        }
+                        if (mIDRFound) {
+                            mVideoBuffer->queueAccessUnit(accessUnit);
+                        }
                     }
 
                     continue;
                 }
             }
 
-            CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-            if (mStartTimeUsNotify != NULL && timeUs > mStartTimeUs) {
+            if (mStartTimeUsNotify != NULL) {
                 int32_t firstSeqNumberInPlaylist;
                 if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
                             "media-sequence", &firstSeqNumberInPlaylist)) {
@@ -1467,8 +1514,6 @@
     }
 
     if (mNextPTSTimeUs >= 0ll) {
-        mFirstPTSValid = false;
-        mAbsoluteTimeAnchorUs = mNextPTSTimeUs;
         mNextPTSTimeUs = -1ll;
     }
 
@@ -1569,7 +1614,7 @@
     CHECK(packetSource->getFormat()->findInt32(kKeySampleRate, &sampleRate));
 
     int64_t timeUs = (PTS * 100ll) / 9ll;
-    if (!mFirstPTSValid) {
+    if (mStartup && !mFirstPTSValid) {
         mFirstPTSValid = true;
         mFirstTimeUs = timeUs;
     }
@@ -1687,33 +1732,4 @@
     msg->post();
 }
 
-int64_t PlaylistFetcher::resumeThreshold(const sp<AMessage> &msg) {
-    int64_t durationUs;
-    if (msg->findInt64("durationUs", &durationUs) && durationUs > 0) {
-        return kNumSkipFrames * durationUs;
-    }
-
-    sp<RefBase> obj;
-    msg->findObject("format", &obj);
-    MetaData *format = static_cast<MetaData *>(obj.get());
-
-    const char *mime;
-    CHECK(format->findCString(kKeyMIMEType, &mime));
-    bool audio = !strncasecmp(mime, "audio/", 6);
-    if (audio) {
-        // Assumes 1000 samples per frame.
-        int32_t sampleRate;
-        CHECK(format->findInt32(kKeySampleRate, &sampleRate));
-        return kNumSkipFrames  /* frames */ * 1000 /* samples */
-                * (1000000 / sampleRate) /* sample duration (us) */;
-    } else {
-        int32_t frameRate;
-        if (format->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) {
-            return kNumSkipFrames * (1000000 / frameRate);
-        }
-    }
-
-    return 500000ll;
-}
-
 }  // namespace android
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 4e15f85..b82e50d 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -36,6 +36,7 @@
 struct PlaylistFetcher : public AHandler {
     static const int64_t kMinBufferedDurationUs;
     static const int32_t kDownloadBlockSize;
+    static const int64_t kFetcherResumeThreshold;
 
     enum {
         kWhatStarted,
@@ -43,7 +44,6 @@
         kWhatStopped,
         kWhatError,
         kWhatDurationUpdate,
-        kWhatTemporarilyDoneFetching,
         kWhatPrepared,
         kWhatPreparationFailed,
         kWhatStartedAt,
@@ -67,7 +67,7 @@
             int32_t startDiscontinuitySeq = 0,
             bool adaptive = false);
 
-    void pauseAsync();
+    void pauseAsync(bool immediate = false);
 
     void stopAsync(bool clear = true);
 
@@ -116,7 +116,7 @@
     // adapting or switching tracks.
     int64_t mSegmentStartTimeUs;
 
-    ssize_t mDiscontinuitySeq;
+    int32_t mDiscontinuitySeq;
     bool mStartTimeUsRelative;
     sp<AMessage> mStopParams; // message containing the latest timestamps we should fetch.
 
@@ -130,13 +130,20 @@
     int32_t mSeqNumber;
     int32_t mNumRetries;
     bool mStartup;
+    bool mIDRFound;
     bool mAdaptive;
     bool mPrepared;
+    bool mTimeChangeSignaled;
     int64_t mNextPTSTimeUs;
 
     int32_t mMonitorQueueGeneration;
     const int32_t mSubtitleGeneration;
 
+    int32_t mLastDiscontinuitySeq;
+
+    Mutex mStoppingLock;
+    bool mStopping;
+
     enum RefreshState {
         INITIAL_MINIMUM_RELOAD_DELAY,
         FIRST_UNCHANGED_RELOAD_ATTEMPT,
@@ -152,7 +159,6 @@
     bool mFirstPTSValid;
     uint64_t mFirstPTS;
     int64_t mFirstTimeUs;
-    int64_t mAbsoluteTimeAnchorUs;
     sp<AnotherPacketSource> mVideoBuffer;
 
     // Stores the initialization vector to decrypt the next block of cipher text, which can
@@ -175,6 +181,7 @@
 
     void postMonitorQueue(int64_t delayUs = 0, int64_t minDelayUs = 0);
     void cancelMonitorQueue();
+    void setStopping(bool stopping);
 
     int64_t delayUsToRefreshPlaylist() const;
     status_t refreshPlaylist();
@@ -212,10 +219,6 @@
 
     void updateDuration();
 
-    // Before resuming a fetcher in onResume, check the remaining duration is longer than that
-    // returned by resumeThreshold.
-    int64_t resumeThreshold(const sp<AMessage> &msg);
-
     DISALLOW_EVIL_CONSTRUCTORS(PlaylistFetcher);
 };
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 934e2e5..6786506 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -35,6 +35,7 @@
 #include <media/stagefright/Utils.h>
 #include <media/IStreamSource.h>
 #include <utils/KeyedVector.h>
+#include <utils/Vector.h>
 
 #include <inttypes.h>
 
@@ -86,6 +87,11 @@
     }
 
 private:
+    struct StreamInfo {
+        unsigned mType;
+        unsigned mPID;
+    };
+
     ATSParser *mParser;
     unsigned mProgramNumber;
     unsigned mProgramMapPID;
@@ -96,6 +102,7 @@
 
     status_t parseProgramMap(ABitReader *br);
     int64_t recoverPTS(uint64_t PTS_33bit);
+    bool switchPIDs(const Vector<StreamInfo> &infos);
 
     DISALLOW_EVIL_CONSTRUCTORS(Program);
 };
@@ -185,7 +192,7 @@
       mProgramMapPID(programMapPID),
       mFirstPTSValid(false),
       mFirstPTS(0),
-      mLastRecoveredPTS(0) {
+      mLastRecoveredPTS(-1ll) {
     ALOGV("new program number %u", programNumber);
 }
 
@@ -240,10 +247,71 @@
     }
 }
 
-struct StreamInfo {
-    unsigned mType;
-    unsigned mPID;
-};
+bool ATSParser::Program::switchPIDs(const Vector<StreamInfo> &infos) {
+    bool success = false;
+
+    if (mStreams.size() == infos.size()) {
+        // build type->PIDs map for old and new mapping
+        size_t i;
+        KeyedVector<int32_t, Vector<int32_t> > oldType2PIDs, newType2PIDs;
+        for (i = 0; i < mStreams.size(); ++i) {
+            ssize_t index = oldType2PIDs.indexOfKey(mStreams[i]->type());
+            if (index < 0) {
+                oldType2PIDs.add(mStreams[i]->type(), Vector<int32_t>());
+            }
+            oldType2PIDs.editValueFor(mStreams[i]->type()).push_back(mStreams[i]->pid());
+        }
+        for (i = 0; i < infos.size(); ++i) {
+            ssize_t index = newType2PIDs.indexOfKey(infos[i].mType);
+            if (index < 0) {
+                newType2PIDs.add(infos[i].mType, Vector<int32_t>());
+            }
+            newType2PIDs.editValueFor(infos[i].mType).push_back(infos[i].mPID);
+        }
+
+        // we can recover if the number of streams for each type hasn't changed
+        if (oldType2PIDs.size() == newType2PIDs.size()) {
+            success = true;
+            for (i = 0; i < oldType2PIDs.size(); ++i) {
+                // KeyedVector is sorted, we just compare key and size of each index
+                if (oldType2PIDs.keyAt(i) != newType2PIDs.keyAt(i)
+                        || oldType2PIDs[i].size() != newType2PIDs[i].size()) {
+                     success = false;
+                     break;
+                }
+            }
+        }
+
+        if (success) {
+            // save current streams to temp
+            KeyedVector<int32_t, sp<Stream> > temp;
+            for (i = 0; i < mStreams.size(); ++i) {
+                 temp.add(mStreams.keyAt(i), mStreams.editValueAt(i));
+            }
+
+            mStreams.clear();
+            for (i = 0; i < temp.size(); ++i) {
+                // The two checks below shouldn't happen,
+                // we already checked above the stream count matches
+                ssize_t index = newType2PIDs.indexOfKey(temp[i]->type());
+                CHECK(index >= 0);
+                Vector<int32_t> &newPIDs = newType2PIDs.editValueAt(index);
+                CHECK(newPIDs.size() > 0);
+
+                // get the next PID for temp[i]->type() in the new PID map
+                Vector<int32_t>::iterator it = newPIDs.begin();
+
+                // change the PID of the stream, and add it back
+                temp.editValueAt(i)->setPID(*it);
+                mStreams.add(temp[i]->pid(), temp.editValueAt(i));
+
+                // removed the used PID
+                newPIDs.erase(it);
+            }
+        }
+    }
+    return success;
+}
 
 status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
     unsigned table_id = br->getBits(8);
@@ -372,39 +440,8 @@
         }
 #endif
 
-        // The only case we can recover from is if we have two streams
-        // and they switched PIDs.
-
-        bool success = false;
-
-        if (mStreams.size() == 2 && infos.size() == 2) {
-            const StreamInfo &info1 = infos.itemAt(0);
-            const StreamInfo &info2 = infos.itemAt(1);
-
-            sp<Stream> s1 = mStreams.editValueAt(0);
-            sp<Stream> s2 = mStreams.editValueAt(1);
-
-            bool caseA =
-                info1.mPID == s1->pid() && info1.mType == s2->type()
-                    && info2.mPID == s2->pid() && info2.mType == s1->type();
-
-            bool caseB =
-                info1.mPID == s2->pid() && info1.mType == s1->type()
-                    && info2.mPID == s1->pid() && info2.mType == s2->type();
-
-            if (caseA || caseB) {
-                unsigned pid1 = s1->pid();
-                unsigned pid2 = s2->pid();
-                s1->setPID(pid2);
-                s2->setPID(pid1);
-
-                mStreams.clear();
-                mStreams.add(s1->pid(), s1);
-                mStreams.add(s2->pid(), s2);
-
-                success = true;
-            }
-        }
+        // we can recover if number of streams for each type remain the same
+        bool success = switchPIDs(infos);
 
         if (!success) {
             ALOGI("Stream PIDs changed and we cannot recover.");
@@ -433,14 +470,25 @@
     // reasonable amount of time. To handle the wrap-around, use fancy math
     // to get an extended PTS that is within [-0xffffffff, 0xffffffff]
     // of the latest recovered PTS.
-    mLastRecoveredPTS = static_cast<int64_t>(
-            ((mLastRecoveredPTS - PTS_33bit + 0x100000000ll)
-            & 0xfffffffe00000000ull) | PTS_33bit);
+    if (mLastRecoveredPTS < 0ll) {
+        // Use the original 33bit number for 1st frame, the reason is that
+        // if 1st frame wraps to negative that's far away from 0, we could
+        // never start. Only start wrapping around from 2nd frame.
+        mLastRecoveredPTS = static_cast<int64_t>(PTS_33bit);
+    } else {
+        mLastRecoveredPTS = static_cast<int64_t>(
+                ((mLastRecoveredPTS - PTS_33bit + 0x100000000ll)
+                & 0xfffffffe00000000ull) | PTS_33bit);
+        // We start from 0, but recovered PTS could be slightly below 0.
+        // Clamp it to 0 as rest of the pipeline doesn't take negative pts.
+        // (eg. video is read first and starts at 0, but audio starts at 0xfffffff0)
+        if (mLastRecoveredPTS < 0ll) {
+            ALOGI("Clamping negative recovered PTS (%" PRId64 ") to 0", mLastRecoveredPTS);
+            mLastRecoveredPTS = 0ll;
+        }
+    }
 
-    // We start from 0, but recovered PTS could be slightly below 0.
-    // Clamp it to 0 as rest of the pipeline doesn't take negative pts.
-    // (eg. video is read first and starts at 0, but audio starts at 0xfffffff0)
-    return mLastRecoveredPTS < 0ll ? 0ll : mLastRecoveredPTS;
+    return mLastRecoveredPTS;
 }
 
 sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
@@ -1118,7 +1166,8 @@
 
         if (payload_unit_start_indicator) {
             if (!section->isEmpty()) {
-                return ERROR_UNSUPPORTED;
+                ALOGW("parsePID encounters payload_unit_start_indicator when section is not empty");
+                section->clear();
             }
 
             unsigned skip = br->getBits(8);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index f266fe7..79a9b04 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -48,7 +48,10 @@
 }
 
 void AnotherPacketSource::setFormat(const sp<MetaData> &meta) {
-    CHECK(mFormat == NULL);
+    if (mFormat != NULL) {
+        // Only allowed to be set once. Requires explicit clear to reset.
+        return;
+    }
 
     mIsAudio = false;
     mIsVideo = false;
@@ -91,13 +94,12 @@
     while (it != mBuffers.end()) {
         sp<ABuffer> buffer = *it;
         int32_t discontinuity;
-        if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
-            break;
-        }
-
-        sp<RefBase> object;
-        if (buffer->meta()->findObject("format", &object)) {
-            return mFormat = static_cast<MetaData*>(object.get());
+        if (!buffer->meta()->findInt32("discontinuity", &discontinuity)) {
+            sp<RefBase> object;
+            if (buffer->meta()->findObject("format", &object)) {
+                setFormat(static_cast<MetaData*>(object.get()));
+                return mFormat;
+            }
         }
 
         ++it;
@@ -131,7 +133,7 @@
 
         sp<RefBase> object;
         if ((*buffer)->meta()->findObject("format", &object)) {
-            mFormat = static_cast<MetaData*>(object.get());
+            setFormat(static_cast<MetaData*>(object.get()));
         }
 
         return OK;
@@ -166,7 +168,7 @@
 
         sp<RefBase> object;
         if (buffer->meta()->findObject("format", &object)) {
-            mFormat = static_cast<MetaData*>(object.get());
+            setFormat(static_cast<MetaData*>(object.get()));
         }
 
         int64_t timeUs;
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 7afe699..477cfc6 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -28,6 +28,7 @@
 
 #include <media/hardware/MetadataBufferType.h>
 #include <ui/GraphicBuffer.h>
+#include <gui/BufferItem.h>
 
 #include <inttypes.h>
 #include "FrameDropper.h"
@@ -155,7 +156,7 @@
 
         if (mLatestBufferId >= 0) {
             sp<AMessage> msg =
-                new AMessage(kWhatRepeatLastFrame, mReflector->id());
+                new AMessage(kWhatRepeatLastFrame, mReflector);
 
             msg->setInt32("generation", ++mRepeatLastFrameGeneration);
             msg->post(mRepeatAfterUs);
@@ -360,7 +361,7 @@
         mSuspended = true;
 
         while (mNumFramesAvailable > 0) {
-            BufferQueue::BufferItem item;
+            BufferItem item;
             status_t err = mConsumer->acquireBuffer(&item, 0);
 
             if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
@@ -410,7 +411,7 @@
 
     ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%zu",
             mNumFramesAvailable);
-    BufferQueue::BufferItem item;
+    BufferItem item;
     status_t err = mConsumer->acquireBuffer(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
         // shouldn't happen
@@ -503,7 +504,7 @@
         return false;
     }
 
-    BufferQueue::BufferItem item;
+    BufferItem item;
     item.mBuf = mLatestBufferId;
     item.mFrameNumber = mLatestBufferFrameNum;
     item.mTimestamp = mRepeatLastFrameTimestamp;
@@ -524,7 +525,7 @@
         mRepeatLastFrameTimestamp = item.mTimestamp + mRepeatAfterUs * 1000;
 
         if (mReflector != NULL) {
-            sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector->id());
+            sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector);
             msg->setInt32("generation", ++mRepeatLastFrameGeneration);
             msg->post(mRepeatAfterUs);
         }
@@ -534,7 +535,7 @@
 }
 
 void GraphicBufferSource::setLatestBuffer_l(
-        const BufferQueue::BufferItem &item, bool dropped) {
+        const BufferItem &item, bool dropped) {
     ALOGV("setLatestBuffer_l");
 
     if (mLatestBufferId >= 0) {
@@ -557,7 +558,7 @@
     mRepeatLastFrameCount = kRepeatLastFrameCount;
 
     if (mReflector != NULL) {
-        sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector->id());
+        sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector);
         msg->setInt32("generation", ++mRepeatLastFrameGeneration);
         msg->post(mRepeatAfterUs);
     }
@@ -590,7 +591,7 @@
     return OK;
 }
 
-int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) {
+int64_t GraphicBufferSource::getTimestamp(const BufferItem &item) {
     int64_t timeUs = item.mTimestamp / 1000;
 
     if (mTimePerCaptureUs > 0ll) {
@@ -651,7 +652,7 @@
 }
 
 status_t GraphicBufferSource::submitBuffer_l(
-        const BufferQueue::BufferItem &item, int cbi) {
+        const BufferItem &item, int cbi) {
     ALOGV("submitBuffer_l cbi=%d", cbi);
 
     int64_t timeUs = getTimestamp(item);
@@ -777,7 +778,7 @@
             ALOGV("onFrameAvailable: suspended, ignoring frame");
         }
 
-        BufferQueue::BufferItem item;
+        BufferItem item;
         status_t err = mConsumer->acquireBuffer(&item, 0);
         if (err == OK) {
             // If this is the first time we're seeing this buffer, add it to our
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index ce3881e..1067472 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -192,15 +192,15 @@
 
     // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer
     // reference into the codec buffer, and submits the data to the codec.
-    status_t submitBuffer_l(const BufferQueue::BufferItem &item, int cbi);
+    status_t submitBuffer_l(const BufferItem &item, int cbi);
 
     // Submits an empty buffer, with the EOS flag set.   Returns without
     // doing anything if we don't have a codec buffer available.
     void submitEndOfInputStream_l();
 
-    void setLatestBuffer_l(const BufferQueue::BufferItem &item, bool dropped);
+    void setLatestBuffer_l(const BufferItem &item, bool dropped);
     bool repeatLatestBuffer_l();
-    int64_t getTimestamp(const BufferQueue::BufferItem &item);
+    int64_t getTimestamp(const BufferItem &item);
 
     // Lock, covers all member variables.
     mutable Mutex mMutex;
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 7f99dcd..801a1bd 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -58,7 +58,7 @@
         OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) {
     CHECK(data == NULL);
 
-    sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler->id());
+    sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler);
     msg->setInt32("cmd", cmd);
     msg->setInt32("param", param);
     msg->post();
@@ -307,7 +307,7 @@
 
 OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer(
         OMX_BUFFERHEADERTYPE *buffer) {
-    sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler->id());
+    sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler);
     msg->setPointer("header", buffer);
     msg->post();
 
@@ -316,7 +316,7 @@
 
 OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer(
         OMX_BUFFERHEADERTYPE *buffer) {
-    sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler->id());
+    sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler);
     msg->setPointer("header", buffer);
     msg->post();
 
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index a6bd824..a86ab74 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -82,7 +82,7 @@
         size_t index,
         const sp<AMessage> &notify,
         bool injected) {
-    sp<AMessage> msg = new AMessage(kWhatAddStream, id());
+    sp<AMessage> msg = new AMessage(kWhatAddStream, this);
     msg->setInt32("rtp-socket", rtpSocket);
     msg->setInt32("rtcp-socket", rtcpSocket);
     msg->setObject("session-desc", sessionDesc);
@@ -93,7 +93,7 @@
 }
 
 void ARTPConnection::removeStream(int rtpSocket, int rtcpSocket) {
-    sp<AMessage> msg = new AMessage(kWhatRemoveStream, id());
+    sp<AMessage> msg = new AMessage(kWhatRemoveStream, this);
     msg->setInt32("rtp-socket", rtpSocket);
     msg->setInt32("rtcp-socket", rtcpSocket);
     msg->post();
@@ -233,7 +233,7 @@
         return;
     }
 
-    sp<AMessage> msg = new AMessage(kWhatPollStreams, id());
+    sp<AMessage> msg = new AMessage(kWhatPollStreams, this);
     msg->post();
 
     mPollEventPending = true;
@@ -639,7 +639,7 @@
 }
 
 void ARTPConnection::injectPacket(int index, const sp<ABuffer> &buffer) {
-    sp<AMessage> msg = new AMessage(kWhatInjectPacket, id());
+    sp<AMessage> msg = new AMessage(kWhatInjectPacket, this);
     msg->setInt32("index", index);
     msg->setBuffer("buffer", buffer);
     msg->post();
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index ba4e33c..e5acb06 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -82,7 +82,7 @@
         info->mRTPSocket = rtpSocket;
         info->mRTCPSocket = rtcpSocket;
 
-        sp<AMessage> notify = new AMessage(kWhatAccessUnitComplete, id());
+        sp<AMessage> notify = new AMessage(kWhatAccessUnitComplete, this);
         notify->setSize("track-index", mTracks.size() - 1);
 
         mRTPConn->addStream(
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index e1607bf..56c4aa6 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -146,7 +146,7 @@
         TRESPASS();
     }
 
-    (new AMessage(kWhatStart, mReflector->id()))->post();
+    (new AMessage(kWhatStart, mReflector))->post();
 
     while (!(mFlags & kFlagStarted)) {
         mCondition.wait(mLock);
@@ -161,7 +161,7 @@
         return OK;
     }
 
-    (new AMessage(kWhatStop, mReflector->id()))->post();
+    (new AMessage(kWhatStop, mReflector))->post();
 
     while (mFlags & kFlagStarted) {
         mCondition.wait(mLock);
@@ -213,8 +213,8 @@
                 mCondition.signal();
             }
 
-            (new AMessage(kWhatRead, mReflector->id()))->post();
-            (new AMessage(kWhatSendSR, mReflector->id()))->post();
+            (new AMessage(kWhatRead, mReflector))->post();
+            (new AMessage(kWhatSendSR, mReflector))->post();
             break;
         }
 
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index 60b3aaf..855ffdc 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -68,28 +68,28 @@
 }
 
 void ARTSPConnection::connect(const char *url, const sp<AMessage> &reply) {
-    sp<AMessage> msg = new AMessage(kWhatConnect, id());
+    sp<AMessage> msg = new AMessage(kWhatConnect, this);
     msg->setString("url", url);
     msg->setMessage("reply", reply);
     msg->post();
 }
 
 void ARTSPConnection::disconnect(const sp<AMessage> &reply) {
-    sp<AMessage> msg = new AMessage(kWhatDisconnect, id());
+    sp<AMessage> msg = new AMessage(kWhatDisconnect, this);
     msg->setMessage("reply", reply);
     msg->post();
 }
 
 void ARTSPConnection::sendRequest(
         const char *request, const sp<AMessage> &reply) {
-    sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
+    sp<AMessage> msg = new AMessage(kWhatSendRequest, this);
     msg->setString("request", request);
     msg->setMessage("reply", reply);
     msg->post();
 }
 
 void ARTSPConnection::observeBinaryData(const sp<AMessage> &reply) {
-    sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, id());
+    sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, this);
     msg->setMessage("reply", reply);
     msg->post();
 }
@@ -286,7 +286,7 @@
 
     if (err < 0) {
         if (errno == EINPROGRESS) {
-            sp<AMessage> msg = new AMessage(kWhatCompleteConnection, id());
+            sp<AMessage> msg = new AMessage(kWhatCompleteConnection, this);
             msg->setMessage("reply", reply);
             msg->setInt32("connection-id", mConnectionID);
             msg->post();
@@ -523,7 +523,7 @@
         return;
     }
 
-    sp<AMessage> msg = new AMessage(kWhatReceiveResponse, id());
+    sp<AMessage> msg = new AMessage(kWhatReceiveResponse, this);
     msg->post();
 
     mReceiveResponseEventPending = true;
@@ -746,7 +746,7 @@
             AString request;
             CHECK(reply->findString("original-request", &request));
 
-            sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
+            sp<AMessage> msg = new AMessage(kWhatSendRequest, this);
             msg->setMessage("reply", reply);
             msg->setString("request", request.c_str(), request.size());
 
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 3bf489b..0642343 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -169,10 +169,10 @@
         looper()->registerHandler(mConn);
         (1 ? mNetLooper : looper())->registerHandler(mRTPConn);
 
-        sp<AMessage> notify = new AMessage('biny', id());
+        sp<AMessage> notify = new AMessage('biny', this);
         mConn->observeBinaryData(notify);
 
-        sp<AMessage> reply = new AMessage('conn', id());
+        sp<AMessage> reply = new AMessage('conn', this);
         mConn->connect(mOriginalSessionURL.c_str(), reply);
     }
 
@@ -180,10 +180,10 @@
         looper()->registerHandler(mConn);
         (1 ? mNetLooper : looper())->registerHandler(mRTPConn);
 
-        sp<AMessage> notify = new AMessage('biny', id());
+        sp<AMessage> notify = new AMessage('biny', this);
         mConn->observeBinaryData(notify);
 
-        sp<AMessage> reply = new AMessage('sdpl', id());
+        sp<AMessage> reply = new AMessage('sdpl', this);
         reply->setObject("description", desc);
         mConn->connect(mOriginalSessionURL.c_str(), reply);
     }
@@ -210,11 +210,11 @@
     }
 
     void disconnect() {
-        (new AMessage('abor', id()))->post();
+        (new AMessage('abor', this))->post();
     }
 
     void seek(int64_t timeUs) {
-        sp<AMessage> msg = new AMessage('seek', id());
+        sp<AMessage> msg = new AMessage('seek', this);
         msg->setInt64("time", timeUs);
         mPauseGeneration++;
         msg->post();
@@ -225,14 +225,14 @@
     }
 
     void pause() {
-        sp<AMessage> msg = new AMessage('paus', id());
+        sp<AMessage> msg = new AMessage('paus', this);
         mPauseGeneration++;
         msg->setInt32("pausecheck", mPauseGeneration);
         msg->post(kPauseDelayUs);
     }
 
     void resume() {
-        sp<AMessage> msg = new AMessage('resu', id());
+        sp<AMessage> msg = new AMessage('resu', this);
         mPauseGeneration++;
         msg->post();
     }
@@ -454,10 +454,10 @@
                     request.append("Accept: application/sdp\r\n");
                     request.append("\r\n");
 
-                    sp<AMessage> reply = new AMessage('desc', id());
+                    sp<AMessage> reply = new AMessage('desc', this);
                     mConn->sendRequest(request.c_str(), reply);
                 } else {
-                    (new AMessage('disc', id()))->post();
+                    (new AMessage('disc', this))->post();
                 }
                 break;
             }
@@ -468,10 +468,10 @@
 
                 int32_t reconnect;
                 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
-                    sp<AMessage> reply = new AMessage('conn', id());
+                    sp<AMessage> reply = new AMessage('conn', this);
                     mConn->connect(mOriginalSessionURL.c_str(), reply);
                 } else {
-                    (new AMessage('quit', id()))->post();
+                    (new AMessage('quit', this))->post();
                 }
                 break;
             }
@@ -514,7 +514,7 @@
                             ALOGI("rewritten session url: '%s'", mSessionURL.c_str());
                         }
 
-                        sp<AMessage> reply = new AMessage('conn', id());
+                        sp<AMessage> reply = new AMessage('conn', this);
                         mConn->connect(mOriginalSessionURL.c_str(), reply);
                         break;
                     }
@@ -586,7 +586,7 @@
                 }
 
                 if (result != OK) {
-                    sp<AMessage> reply = new AMessage('disc', id());
+                    sp<AMessage> reply = new AMessage('disc', this);
                     mConn->disconnect(reply);
                 }
                 break;
@@ -631,7 +631,7 @@
                 }
 
                 if (result != OK) {
-                    sp<AMessage> reply = new AMessage('disc', id());
+                    sp<AMessage> reply = new AMessage('disc', this);
                     mConn->disconnect(reply);
                 }
                 break;
@@ -703,7 +703,7 @@
                             mSessionID.erase(i, mSessionID.size() - i);
                         }
 
-                        sp<AMessage> notify = new AMessage('accu', id());
+                        sp<AMessage> notify = new AMessage('accu', this);
                         notify->setSize("track-index", trackIndex);
 
                         i = response->mHeaders.indexOfKey("transport");
@@ -769,10 +769,10 @@
 
                     request.append("\r\n");
 
-                    sp<AMessage> reply = new AMessage('play', id());
+                    sp<AMessage> reply = new AMessage('play', this);
                     mConn->sendRequest(request.c_str(), reply);
                 } else {
-                    sp<AMessage> reply = new AMessage('disc', id());
+                    sp<AMessage> reply = new AMessage('disc', this);
                     mConn->disconnect(reply);
                 }
                 break;
@@ -797,7 +797,7 @@
                     } else {
                         parsePlayResponse(response);
 
-                        sp<AMessage> timeout = new AMessage('tiou', id());
+                        sp<AMessage> timeout = new AMessage('tiou', this);
                         mCheckTimeoutGeneration++;
                         timeout->setInt32("tioucheck", mCheckTimeoutGeneration);
                         timeout->post(kStartupTimeoutUs);
@@ -805,7 +805,7 @@
                 }
 
                 if (result != OK) {
-                    sp<AMessage> reply = new AMessage('disc', id());
+                    sp<AMessage> reply = new AMessage('disc', this);
                     mConn->disconnect(reply);
                 }
 
@@ -831,7 +831,7 @@
                 request.append("\r\n");
                 request.append("\r\n");
 
-                sp<AMessage> reply = new AMessage('opts', id());
+                sp<AMessage> reply = new AMessage('opts', this);
                 reply->setInt32("generation", mKeepAliveGeneration);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
@@ -894,7 +894,7 @@
                 mPausing = false;
                 mSeekable = true;
 
-                sp<AMessage> reply = new AMessage('tear', id());
+                sp<AMessage> reply = new AMessage('tear', this);
 
                 int32_t reconnect;
                 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
@@ -926,7 +926,7 @@
                 ALOGI("TEARDOWN completed with result %d (%s)",
                      result, strerror(-result));
 
-                sp<AMessage> reply = new AMessage('disc', id());
+                sp<AMessage> reply = new AMessage('disc', this);
 
                 int32_t reconnect;
                 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
@@ -958,7 +958,7 @@
                 if (mNumAccessUnitsReceived == 0) {
 #if 1
                     ALOGI("stream ended? aborting.");
-                    (new AMessage('abor', id()))->post();
+                    (new AMessage('abor', this))->post();
                     break;
 #else
                     ALOGI("haven't seen an AU in a looong time.");
@@ -1077,7 +1077,7 @@
 
                 request.append("\r\n");
 
-                sp<AMessage> reply = new AMessage('pau2', id());
+                sp<AMessage> reply = new AMessage('pau2', this);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -1114,7 +1114,7 @@
 
                 request.append("\r\n");
 
-                sp<AMessage> reply = new AMessage('res2', id());
+                sp<AMessage> reply = new AMessage('res2', this);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -1143,7 +1143,7 @@
 
                         // Post new timeout in order to make sure to use
                         // fake timestamps if no new Sender Reports arrive
-                        sp<AMessage> timeout = new AMessage('tiou', id());
+                        sp<AMessage> timeout = new AMessage('tiou', this);
                         mCheckTimeoutGeneration++;
                         timeout->setInt32("tioucheck", mCheckTimeoutGeneration);
                         timeout->post(kStartupTimeoutUs);
@@ -1152,7 +1152,7 @@
 
                 if (result != OK) {
                     ALOGE("resume failed, aborting.");
-                    (new AMessage('abor', id()))->post();
+                    (new AMessage('abor', this))->post();
                 }
 
                 mPausing = false;
@@ -1180,7 +1180,7 @@
                 mCheckPending = true;
                 ++mCheckGeneration;
 
-                sp<AMessage> reply = new AMessage('see1', id());
+                sp<AMessage> reply = new AMessage('see1', this);
                 reply->setInt64("time", timeUs);
 
                 if (mPausing) {
@@ -1221,7 +1221,7 @@
 
                 // Start new timeoutgeneration to avoid getting timeout
                 // before PLAY response arrive
-                sp<AMessage> timeout = new AMessage('tiou', id());
+                sp<AMessage> timeout = new AMessage('tiou', this);
                 mCheckTimeoutGeneration++;
                 timeout->setInt32("tioucheck", mCheckTimeoutGeneration);
                 timeout->post(kStartupTimeoutUs);
@@ -1243,7 +1243,7 @@
 
                 request.append("\r\n");
 
-                sp<AMessage> reply = new AMessage('see2', id());
+                sp<AMessage> reply = new AMessage('see2', this);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -1277,7 +1277,7 @@
 
                         // Post new timeout in order to make sure to use
                         // fake timestamps if no new Sender Reports arrive
-                        sp<AMessage> timeout = new AMessage('tiou', id());
+                        sp<AMessage> timeout = new AMessage('tiou', this);
                         mCheckTimeoutGeneration++;
                         timeout->setInt32("tioucheck", mCheckTimeoutGeneration);
                         timeout->post(kStartupTimeoutUs);
@@ -1293,7 +1293,7 @@
 
                 if (result != OK) {
                     ALOGE("seek failed, aborting.");
-                    (new AMessage('abor', id()))->post();
+                    (new AMessage('abor', this))->post();
                 }
 
                 mPausing = false;
@@ -1343,12 +1343,12 @@
 
                         mTryTCPInterleaving = true;
 
-                        sp<AMessage> msg = new AMessage('abor', id());
+                        sp<AMessage> msg = new AMessage('abor', this);
                         msg->setInt32("reconnect", true);
                         msg->post();
                     } else {
                         ALOGW("Never received any data, disconnecting.");
-                        (new AMessage('abor', id()))->post();
+                        (new AMessage('abor', this))->post();
                     }
                 } else {
                     if (!mAllTracksHaveTime) {
@@ -1369,7 +1369,7 @@
     }
 
     void postKeepAlive() {
-        sp<AMessage> msg = new AMessage('aliv', id());
+        sp<AMessage> msg = new AMessage('aliv', this);
         msg->setInt32("generation", mKeepAliveGeneration);
         msg->post((mKeepAliveTimeoutUs * 9) / 10);
     }
@@ -1380,7 +1380,7 @@
         }
 
         mCheckPending = true;
-        sp<AMessage> check = new AMessage('chek', id());
+        sp<AMessage> check = new AMessage('chek', this);
         check->setInt32("generation", mCheckGeneration);
         check->post(kAccessUnitTimeoutUs);
     }
@@ -1566,7 +1566,7 @@
         if (source->initCheck() != OK) {
             ALOGW("Unsupported format. Ignoring track #%d.", index);
 
-            sp<AMessage> reply = new AMessage('setu', id());
+            sp<AMessage> reply = new AMessage('setu', this);
             reply->setSize("index", index);
             reply->setInt32("result", ERROR_UNSUPPORTED);
             reply->post();
@@ -1652,7 +1652,7 @@
 
         request.append("\r\n");
 
-        sp<AMessage> reply = new AMessage('setu', id());
+        sp<AMessage> reply = new AMessage('setu', this);
         reply->setSize("index", index);
         reply->setSize("track-index", mTracks.size() - 1);
         mConn->sendRequest(request.c_str(), reply);
diff --git a/media/libstagefright/rtsp/MyTransmitter.h b/media/libstagefright/rtsp/MyTransmitter.h
index 009a3b1..369f276 100644
--- a/media/libstagefright/rtsp/MyTransmitter.h
+++ b/media/libstagefright/rtsp/MyTransmitter.h
@@ -100,7 +100,7 @@
         mLooper->registerHandler(this);
         mLooper->registerHandler(mConn);
 
-        sp<AMessage> reply = new AMessage('conn', id());
+        sp<AMessage> reply = new AMessage('conn', this);
         mConn->connect(mServerURL.c_str(), reply);
 
 #ifdef ANDROID
@@ -229,7 +229,7 @@
         request.append("\r\n");
         request.append(sdp);
 
-        sp<AMessage> reply = new AMessage('anno', id());
+        sp<AMessage> reply = new AMessage('anno', this);
         mConn->sendRequest(request.c_str(), reply);
     }
 
@@ -350,7 +350,7 @@
                      << result << " (" << strerror(-result) << ")";
 
                 if (result != OK) {
-                    (new AMessage('quit', id()))->post();
+                    (new AMessage('quit', this))->post();
                     break;
                 }
 
@@ -381,7 +381,7 @@
                     if (response->mStatusCode == 401) {
                         if (mAuthType != NONE) {
                             LOG(INFO) << "FAILED to authenticate";
-                            (new AMessage('quit', id()))->post();
+                            (new AMessage('quit', this))->post();
                             break;
                         }
 
@@ -391,14 +391,14 @@
                 }
 
                 if (result != OK || response->mStatusCode != 200) {
-                    (new AMessage('quit', id()))->post();
+                    (new AMessage('quit', this))->post();
                     break;
                 }
 
                 unsigned rtpPort;
                 ARTPConnection::MakePortPair(&mRTPSocket, &mRTCPSocket, &rtpPort);
 
-                // (new AMessage('poll', id()))->post();
+                // (new AMessage('poll', this))->post();
 
                 AString request;
                 request.append("SETUP ");
@@ -414,7 +414,7 @@
                 request.append(";mode=record\r\n");
                 request.append("\r\n");
 
-                sp<AMessage> reply = new AMessage('setu', id());
+                sp<AMessage> reply = new AMessage('setu', this);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -468,7 +468,7 @@
                 }
 
                 if (result != OK || response->mStatusCode != 200) {
-                    (new AMessage('quit', id()))->post();
+                    (new AMessage('quit', this))->post();
                     break;
                 }
 
@@ -535,7 +535,7 @@
                 request.append("\r\n");
                 request.append("\r\n");
 
-                sp<AMessage> reply = new AMessage('reco', id());
+                sp<AMessage> reply = new AMessage('reco', this);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -558,13 +558,13 @@
                 }
 
                 if (result != OK) {
-                    (new AMessage('quit', id()))->post();
+                    (new AMessage('quit', this))->post();
                     break;
                 }
 
-                (new AMessage('more', id()))->post();
-                (new AMessage('sr  ', id()))->post();
-                (new AMessage('aliv', id()))->post(30000000ll);
+                (new AMessage('more', this))->post();
+                (new AMessage('sr  ', this))->post();
+                (new AMessage('aliv', this))->post(30000000ll);
                 break;
             }
 
@@ -586,7 +586,7 @@
                 request.append("\r\n");
                 request.append("\r\n");
 
-                sp<AMessage> reply = new AMessage('opts', id());
+                sp<AMessage> reply = new AMessage('opts', this);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -603,7 +603,7 @@
                     break;
                 }
 
-                (new AMessage('aliv', id()))->post(30000000ll);
+                (new AMessage('aliv', this))->post(30000000ll);
                 break;
             }
 
@@ -702,7 +702,7 @@
                     request.append("\r\n");
                     request.append("\r\n");
 
-                    sp<AMessage> reply = new AMessage('paus', id());
+                    sp<AMessage> reply = new AMessage('paus', this);
                     mConn->sendRequest(request.c_str(), reply);
                 }
                 break;
@@ -753,7 +753,7 @@
                 request.append("\r\n");
                 request.append("\r\n");
 
-                sp<AMessage> reply = new AMessage('tear', id());
+                sp<AMessage> reply = new AMessage('tear', this);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -775,7 +775,7 @@
                     CHECK(response != NULL);
                 }
 
-                (new AMessage('quit', id()))->post();
+                (new AMessage('quit', this))->post();
                 break;
             }
 
@@ -784,14 +784,14 @@
                 LOG(INFO) << "disconnect completed";
 
                 mConnected = false;
-                (new AMessage('quit', id()))->post();
+                (new AMessage('quit', this))->post();
                 break;
             }
 
             case 'quit':
             {
                 if (mConnected) {
-                    mConn->disconnect(new AMessage('disc', id()));
+                    mConn->disconnect(new AMessage('disc', this));
                     break;
                 }
 
diff --git a/media/libstagefright/rtsp/SDPLoader.cpp b/media/libstagefright/rtsp/SDPLoader.cpp
index a24eb69..0f46c83 100644
--- a/media/libstagefright/rtsp/SDPLoader.cpp
+++ b/media/libstagefright/rtsp/SDPLoader.cpp
@@ -51,7 +51,7 @@
 void SDPLoader::load(const char *url, const KeyedVector<String8, String8> *headers) {
     mNetLooper->registerHandler(this);
 
-    sp<AMessage> msg = new AMessage(kWhatLoad, id());
+    sp<AMessage> msg = new AMessage(kWhatLoad, this);
     msg->setString("url", url);
 
     if (headers != NULL) {
diff --git a/media/libstagefright/rtsp/UDPPusher.cpp b/media/libstagefright/rtsp/UDPPusher.cpp
index 47ea6f1..5c685a1 100644
--- a/media/libstagefright/rtsp/UDPPusher.cpp
+++ b/media/libstagefright/rtsp/UDPPusher.cpp
@@ -65,7 +65,7 @@
     mFirstTimeMs = fromlel(timeMs);
     mFirstTimeUs = ALooper::GetNowUs();
 
-    (new AMessage(kWhatPush, id()))->post();
+    (new AMessage(kWhatPush, this))->post();
 }
 
 bool UDPPusher::onPush() {
@@ -103,7 +103,7 @@
     timeMs -= mFirstTimeMs;
     int64_t whenUs = mFirstTimeUs + timeMs * 1000ll;
     int64_t nowUs = ALooper::GetNowUs();
-    (new AMessage(kWhatPush, id()))->post(whenUs - nowUs);
+    (new AMessage(kWhatPush, this))->post(whenUs - nowUs);
 
     return true;
 }
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index a070487..aecf666 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -56,25 +56,25 @@
 }
 
 void TimedTextPlayer::start() {
-    (new AMessage(kWhatStart, id()))->post();
+    (new AMessage(kWhatStart, this))->post();
 }
 
 void TimedTextPlayer::pause() {
-    (new AMessage(kWhatPause, id()))->post();
+    (new AMessage(kWhatPause, this))->post();
 }
 
 void TimedTextPlayer::resume() {
-    (new AMessage(kWhatResume, id()))->post();
+    (new AMessage(kWhatResume, this))->post();
 }
 
 void TimedTextPlayer::seekToAsync(int64_t timeUs) {
-    sp<AMessage> msg = new AMessage(kWhatSeek, id());
+    sp<AMessage> msg = new AMessage(kWhatSeek, this);
     msg->setInt64("seekTimeUs", timeUs);
     msg->post();
 }
 
 void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) {
-    sp<AMessage> msg = new AMessage(kWhatSetSource, id());
+    sp<AMessage> msg = new AMessage(kWhatSetSource, this);
     msg->setObject("source", source);
     msg->post();
 }
@@ -231,7 +231,7 @@
     status_t err = mSource->read(&startTimeUs, &endTimeUs,
                                  &(parcelEvent->parcel), options);
     if (err == WOULD_BLOCK) {
-        sp<AMessage> msg = new AMessage(kWhatRetryRead, id());
+        sp<AMessage> msg = new AMessage(kWhatRetryRead, this);
         if (options != NULL) {
             int64_t seekTimeUs = kInvalidTimeUs;
             MediaSource::ReadOptions::SeekMode seekMode =
@@ -259,7 +259,7 @@
 
 void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
     int64_t delayUs = delayUsFromCurrentTime(timeUs);
-    sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id());
+    sp<AMessage> msg = new AMessage(kWhatSendSubtitle, this);
     msg->setInt32("generation", mSendSubtitleGeneration);
     if (parcel != NULL) {
         msg->setObject("subtitle", parcel);
diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp
index b1cdec0..6f0087f 100644
--- a/media/libstagefright/wifi-display/MediaSender.cpp
+++ b/media/libstagefright/wifi-display/MediaSender.cpp
@@ -121,7 +121,7 @@
         }
 
         if (err == OK) {
-            sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+            sp<AMessage> notify = new AMessage(kWhatSenderNotify, this);
             notify->setInt32("generation", mGeneration);
             mTSSender = new RTPSender(mNetSession, notify);
             looper()->registerHandler(mTSSender);
@@ -170,7 +170,7 @@
         return INVALID_OPERATION;
     }
 
-    sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatSenderNotify, this);
     notify->setInt32("generation", mGeneration);
     notify->setSize("trackIndex", trackIndex);
 
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
index e88a3bd..4e72533 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -95,11 +95,11 @@
         return INVALID_OPERATION;
     }
 
-    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, this);
 
     sp<AMessage> rtcpNotify;
     if (remoteRTCPPort >= 0) {
-        rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+        rtcpNotify = new AMessage(kWhatRTCPNotify, this);
     }
 
     CHECK_EQ(mRTPSessionID, 0);
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 2834a66..8368945 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -93,7 +93,7 @@
 
 void Converter::shutdownAsync() {
     ALOGV("shutdown");
-    (new AMessage(kWhatShutdown, id()))->post();
+    (new AMessage(kWhatShutdown, this))->post();
 }
 
 status_t Converter::init() {
@@ -482,11 +482,11 @@
 
 #if 1
     if (mEncoderActivityNotify == NULL) {
-        mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, id());
+        mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, this);
     }
     mEncoder->requestActivityNotification(mEncoderActivityNotify->dup());
 #else
-    sp<AMessage> notify = new AMessage(kWhatEncoderActivity, id());
+    sp<AMessage> notify = new AMessage(kWhatEncoderActivity, this);
     notify->setInt64("whenUs", ALooper::GetNowUs());
     mEncoder->requestActivityNotification(notify);
 #endif
@@ -731,8 +731,7 @@
 
                 // MediaSender will post the following message when HDCP
                 // is done, to release the output buffer back to encoder.
-                sp<AMessage> notify(new AMessage(
-                        kWhatReleaseOutputBuffer, id()));
+                sp<AMessage> notify(new AMessage(kWhatReleaseOutputBuffer, this));
                 notify->setInt32("bufferIndex", bufferIndex);
 
                 buffer = new ABuffer(
@@ -787,18 +786,18 @@
 }
 
 void Converter::requestIDRFrame() {
-    (new AMessage(kWhatRequestIDRFrame, id()))->post();
+    (new AMessage(kWhatRequestIDRFrame, this))->post();
 }
 
 void Converter::dropAFrame() {
     // Unsupported in surface input mode.
     CHECK(!(mFlags & FLAG_USE_SURFACE_INPUT));
 
-    (new AMessage(kWhatDropAFrame, id()))->post();
+    (new AMessage(kWhatDropAFrame, this))->post();
 }
 
 void Converter::suspendEncoding(bool suspend) {
-    sp<AMessage> msg = new AMessage(kWhatSuspendEncoding, id());
+    sp<AMessage> msg = new AMessage(kWhatSuspendEncoding, this);
     msg->setInt32("suspend", suspend);
     msg->post();
 }
diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp
index 86b918f..ce07a4e 100644
--- a/media/libstagefright/wifi-display/source/MediaPuller.cpp
+++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp
@@ -63,21 +63,21 @@
 }
 
 status_t MediaPuller::start() {
-    return postSynchronouslyAndReturnError(new AMessage(kWhatStart, id()));
+    return postSynchronouslyAndReturnError(new AMessage(kWhatStart, this));
 }
 
 void MediaPuller::stopAsync(const sp<AMessage> &notify) {
-    sp<AMessage> msg = new AMessage(kWhatStop, id());
+    sp<AMessage> msg = new AMessage(kWhatStop, this);
     msg->setMessage("notify", notify);
     msg->post();
 }
 
 void MediaPuller::pause() {
-    (new AMessage(kWhatPause, id()))->post();
+    (new AMessage(kWhatPause, this))->post();
 }
 
 void MediaPuller::resume() {
-    (new AMessage(kWhatResume, id()))->post();
+    (new AMessage(kWhatResume, this))->post();
 }
 
 void MediaPuller::onMessageReceived(const sp<AMessage> &msg) {
@@ -105,7 +105,7 @@
             sp<AMessage> response = new AMessage;
             response->setInt32("err", err);
 
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
             response->postReply(replyID);
             break;
@@ -215,7 +215,7 @@
 }
 
 void MediaPuller::schedulePull() {
-    sp<AMessage> msg = new AMessage(kWhatPull, id());
+    sp<AMessage> msg = new AMessage(kWhatPull, this);
     msg->setInt32("generation", mPullGeneration);
     msg->post();
 }
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 2cb4786..6080943 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -214,7 +214,7 @@
         mConverter->shutdownAsync();
     }
 
-    sp<AMessage> msg = new AMessage(kWhatMediaPullerStopped, id());
+    sp<AMessage> msg = new AMessage(kWhatMediaPullerStopped, this);
 
     if (mStarted && mMediaPuller != NULL) {
         if (mRepeaterSource != NULL) {
@@ -382,7 +382,7 @@
         size_t videoResolutionIndex,
         VideoFormats::ProfileType videoProfileType,
         VideoFormats::LevelType videoLevelType) {
-    sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, this);
     mMediaSender = new MediaSender(mNetSession, notify);
     looper()->registerHandler(mMediaSender);
 
@@ -440,7 +440,7 @@
 status_t WifiDisplaySource::PlaybackSession::play() {
     updateLiveness();
 
-    (new AMessage(kWhatResume, id()))->post();
+    (new AMessage(kWhatResume, this))->post();
 
     return OK;
 }
@@ -460,7 +460,7 @@
 status_t WifiDisplaySource::PlaybackSession::pause() {
     updateLiveness();
 
-    (new AMessage(kWhatPause, id()))->post();
+    (new AMessage(kWhatPause, this))->post();
 
     return OK;
 }
@@ -786,7 +786,7 @@
 
         size_t trackIndex = mTracks.size();
 
-        sp<AMessage> notify = new AMessage(kWhatTrackNotify, id());
+        sp<AMessage> notify = new AMessage(kWhatTrackNotify, this);
         notify->setSize("trackIndex", trackIndex);
 
         sp<Track> track = new Track(notify, format);
@@ -833,7 +833,7 @@
 
     int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs;
 
-    sp<AMessage> msg = new AMessage(kWhatPullExtractorSample, id());
+    sp<AMessage> msg = new AMessage(kWhatPullExtractorSample, this);
     msg->setInt32("generation", mPullExtractorGeneration);
     msg->post(whenUs - nowUs);
 
@@ -857,7 +857,7 @@
     size_t trackIndex;
     CHECK_EQ((status_t)OK, mExtractor->getSampleTrackIndex(&trackIndex));
 
-    sp<AMessage> msg = new AMessage(kWhatConverterNotify, id());
+    sp<AMessage> msg = new AMessage(kWhatConverterNotify, this);
 
     msg->setSize(
             "trackIndex", mExtractorTrackToInternalTrack.valueFor(trackIndex));
@@ -955,7 +955,7 @@
                     ? MEDIA_MIMETYPE_AUDIO_RAW : MEDIA_MIMETYPE_AUDIO_AAC);
     }
 
-    notify = new AMessage(kWhatConverterNotify, id());
+    notify = new AMessage(kWhatConverterNotify, this);
     notify->setSize("trackIndex", trackIndex);
 
     sp<Converter> converter = new Converter(notify, codecLooper, format);
@@ -970,7 +970,7 @@
         return err;
     }
 
-    notify = new AMessage(Converter::kWhatMediaPullerNotify, converter->id());
+    notify = new AMessage(Converter::kWhatMediaPullerNotify, converter);
     notify->setSize("trackIndex", trackIndex);
 
     sp<MediaPuller> puller = new MediaPuller(source, notify);
@@ -980,7 +980,7 @@
         *numInputBuffers = converter->getInputBufferCount();
     }
 
-    notify = new AMessage(kWhatTrackNotify, id());
+    notify = new AMessage(kWhatTrackNotify, this);
     notify->setSize("trackIndex", trackIndex);
 
     sp<Track> track = new Track(
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
index 59d7e6e..af6b663 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
@@ -173,7 +173,7 @@
 }
 
 void RepeaterSource::postRead() {
-    (new AMessage(kWhatRead, mReflector->id()))->post();
+    (new AMessage(kWhatRead, mReflector))->post();
 }
 
 void RepeaterSource::onMessageReceived(const sp<AMessage> &msg) {
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 7eb8b73..14d0951 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -57,7 +57,7 @@
       mNetSession(netSession),
       mClient(client),
       mSessionID(0),
-      mStopReplyID(0),
+      mStopReplyID(NULL),
       mChosenRTPPort(-1),
       mUsingPCMAudio(false),
       mClientSessionID(0),
@@ -106,7 +106,7 @@
 status_t WifiDisplaySource::start(const char *iface) {
     CHECK_EQ(mState, INITIALIZED);
 
-    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    sp<AMessage> msg = new AMessage(kWhatStart, this);
     msg->setString("iface", iface);
 
     sp<AMessage> response;
@@ -114,21 +114,21 @@
 }
 
 status_t WifiDisplaySource::stop() {
-    sp<AMessage> msg = new AMessage(kWhatStop, id());
+    sp<AMessage> msg = new AMessage(kWhatStop, this);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t WifiDisplaySource::pause() {
-    sp<AMessage> msg = new AMessage(kWhatPause, id());
+    sp<AMessage> msg = new AMessage(kWhatPause, this);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
 status_t WifiDisplaySource::resume() {
-    sp<AMessage> msg = new AMessage(kWhatResume, id());
+    sp<AMessage> msg = new AMessage(kWhatResume, this);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
@@ -138,7 +138,7 @@
     switch (msg->what()) {
         case kWhatStart:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             AString iface;
@@ -167,7 +167,7 @@
 
             if (err == OK) {
                 if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) {
-                    sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());
+                    sp<AMessage> notify = new AMessage(kWhatRTSPNotify, this);
 
                     err = mNetSession->createRTSPServer(
                             mInterfaceAddr, port, notify, &mSessionID);
@@ -310,7 +310,7 @@
                 if (err == OK) {
                     mState = AWAITING_CLIENT_TEARDOWN;
 
-                    (new AMessage(kWhatTeardownTriggerTimedOut, id()))->post(
+                    (new AMessage(kWhatTeardownTriggerTimedOut, this))->post(
                             kTeardownTriggerTimeouSecs * 1000000ll);
 
                     break;
@@ -325,7 +325,7 @@
 
         case kWhatPause:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             status_t err = OK;
@@ -345,7 +345,7 @@
 
         case kWhatResume:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             status_t err = OK;
@@ -492,7 +492,7 @@
             if (mState == AWAITING_CLIENT_TEARDOWN) {
                 ALOGI("TEARDOWN trigger timed out, forcing disconnection.");
 
-                CHECK_NE(mStopReplyID, 0);
+                CHECK(mStopReplyID != NULL);
                 finishStop();
                 break;
             }
@@ -529,7 +529,7 @@
                     // HDCPObserver::notify is completely handled before
                     // we clear the HDCP instance and unload the shared
                     // library :(
-                    (new AMessage(kWhatFinishStop2, id()))->post(300000ll);
+                    (new AMessage(kWhatFinishStop2, this))->post(300000ll);
                     break;
                 }
 
@@ -1027,7 +1027,7 @@
     }
 
     mReaperPending = true;
-    (new AMessage(kWhatReapDeadClients, id()))->post(kReaperIntervalUs);
+    (new AMessage(kWhatReapDeadClients, this))->post(kReaperIntervalUs);
 }
 
 void WifiDisplaySource::scheduleKeepAlive(int32_t sessionID) {
@@ -1035,7 +1035,7 @@
     // expire, make sure the timeout is greater than 5 secs to begin with.
     CHECK_GT(kPlaybackSessionTimeoutUs, 5000000ll);
 
-    sp<AMessage> msg = new AMessage(kWhatKeepAlive, id());
+    sp<AMessage> msg = new AMessage(kWhatKeepAlive, this);
     msg->setInt32("sessionID", sessionID);
     msg->post(kPlaybackSessionTimeoutUs - 5000000ll);
 }
@@ -1239,7 +1239,7 @@
 
     int32_t playbackSessionID = makeUniquePlaybackSessionID();
 
-    sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, this);
     notify->setInt32("playbackSessionID", playbackSessionID);
     notify->setInt32("sessionID", sessionID);
 
@@ -1470,7 +1470,7 @@
     mNetSession->sendRequest(sessionID, response.c_str());
 
     if (mState == AWAITING_CLIENT_TEARDOWN) {
-        CHECK_NE(mStopReplyID, 0);
+        CHECK(mStopReplyID != NULL);
         finishStop();
     } else {
         mClient->onDisplayError(IRemoteDisplayClient::kDisplayErrorUnknown);
@@ -1707,7 +1707,7 @@
         return ERROR_UNSUPPORTED;
     }
 
-    sp<AMessage> notify = new AMessage(kWhatHDCPNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatHDCPNotify, this);
     mHDCPObserver = new HDCPObserver(notify);
 
     status_t err = mHDCP->setObserver(mHDCPObserver);
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 750265f..0f779e4 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -27,6 +27,7 @@
 
 namespace android {
 
+struct AReplyToken;
 struct IHDCP;
 struct IRemoteDisplayClient;
 struct ParsedMessage;
@@ -121,7 +122,7 @@
     struct in_addr mInterfaceAddr;
     int32_t mSessionID;
 
-    uint32_t mStopReplyID;
+    sp<AReplyToken> mStopReplyID;
 
     AString mWfdClientRtpPorts;
     int32_t mChosenRTPPort;  // extracted from "wfd_client_rtp_ports"
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index f1b84ad..0ad0bf3 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -26,7 +26,8 @@
 	libutils \
 	liblog \
 	libbinder \
-	libsoundtriggerservice
+	libsoundtriggerservice \
+	libradioservice
 
 LOCAL_STATIC_LIBRARIES := \
 	libregistermsext
@@ -38,7 +39,8 @@
     frameworks/av/services/audiopolicy \
     frameworks/av/services/camera/libcameraservice \
     $(call include-path-for, audio-utils) \
-    frameworks/av/services/soundtrigger
+    frameworks/av/services/soundtrigger \
+    frameworks/av/services/radio
 
 LOCAL_MODULE:= mediaserver
 LOCAL_32_BIT_ONLY := true
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 263dd32..99572f8 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -35,6 +35,7 @@
 #include "MediaPlayerService.h"
 #include "service/AudioPolicyService.h"
 #include "SoundTriggerHwService.h"
+#include "RadioService.h"
 
 using namespace android;
 
@@ -130,6 +131,7 @@
         CameraService::instantiate();
         AudioPolicyService::instantiate();
         SoundTriggerHwService::instantiate();
+        RadioService::instantiate();
         registerExtensions();
         ProcessState::self()->startThreadPool();
         IPCThreadState::self()->joinThreadPool();
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 3124e4a..80c1c2f 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -116,7 +116,7 @@
 
         case kWhatStopActivityNotifications:
         {
-            uint32_t replyID;
+            sp<AReplyToken> replyID;
             msg->senderAwaitsResponse(&replyID);
 
             mCodec->mGeneration++;
@@ -136,7 +136,7 @@
 
 
 static void requestActivityNotification(AMediaCodec *codec) {
-    (new AMessage(kWhatRequestActivityNotifications, codec->mHandler->id()))->post();
+    (new AMessage(kWhatRequestActivityNotifications, codec->mHandler))->post();
 }
 
 extern "C" {
@@ -219,7 +219,7 @@
     if (ret != OK) {
         return translate_error(ret);
     }
-    mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler->id());
+    mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler);
     mData->mActivityNotification->setInt32("generation", mData->mGeneration);
     requestActivityNotification(mData);
     return AMEDIA_OK;
@@ -229,7 +229,7 @@
 media_status_t AMediaCodec_stop(AMediaCodec *mData) {
     media_status_t ret = translate_error(mData->mCodec->stop());
 
-    sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler->id());
+    sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler);
     sp<AMessage> response;
     msg->postAndAwaitResponse(&response);
     mData->mActivityNotification.clear();
diff --git a/radio/Android.mk b/radio/Android.mk
new file mode 100644
index 0000000..ecbb8fd
--- /dev/null
+++ b/radio/Android.mk
@@ -0,0 +1,39 @@
+# Copyright 2014 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	Radio.cpp \
+	IRadio.cpp \
+	IRadioClient.cpp \
+	IRadioService.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	liblog \
+	libbinder \
+	libhardware \
+	libradio_metadata
+
+#LOCAL_C_INCLUDES += \
+	system/media/camera/include \
+	system/media/private/camera/include
+
+LOCAL_MODULE:= libradio
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/radio/IRadio.cpp b/radio/IRadio.cpp
new file mode 100644
index 0000000..242df77
--- /dev/null
+++ b/radio/IRadio.cpp
@@ -0,0 +1,344 @@
+/*
+**
+** Copyright 2015, 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_TAG "IRadio"
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <binder/IMemory.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioService.h>
+#include <radio/IRadioClient.h>
+#include <system/radio.h>
+#include <system/radio_metadata.h>
+
+namespace android {
+
+enum {
+    DETACH = IBinder::FIRST_CALL_TRANSACTION,
+    SET_CONFIGURATION,
+    GET_CONFIGURATION,
+    SET_MUTE,
+    GET_MUTE,
+    SCAN,
+    STEP,
+    TUNE,
+    CANCEL,
+    GET_PROGRAM_INFORMATION,
+    HAS_CONTROL
+};
+
+class BpRadio: public BpInterface<IRadio>
+{
+public:
+    BpRadio(const sp<IBinder>& impl)
+        : BpInterface<IRadio>(impl)
+    {
+    }
+
+    void detach()
+    {
+        ALOGV("detach");
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        remote()->transact(DETACH, data, &reply);
+    }
+
+    virtual status_t setConfiguration(const struct radio_band_config *config)
+    {
+        Parcel data, reply;
+        if (config == NULL) {
+            return BAD_VALUE;
+        }
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        data.write(config, sizeof(struct radio_band_config));
+        status_t status = remote()->transact(SET_CONFIGURATION, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t getConfiguration(struct radio_band_config *config)
+    {
+        Parcel data, reply;
+        if (config == NULL) {
+            return BAD_VALUE;
+        }
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_CONFIGURATION, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+            if (status == NO_ERROR) {
+                reply.read(config, sizeof(struct radio_band_config));
+            }
+        }
+        return status;
+    }
+
+    virtual status_t setMute(bool mute)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        data.writeInt32(mute ? 1 : 0);
+        status_t status = remote()->transact(SET_MUTE, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t getMute(bool *mute)
+    {
+        Parcel data, reply;
+        if (mute == NULL) {
+            return BAD_VALUE;
+        }
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_MUTE, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+            if (status == NO_ERROR) {
+                int muteread = reply.readInt32();
+                *mute = muteread != 0;
+            }
+        }
+        return status;
+    }
+
+    virtual status_t scan(radio_direction_t direction, bool skipSubChannel)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        data.writeInt32(direction);
+        data.writeInt32(skipSubChannel ? 1 : 0);
+        status_t status = remote()->transact(SCAN, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t step(radio_direction_t direction, bool skipSubChannel)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        data.writeInt32(direction);
+        data.writeInt32(skipSubChannel ? 1 : 0);
+        status_t status = remote()->transact(STEP, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t tune(unsigned int channel, unsigned int subChannel)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        data.writeInt32(channel);
+        data.writeInt32(subChannel);
+        status_t status = remote()->transact(TUNE, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t cancel()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        status_t status = remote()->transact(CANCEL, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t getProgramInformation(struct radio_program_info *info)
+    {
+        Parcel data, reply;
+        if (info == NULL) {
+            return BAD_VALUE;
+        }
+        radio_metadata_t *metadata = info->metadata;
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_PROGRAM_INFORMATION, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+            if (status == NO_ERROR) {
+                reply.read(info, sizeof(struct radio_program_info));
+                info->metadata = metadata;
+                if (metadata == NULL) {
+                    return status;
+                }
+                size_t size = (size_t)reply.readInt32();
+                if (size == 0) {
+                    return status;
+                }
+                metadata =
+                    (radio_metadata_t *)calloc(size / sizeof(unsigned int), sizeof(unsigned int));
+                if (metadata == NULL) {
+                    return NO_MEMORY;
+                }
+                reply.read(metadata, size);
+                status = radio_metadata_add_metadata(&info->metadata, metadata);
+                free(metadata);
+            }
+        }
+        return status;
+    }
+
+    virtual status_t hasControl(bool *hasControl)
+    {
+        Parcel data, reply;
+        if (hasControl == NULL) {
+            return BAD_VALUE;
+        }
+        data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        status_t status = remote()->transact(HAS_CONTROL, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+            if (status == NO_ERROR) {
+                *hasControl = reply.readInt32() != 0;
+            }
+        }
+        return status;
+    }
+};
+
+IMPLEMENT_META_INTERFACE(Radio, "android.hardware.IRadio");
+
+// ----------------------------------------------------------------------
+
+status_t BnRadio::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case DETACH: {
+            ALOGV("DETACH");
+            CHECK_INTERFACE(IRadio, data, reply);
+            detach();
+            return NO_ERROR;
+        } break;
+        case SET_CONFIGURATION: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            struct radio_band_config config;
+            data.read(&config, sizeof(struct radio_band_config));
+            status_t status = setConfiguration(&config);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        }
+        case GET_CONFIGURATION: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            struct radio_band_config config;
+            status_t status = getConfiguration(&config);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->write(&config, sizeof(struct radio_band_config));
+            }
+            return NO_ERROR;
+        }
+        case SET_MUTE: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            bool mute = data.readInt32() != 0;
+            status_t status = setMute(mute);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        }
+        case GET_MUTE: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            bool mute;
+            status_t status = getMute(&mute);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeInt32(mute ? 1 : 0);
+            }
+            return NO_ERROR;
+        }
+        case SCAN: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            radio_direction_t direction = (radio_direction_t)data.readInt32();
+            bool skipSubChannel = data.readInt32() == 1;
+            status_t status = scan(direction, skipSubChannel);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        }
+        case STEP: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            radio_direction_t direction = (radio_direction_t)data.readInt32();
+            bool skipSubChannel = data.readInt32() == 1;
+            status_t status = step(direction, skipSubChannel);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        }
+        case TUNE: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            unsigned int channel = (unsigned int)data.readInt32();
+            unsigned int subChannel = (unsigned int)data.readInt32();
+            status_t status = tune(channel, subChannel);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        }
+        case CANCEL: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            status_t status = cancel();
+            reply->writeInt32(status);
+            return NO_ERROR;
+        }
+        case GET_PROGRAM_INFORMATION: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            struct radio_program_info info;
+
+            status_t status = radio_metadata_allocate(&info.metadata, 0, 0);
+            if (status != NO_ERROR) {
+                return status;
+            }
+            status = getProgramInformation(&info);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->write(&info, sizeof(struct radio_program_info));
+                int count = radio_metadata_get_count(info.metadata);
+                if (count > 0) {
+                    size_t size = radio_metadata_get_size(info.metadata);
+                    reply->writeInt32(size);
+                    reply->write(info.metadata, size);
+                } else {
+                    reply->writeInt32(0);
+                }
+            }
+            radio_metadata_deallocate(info.metadata);
+            return NO_ERROR;
+        }
+        case HAS_CONTROL: {
+            CHECK_INTERFACE(IRadio, data, reply);
+            bool control;
+            status_t status = hasControl(&control);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeInt32(control ? 1 : 0);
+            }
+            return NO_ERROR;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/radio/IRadioClient.cpp b/radio/IRadioClient.cpp
new file mode 100644
index 0000000..033ca49
--- /dev/null
+++ b/radio/IRadioClient.cpp
@@ -0,0 +1,75 @@
+/*
+**
+** Copyright 2015, 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 <stdint.h>
+#include <sys/types.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <radio/IRadioClient.h>
+
+namespace android {
+
+enum {
+    ON_EVENT = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpRadioClient: public BpInterface<IRadioClient>
+{
+
+public:
+    BpRadioClient(const sp<IBinder>& impl)
+        : BpInterface<IRadioClient>(impl)
+    {
+    }
+
+    virtual void onEvent(const sp<IMemory>& eventMemory)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadioClient::getInterfaceDescriptor());
+        data.writeStrongBinder(IInterface::asBinder(eventMemory));
+        remote()->transact(ON_EVENT,
+                           data,
+                           &reply);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(RadioClient,
+                         "android.hardware.IRadioClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnRadioClient::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case ON_EVENT: {
+            CHECK_INTERFACE(IRadioClient, data, reply);
+            sp<IMemory> eventMemory = interface_cast<IMemory>(
+                data.readStrongBinder());
+            onEvent(eventMemory);
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }   return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/radio/IRadioService.cpp b/radio/IRadioService.cpp
new file mode 100644
index 0000000..8c2b3ef
--- /dev/null
+++ b/radio/IRadioService.cpp
@@ -0,0 +1,181 @@
+/*
+**
+** Copyright 2015, 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_TAG "BpRadioService"
+//
+#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <radio/IRadioService.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioClient.h>
+
+namespace android {
+
+enum {
+    LIST_MODULES = IBinder::FIRST_CALL_TRANSACTION,
+    ATTACH,
+};
+
+#define MAX_ITEMS_PER_LIST 1024
+
+class BpRadioService: public BpInterface<IRadioService>
+{
+public:
+    BpRadioService(const sp<IBinder>& impl)
+        : BpInterface<IRadioService>(impl)
+    {
+    }
+
+    virtual status_t listModules(struct radio_properties *properties,
+                                 uint32_t *numModules)
+    {
+        if (numModules == NULL || (*numModules != 0 && properties == NULL)) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadioService::getInterfaceDescriptor());
+        unsigned int numModulesReq = (properties == NULL) ? 0 : *numModules;
+        data.writeInt32(numModulesReq);
+        status_t status = remote()->transact(LIST_MODULES, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+            *numModules = (unsigned int)reply.readInt32();
+        }
+        ALOGV("listModules() status %d got *numModules %d", status, *numModules);
+        if (status == NO_ERROR) {
+            if (numModulesReq > *numModules) {
+                numModulesReq = *numModules;
+            }
+            if (numModulesReq > 0) {
+                reply.read(properties, numModulesReq * sizeof(struct radio_properties));
+            }
+        }
+        return status;
+    }
+
+    virtual status_t attach(radio_handle_t handle,
+                            const sp<IRadioClient>& client,
+                            const struct radio_band_config *config,
+                            bool withAudio,
+                            sp<IRadio>& radio)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IRadioService::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        data.writeStrongBinder(IInterface::asBinder(client));
+        ALOGV("attach() config %p withAudio %d region %d type %d", config, withAudio, config->region, config->band.type);
+        if (config == NULL) {
+            data.writeInt32(0);
+        } else {
+            data.writeInt32(1);
+            data.write(config, sizeof(struct radio_band_config));
+        }
+        data.writeInt32(withAudio ? 1 : 0);
+        status_t status = remote()->transact(ATTACH, data, &reply);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        status = reply.readInt32();
+        if (reply.readInt32() != 0) {
+            radio = interface_cast<IRadio>(reply.readStrongBinder());
+        }
+        return status;
+    }
+};
+
+IMPLEMENT_META_INTERFACE(RadioService, "android.hardware.IRadioService");
+
+// ----------------------------------------------------------------------
+
+status_t BnRadioService::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case LIST_MODULES: {
+            CHECK_INTERFACE(IRadioService, data, reply);
+            unsigned int numModulesReq = data.readInt32();
+            if (numModulesReq > MAX_ITEMS_PER_LIST) {
+                numModulesReq = MAX_ITEMS_PER_LIST;
+            }
+            unsigned int numModules = numModulesReq;
+            struct radio_properties *properties =
+                    (struct radio_properties *)calloc(numModulesReq,
+                                                      sizeof(struct radio_properties));
+            if (properties == NULL) {
+                reply->writeInt32(NO_MEMORY);
+                reply->writeInt32(0);
+                return NO_ERROR;
+            }
+
+            status_t status = listModules(properties, &numModules);
+            reply->writeInt32(status);
+            reply->writeInt32(numModules);
+            ALOGV("LIST_MODULES status %d got numModules %d", status, numModules);
+
+            if (status == NO_ERROR) {
+                if (numModulesReq > numModules) {
+                    numModulesReq = numModules;
+                }
+                reply->write(properties,
+                             numModulesReq * sizeof(struct radio_properties));
+            }
+            free(properties);
+            return NO_ERROR;
+        } break;
+
+        case ATTACH: {
+            CHECK_INTERFACE(IRadioService, data, reply);
+            radio_handle_t handle = data.readInt32();
+            sp<IRadioClient> client =
+                    interface_cast<IRadioClient>(data.readStrongBinder());
+            struct radio_band_config config;
+            struct radio_band_config *configPtr = NULL;
+            if (data.readInt32() != 0) {
+                data.read(&config, sizeof(struct radio_band_config));
+                configPtr = &config;
+            }
+            bool withAudio = data.readInt32() != 0;
+            ALOGV("ATTACH configPtr %p withAudio %d", configPtr, withAudio);
+            sp<IRadio> radio;
+            status_t status = attach(handle, client, configPtr, withAudio, radio);
+            reply->writeInt32(status);
+            if (radio != 0) {
+                reply->writeInt32(1);
+                reply->writeStrongBinder(IInterface::asBinder(radio));
+            } else {
+                reply->writeInt32(0);
+            }
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/radio/Radio.cpp b/radio/Radio.cpp
new file mode 100644
index 0000000..e3554c2
--- /dev/null
+++ b/radio/Radio.cpp
@@ -0,0 +1,283 @@
+/*
+**
+** Copyright (C) 2015, 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_TAG "Radio"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IMemory.h>
+
+#include <radio/Radio.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioService.h>
+#include <radio/IRadioClient.h>
+#include <radio/RadioCallback.h>
+
+namespace android {
+
+namespace {
+    sp<IRadioService>          gRadioService;
+    const int                  kRadioServicePollDelay = 500000; // 0.5s
+    const char*                kRadioServiceName      = "media.radio";
+    Mutex                      gLock;
+
+    class DeathNotifier : public IBinder::DeathRecipient
+    {
+    public:
+        DeathNotifier() {
+        }
+
+        virtual void binderDied(const wp<IBinder>& who __unused) {
+            ALOGV("binderDied");
+            Mutex::Autolock _l(gLock);
+            gRadioService.clear();
+            ALOGW("Radio service died!");
+        }
+    };
+
+    sp<DeathNotifier>         gDeathNotifier;
+}; // namespace anonymous
+
+const sp<IRadioService>& Radio::getRadioService()
+{
+    Mutex::Autolock _l(gLock);
+    if (gRadioService.get() == 0) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder;
+        do {
+            binder = sm->getService(String16(kRadioServiceName));
+            if (binder != 0) {
+                break;
+            }
+            ALOGW("RadioService not published, waiting...");
+            usleep(kRadioServicePollDelay);
+        } while(true);
+        if (gDeathNotifier == NULL) {
+            gDeathNotifier = new DeathNotifier();
+        }
+        binder->linkToDeath(gDeathNotifier);
+        gRadioService = interface_cast<IRadioService>(binder);
+    }
+    ALOGE_IF(gRadioService == 0, "no RadioService!?");
+    return gRadioService;
+}
+
+// Static methods
+status_t Radio::listModules(struct radio_properties *properties,
+                            uint32_t *numModules)
+{
+    ALOGV("listModules()");
+    const sp<IRadioService>& service = getRadioService();
+    if (service == 0) {
+        return NO_INIT;
+    }
+    return service->listModules(properties, numModules);
+}
+
+sp<Radio> Radio::attach(radio_handle_t handle,
+                        const struct radio_band_config *config,
+                        bool withAudio,
+                        const sp<RadioCallback>& callback)
+{
+    ALOGV("attach()");
+    sp<Radio> radio;
+    const sp<IRadioService>& service = getRadioService();
+    if (service == 0) {
+        return radio;
+    }
+    radio = new Radio(handle, callback);
+    status_t status = service->attach(handle, radio, config, withAudio, radio->mIRadio);
+
+    if (status == NO_ERROR && radio->mIRadio != 0) {
+        IInterface::asBinder(radio->mIRadio)->linkToDeath(radio);
+    } else {
+        ALOGW("Error %d connecting to radio service", status);
+        radio.clear();
+    }
+    return radio;
+}
+
+
+
+// Radio
+Radio::Radio(radio_handle_t handle, const sp<RadioCallback>& callback)
+    : mHandle(handle), mCallback(callback)
+{
+}
+
+Radio::~Radio()
+{
+    if (mIRadio != 0) {
+        mIRadio->detach();
+    }
+}
+
+
+void Radio::detach() {
+    ALOGV("detach()");
+    Mutex::Autolock _l(mLock);
+    mCallback.clear();
+    if (mIRadio != 0) {
+        mIRadio->detach();
+        IInterface::asBinder(mIRadio)->unlinkToDeath(this);
+        mIRadio = 0;
+    }
+}
+
+status_t Radio::setConfiguration(const struct radio_band_config *config)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->setConfiguration(config);
+}
+
+status_t Radio::getConfiguration(struct radio_band_config *config)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->getConfiguration(config);
+}
+
+status_t Radio::setMute(bool mute)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->setMute(mute);
+}
+
+status_t Radio::getMute(bool *mute)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->getMute(mute);
+}
+
+status_t Radio::scan(radio_direction_t direction, bool skipSubchannel)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->scan(direction, skipSubchannel);
+}
+
+status_t Radio::step(radio_direction_t direction, bool skipSubchannel)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->step(direction, skipSubchannel);
+}
+
+status_t Radio::tune(unsigned int channel, unsigned int subChannel)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->tune(channel, subChannel);
+}
+
+status_t Radio::cancel()
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->cancel();
+}
+
+status_t Radio::getProgramInformation(struct radio_program_info *info)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->getProgramInformation(info);
+}
+
+status_t Radio::hasControl(bool *hasControl)
+{
+    Mutex::Autolock _l(mLock);
+    if (mIRadio == 0) {
+        return NO_INIT;
+    }
+    return mIRadio->hasControl(hasControl);
+}
+
+
+// BpRadioClient
+void Radio::onEvent(const sp<IMemory>& eventMemory)
+{
+    Mutex::Autolock _l(mLock);
+    if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+        return;
+    }
+
+    struct radio_event *event = (struct radio_event *)eventMemory->pointer();
+    // restore local metadata pointer from offset
+    switch (event->type) {
+    case RADIO_EVENT_TUNED:
+    case RADIO_EVENT_AF_SWITCH:
+        if (event->info.metadata != NULL) {
+            event->info.metadata =
+                    (radio_metadata_t *)((char *)event + (size_t)event->info.metadata);
+        }
+        break;
+    case RADIO_EVENT_METADATA:
+        if (event->metadata != NULL) {
+            event->metadata =
+                    (radio_metadata_t *)((char *)event + (size_t)event->metadata);
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (mCallback != 0) {
+        mCallback->onEvent(event);
+    }
+}
+
+
+//IBinder::DeathRecipient
+void Radio::binderDied(const wp<IBinder>& who __unused) {
+    Mutex::Autolock _l(mLock);
+    ALOGW("Radio server binder Died ");
+    mIRadio = 0;
+    struct radio_event event;
+    memset(&event, 0, sizeof(struct radio_event));
+    event.type = RADIO_EVENT_SERVER_DIED;
+    event.status = DEAD_OBJECT;
+    if (mCallback != 0) {
+        mCallback->onEvent(&event);
+    }
+}
+
+}; // namespace android
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 940267a..461b5d3 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1957,18 +1957,18 @@
 status_t AudioFlinger::openInput(audio_module_handle_t module,
                                           audio_io_handle_t *input,
                                           audio_config_t *config,
-                                          audio_devices_t *device,
+                                          audio_devices_t *devices,
                                           const String8& address,
                                           audio_source_t source,
                                           audio_input_flags_t flags)
 {
     Mutex::Autolock _l(mLock);
 
-    if (*device == AUDIO_DEVICE_NONE) {
+    if (*devices == AUDIO_DEVICE_NONE) {
         return BAD_VALUE;
     }
 
-    sp<RecordThread> thread = openInput_l(module, input, config, *device, address, source, flags);
+    sp<RecordThread> thread = openInput_l(module, input, config, *devices, address, source, flags);
 
     if (thread != 0) {
         // notify client processes of the new input creation
@@ -1981,12 +1981,12 @@
 sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t module,
                                                          audio_io_handle_t *input,
                                                          audio_config_t *config,
-                                                         audio_devices_t device,
+                                                         audio_devices_t devices,
                                                          const String8& address,
                                                          audio_source_t source,
                                                          audio_input_flags_t flags)
 {
-    AudioHwDevice *inHwDev = findSuitableHwDev_l(module, device);
+    AudioHwDevice *inHwDev = findSuitableHwDev_l(module, devices);
     if (inHwDev == NULL) {
         *input = AUDIO_IO_HANDLE_NONE;
         return 0;
@@ -1999,7 +1999,7 @@
     audio_config_t halconfig = *config;
     audio_hw_device_t *inHwHal = inHwDev->hwDevice();
     audio_stream_in_t *inStream = NULL;
-    status_t status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig,
+    status_t status = inHwHal->open_input_stream(inHwHal, *input, devices, &halconfig,
                                         &inStream, flags, address.string(), source);
     ALOGV("openInput_l() openInputStream returned input %p, SamplingRate %d"
            ", Format %#x, Channels %x, flags %#x, status %d addr %s",
@@ -2021,7 +2021,7 @@
         // FIXME describe the change proposed by HAL (save old values so we can log them here)
         ALOGV("openInput_l() reopening with proposed sampling rate and channel mask");
         inStream = NULL;
-        status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig,
+        status = inHwHal->open_input_stream(inHwHal, *input, devices, &halconfig,
                                             &inStream, flags, address.string(), source);
         // FIXME log this new status; HAL should not propose any further changes
     }
@@ -2086,7 +2086,7 @@
                                   inputStream,
                                   *input,
                                   primaryOutputDevice_l(),
-                                  device
+                                  devices
 #ifdef TEE_SINK
                                   , teeSink
 #endif
@@ -2809,13 +2809,13 @@
 
 
 struct Entry {
-#define MAX_NAME 32     // %Y%m%d%H%M%S_%d.wav
-    char mName[MAX_NAME];
+#define TEE_MAX_FILENAME 32 // %Y%m%d%H%M%S_%d.wav = 4+2+2+2+2+2+1+1+4+1 = 21
+    char mFileName[TEE_MAX_FILENAME];
 };
 
 int comparEntry(const void *p1, const void *p2)
 {
-    return strcmp(((const Entry *) p1)->mName, ((const Entry *) p2)->mName);
+    return strcmp(((const Entry *) p1)->mFileName, ((const Entry *) p2)->mFileName);
 }
 
 #ifdef TEE_SINK
@@ -2834,11 +2834,11 @@
         DIR *dir = opendir(teePath);
         teePath[teePathLen++] = '/';
         if (dir != NULL) {
-#define MAX_SORT 20 // number of entries to sort
-#define MAX_KEEP 10 // number of entries to keep
-            struct Entry entries[MAX_SORT];
+#define TEE_MAX_SORT 20 // number of entries to sort
+#define TEE_MAX_KEEP 10 // number of entries to keep
+            struct Entry entries[TEE_MAX_SORT];
             size_t entryCount = 0;
-            while (entryCount < MAX_SORT) {
+            while (entryCount < TEE_MAX_SORT) {
                 struct dirent de;
                 struct dirent *result = NULL;
                 int rc = readdir_r(dir, &de, &result);
@@ -2855,17 +2855,17 @@
                 }
                 // ignore non .wav file entries
                 size_t nameLen = strlen(de.d_name);
-                if (nameLen <= 4 || nameLen >= MAX_NAME ||
+                if (nameLen <= 4 || nameLen >= TEE_MAX_FILENAME ||
                         strcmp(&de.d_name[nameLen - 4], ".wav")) {
                     continue;
                 }
-                strcpy(entries[entryCount++].mName, de.d_name);
+                strcpy(entries[entryCount++].mFileName, de.d_name);
             }
             (void) closedir(dir);
-            if (entryCount > MAX_KEEP) {
+            if (entryCount > TEE_MAX_KEEP) {
                 qsort(entries, entryCount, sizeof(Entry), comparEntry);
-                for (size_t i = 0; i < entryCount - MAX_KEEP; ++i) {
-                    strcpy(&teePath[teePathLen], entries[i].mName);
+                for (size_t i = 0; i < entryCount - TEE_MAX_KEEP; ++i) {
+                    strcpy(&teePath[teePathLen], entries[i].mFileName);
                     (void) unlink(teePath);
                 }
             }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index f5dec97..7b76185 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -796,6 +796,10 @@
 #undef INCLUDING_FROM_AUDIOFLINGER_H
 
 const char *formatToString(audio_format_t format);
+String8 inputFlagsToString(audio_input_flags_t flags);
+String8 outputFlagsToString(audio_output_flags_t flags);
+String8 devicesToString(audio_devices_t devices);
+const char *sourceToString(audio_source_t source);
 
 // ----------------------------------------------------------------------------
 
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index e070f90..f1cf0aa 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -415,6 +415,7 @@
             memset(mMixerBuffer, 0, mMixerBufferSize);
             mMixerBufferState = ZEROED;
         }
+        // prepare the buffer used to write to sink
         void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer;
         if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format
             memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat,
diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp
index 386b29d..65fbf2b 100644
--- a/services/audioflinger/FastMixerDumpState.cpp
+++ b/services/audioflinger/FastMixerDumpState.cpp
@@ -69,10 +69,10 @@
                 "            numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
                 "            sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
                 "            mixPeriod=%.2f ms\n",
-                 FastMixerState::commandToString(mCommand), mWriteSequence, mFramesWritten,
-                 mNumTracks, mWriteErrors, mUnderruns, mOverruns,
-                 mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
-                 mixPeriodSec * 1e3);
+                FastMixerState::commandToString(mCommand), mWriteSequence, mFramesWritten,
+                mNumTracks, mWriteErrors, mUnderruns, mOverruns,
+                mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
+                mixPeriodSec * 1e3);
 #ifdef FAST_THREAD_STATISTICS
     // find the interval of valid samples
     uint32_t bounds = mBounds;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7451245..c1da6bc 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -335,7 +335,90 @@
     }
 }
 
-static String8 outputFlagsToString(audio_output_flags_t flags)
+String8 devicesToString(audio_devices_t devices)
+{
+    static const struct mapping {
+        audio_devices_t mDevices;
+        const char *    mString;
+    } mappingsOut[] = {
+        AUDIO_DEVICE_OUT_EARPIECE,          "EARPIECE",
+        AUDIO_DEVICE_OUT_SPEAKER,           "SPEAKER",
+        AUDIO_DEVICE_OUT_WIRED_HEADSET,     "WIRED_HEADSET",
+        AUDIO_DEVICE_OUT_WIRED_HEADPHONE,   "WIRED_HEADPHONE",
+        AUDIO_DEVICE_OUT_TELEPHONY_TX,      "TELEPHONY_TX",
+        AUDIO_DEVICE_NONE,                  "NONE",         // must be last
+    }, mappingsIn[] = {
+        AUDIO_DEVICE_IN_BUILTIN_MIC,        "BUILTIN_MIC",
+        AUDIO_DEVICE_IN_WIRED_HEADSET,      "WIRED_HEADSET",
+        AUDIO_DEVICE_IN_VOICE_CALL,         "VOICE_CALL",
+        AUDIO_DEVICE_IN_REMOTE_SUBMIX,      "REMOTE_SUBMIX",
+        AUDIO_DEVICE_NONE,                  "NONE",         // must be last
+    };
+    String8 result;
+    audio_devices_t allDevices = AUDIO_DEVICE_NONE;
+    const mapping *entry;
+    if (devices & AUDIO_DEVICE_BIT_IN) {
+        devices &= ~AUDIO_DEVICE_BIT_IN;
+        entry = mappingsIn;
+    } else {
+        entry = mappingsOut;
+    }
+    for ( ; entry->mDevices != AUDIO_DEVICE_NONE; entry++) {
+        allDevices = (audio_devices_t) (allDevices | entry->mDevices);
+        if (devices & entry->mDevices) {
+            if (!result.isEmpty()) {
+                result.append("|");
+            }
+            result.append(entry->mString);
+        }
+    }
+    if (devices & ~allDevices) {
+        if (!result.isEmpty()) {
+            result.append("|");
+        }
+        result.appendFormat("0x%X", devices & ~allDevices);
+    }
+    if (result.isEmpty()) {
+        result.append(entry->mString);
+    }
+    return result;
+}
+
+String8 inputFlagsToString(audio_input_flags_t flags)
+{
+    static const struct mapping {
+        audio_input_flags_t     mFlag;
+        const char *            mString;
+    } mappings[] = {
+        AUDIO_INPUT_FLAG_FAST,              "FAST",
+        AUDIO_INPUT_FLAG_HW_HOTWORD,        "HW_HOTWORD",
+        AUDIO_INPUT_FLAG_NONE,              "NONE",         // must be last
+    };
+    String8 result;
+    audio_input_flags_t allFlags = AUDIO_INPUT_FLAG_NONE;
+    const mapping *entry;
+    for (entry = mappings; entry->mFlag != AUDIO_INPUT_FLAG_NONE; entry++) {
+        allFlags = (audio_input_flags_t) (allFlags | entry->mFlag);
+        if (flags & entry->mFlag) {
+            if (!result.isEmpty()) {
+                result.append("|");
+            }
+            result.append(entry->mString);
+        }
+    }
+    if (flags & ~allFlags) {
+        if (!result.isEmpty()) {
+            result.append("|");
+        }
+        result.appendFormat("0x%X", flags & ~allFlags);
+    }
+    if (result.isEmpty()) {
+        result.append(entry->mString);
+    }
+    return result;
+}
+
+String8 outputFlagsToString(audio_output_flags_t flags)
 {
     static const struct mapping {
         audio_output_flags_t    mFlag;
@@ -345,7 +428,7 @@
         AUDIO_OUTPUT_FLAG_PRIMARY,          "PRIMARY",
         AUDIO_OUTPUT_FLAG_FAST,             "FAST",
         AUDIO_OUTPUT_FLAG_DEEP_BUFFER,      "DEEP_BUFFER",
-        AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD, "COMPRESS_OFFLOAAD",
+        AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD, "COMPRESS_OFFLOAD",
         AUDIO_OUTPUT_FLAG_NON_BLOCKING,     "NON_BLOCKING",
         AUDIO_OUTPUT_FLAG_HW_AV_SYNC,       "HW_AV_SYNC",
         AUDIO_OUTPUT_FLAG_NONE,             "NONE",         // must be last
@@ -374,6 +457,24 @@
     return result;
 }
 
+const char *sourceToString(audio_source_t source)
+{
+    switch (source) {
+    case AUDIO_SOURCE_DEFAULT:              return "default";
+    case AUDIO_SOURCE_MIC:                  return "mic";
+    case AUDIO_SOURCE_VOICE_UPLINK:         return "voice uplink";
+    case AUDIO_SOURCE_VOICE_DOWNLINK:       return "voice downlink";
+    case AUDIO_SOURCE_VOICE_CALL:           return "voice call";
+    case AUDIO_SOURCE_CAMCORDER:            return "camcorder";
+    case AUDIO_SOURCE_VOICE_RECOGNITION:    return "voice recognition";
+    case AUDIO_SOURCE_VOICE_COMMUNICATION:  return "voice communication";
+    case AUDIO_SOURCE_REMOTE_SUBMIX:        return "remote submix";
+    case AUDIO_SOURCE_FM_TUNER:             return "FM tuner";
+    case AUDIO_SOURCE_HOTWORD:              return "hotword";
+    default:                                return "unknown";
+    }
+}
+
 AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
         audio_devices_t outDevice, audio_devices_t inDevice, type_t type)
     :   Thread(false /*canCallJava*/),
@@ -640,6 +741,7 @@
         dprintf(fd, "thread %p may be deadlocked\n", this);
     }
 
+    dprintf(fd, "  Thread name: %s\n", mThreadName);
     dprintf(fd, "  I/O handle: %d\n", mId);
     dprintf(fd, "  TID: %d\n", getTid());
     dprintf(fd, "  Standby: %s\n", mStandby ? "yes" : "no");
@@ -663,6 +765,9 @@
     } else {
         dprintf(fd, " none\n");
     }
+    dprintf(fd, "  Output device: %#x (%s)\n", mOutDevice, devicesToString(mOutDevice).string());
+    dprintf(fd, "  Input device: %#x (%s)\n", mInDevice, devicesToString(mInDevice).string());
+    dprintf(fd, "  Audio source: %d (%s)\n", mAudioSource, sourceToString(mAudioSource));
 
     if (locked) {
         mLock.unlock();
@@ -696,19 +801,19 @@
 String16 AudioFlinger::ThreadBase::getWakeLockTag()
 {
     switch (mType) {
-        case MIXER:
-            return String16("AudioMix");
-        case DIRECT:
-            return String16("AudioDirectOut");
-        case DUPLICATING:
-            return String16("AudioDup");
-        case RECORD:
-            return String16("AudioIn");
-        case OFFLOAD:
-            return String16("AudioOffload");
-        default:
-            ALOG_ASSERT(false);
-            return String16("AudioUnknown");
+    case MIXER:
+        return String16("AudioMix");
+    case DIRECT:
+        return String16("AudioDirectOut");
+    case DUPLICATING:
+        return String16("AudioDup");
+    case RECORD:
+        return String16("AudioIn");
+    case OFFLOAD:
+        return String16("AudioOffload");
+    default:
+        ALOG_ASSERT(false);
+        return String16("AudioUnknown");
     }
 }
 
@@ -735,7 +840,7 @@
         if (status == NO_ERROR) {
             mWakeLockToken = binder;
         }
-        ALOGV("acquireWakeLock_l() %s status %d", mName, status);
+        ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status);
     }
 }
 
@@ -748,7 +853,7 @@
 void AudioFlinger::ThreadBase::releaseWakeLock_l()
 {
     if (mWakeLockToken != 0) {
-        ALOGV("releaseWakeLock_l() %s", mName);
+        ALOGV("releaseWakeLock_l() %s", mThreadName);
         if (mPowerManager != 0) {
             mPowerManager->releaseWakeLock(mWakeLockToken, 0,
                     true /* FIXME force oneway contrary to .aidl */);
@@ -769,7 +874,7 @@
         sp<IBinder> binder =
             defaultServiceManager()->checkService(String16("power"));
         if (binder == 0) {
-            ALOGW("Thread %s cannot connect to the power manager service", mName);
+            ALOGW("Thread %s cannot connect to the power manager service", mThreadName);
         } else {
             mPowerManager = interface_cast<IPowerManager>(binder);
             binder->linkToDeath(mDeathRecipient);
@@ -789,7 +894,7 @@
         status_t status;
         status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array(),
                     true /* FIXME force oneway contrary to .aidl */);
-        ALOGV("acquireWakeLock_l() %s status %d", mName, status);
+        ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status);
     }
 }
 
@@ -973,7 +1078,7 @@
     // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo).
     if (mType == DIRECT) {
         ALOGW("createEffect_l() Cannot add effect %s on Direct output type thread %s",
-                desc->name, mName);
+                desc->name, mThreadName);
         lStatus = BAD_VALUE;
         goto Exit;
     }
@@ -997,7 +1102,8 @@
         case DUPLICATING:
         case RECORD:
         default:
-            ALOGW("createEffect_l() Cannot add global effect %s on thread %s", desc->name, mName);
+            ALOGW("createEffect_l() Cannot add global effect %s on thread %s",
+                    desc->name, mThreadName);
             lStatus = BAD_VALUE;
             goto Exit;
         }
@@ -1262,8 +1368,8 @@
         // mLatchD, mLatchQ,
         mLatchDValid(false), mLatchQValid(false)
 {
-    snprintf(mName, kNameLength, "AudioOut_%X", id);
-    mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
+    snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id);
+    mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
 
     // Assumes constructor is called by AudioFlinger with it's mLock held, but
     // it would be safer to explicitly pass initial masterVolume/masterMute as
@@ -1377,6 +1483,9 @@
 void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
 {
     dprintf(fd, "\nOutput thread %p type %d (%s):\n", this, type(), threadTypeToString(type()));
+
+    dumpBase(fd, args);
+
     dprintf(fd, "  Normal frame count: %zu\n", mNormalFrameCount);
     dprintf(fd, "  Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
     dprintf(fd, "  Total writes: %d\n", mNumWrites);
@@ -1391,15 +1500,13 @@
     audio_output_flags_t flags = output != NULL ? output->flags : AUDIO_OUTPUT_FLAG_NONE;
     String8 flagsAsString = outputFlagsToString(flags);
     dprintf(fd, "  AudioStreamOut: %p flags %#x (%s)\n", output, flags, flagsAsString.string());
-
-    dumpBase(fd, args);
 }
 
 // Thread virtuals
 
 void AudioFlinger::PlaybackThread::onFirstRef()
 {
-    run(mName, ANDROID_PRIORITY_URGENT_AUDIO);
+    run(mThreadName, ANDROID_PRIORITY_URGENT_AUDIO);
 }
 
 // ThreadBase virtuals
@@ -1443,9 +1550,10 @@
               (
                 (sharedBuffer != 0)
               ) ||
-              // use case 2: callback handler and frame count is default or at least as large as HAL
+              // use case 2: frame count is default or at least as large as HAL
               (
-                (tid != -1) &&
+                // we formerly checked for a callback handler (non-0 tid),
+                // but that is no longer required for TRANSFER_OBTAIN mode
                 ((frameCount == 0) ||
                 (frameCount >= mFrameCount))
               )
@@ -5059,8 +5167,8 @@
     // mFastCaptureNBLogWriter
     , mFastTrackAvail(false)
 {
-    snprintf(mName, kNameLength, "AudioIn_%X", id);
-    mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
+    snprintf(mThreadName, kThreadNameLength, "AudioIn_%X", id);
+    mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
 
     readInputParameters_l();
 
@@ -5201,7 +5309,7 @@
 
 void AudioFlinger::RecordThread::onFirstRef()
 {
-    run(mName, PRIORITY_URGENT_AUDIO);
+    run(mThreadName, PRIORITY_URGENT_AUDIO);
 }
 
 bool AudioFlinger::RecordThread::threadLoop()
@@ -6049,15 +6157,13 @@
 {
     dprintf(fd, "\nInput thread %p:\n", this);
 
-    if (mActiveTracks.size() > 0) {
-        dprintf(fd, "  Buffer size: %zu bytes\n", mBufferSize);
-    } else {
+    dumpBase(fd, args);
+
+    if (mActiveTracks.size() == 0) {
         dprintf(fd, "  No active record clients\n");
     }
     dprintf(fd, "  Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no");
     dprintf(fd, "  Fast track available: %s\n", mFastTrackAvail ? "yes" : "no");
-
-    dumpBase(fd, args);
 }
 
 void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args __unused)
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a1ac42c..d600ea9 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -427,13 +427,13 @@
                 bool                    mStandby;     // Whether thread is currently in standby.
                 audio_devices_t         mOutDevice;   // output device
                 audio_devices_t         mInDevice;    // input device
-                audio_source_t          mAudioSource; // (see audio.h, audio_source_t)
+                audio_source_t          mAudioSource;
 
                 const audio_io_handle_t mId;
                 Vector< sp<EffectChain> > mEffectChains;
 
-                static const int        kNameLength = 16;   // prctl(PR_SET_NAME) limit
-                char                    mName[kNameLength];
+                static const int        kThreadNameLength = 16; // prctl(PR_SET_NAME) limit
+                char                    mThreadName[kThreadNameLength]; // guaranteed NUL-terminated
                 sp<IPowerManager>       mPowerManager;
                 sp<IBinder>             mWakeLockToken;
                 const sp<PMDeathRecipient> mDeathRecipient;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 8329be4..38667b9 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -444,8 +444,6 @@
         //       this means we are potentially denying other more important fast tracks from
         //       being created.  It would be better to allocate the index dynamically.
         mFastIndex = i;
-        // Read the initial underruns because this field is never cleared by the fast mixer
-        mObservedUnderruns = thread->getFastTrackUnderruns(i);
         thread->mFastTrackAvailMask &= ~(1 << i);
     }
 }
@@ -694,6 +692,12 @@
         }
 
         PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+        if (isFastTrack()) {
+            // refresh fast track underruns on start because that field is never cleared
+            // by the fast mixer; furthermore, the same track can be recycled, i.e. start
+            // after stop.
+            mObservedUnderruns = playbackThread->getFastTrackUnderruns(mFastIndex);
+        }
         status = playbackThread->addTrack_l(this);
         if (status == INVALID_OPERATION || status == PERMISSION_DENIED) {
             triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
diff --git a/services/audiopolicy/managerdefault/ConfigParsingUtils.h b/services/audiopolicy/managerdefault/ConfigParsingUtils.h
index 7969661..b2d9763 100644
--- a/services/audiopolicy/managerdefault/ConfigParsingUtils.h
+++ b/services/audiopolicy/managerdefault/ConfigParsingUtils.h
@@ -26,7 +26,9 @@
 };
 
 #define STRING_TO_ENUM(string) { #string, string }
+#ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
 
 const StringToEnum sDeviceNameToEnumTable[] = {
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE),
diff --git a/services/audiopolicy/managerdefault/Devices.h b/services/audiopolicy/managerdefault/Devices.h
index 65e1416..af2fbda 100644
--- a/services/audiopolicy/managerdefault/Devices.h
+++ b/services/audiopolicy/managerdefault/Devices.h
@@ -41,7 +41,6 @@
 
     audio_devices_t mDeviceType;
     String8 mAddress;
-    audio_port_handle_t mId;
 
     static String8  emptyNameStr;
 };
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 8e894cd..7a79750 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -388,7 +388,8 @@
     if (mAnw == NULL) {
         return NO_MEMORY;
     }
-    res = device->createStream(mAnw, width, height, format, &mStreamId);
+    res = device->createStream(mAnw, width, height, format,
+            HAL_DATASPACE_UNKNOWN, &mStreamId);
     if (res) {
         return res;
     }
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index eadaa00..fd4e714 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -155,7 +155,7 @@
                 callbackFormat, params.previewFormat);
         res = device->createStream(mCallbackWindow,
                 params.previewWidth, params.previewHeight,
-                callbackFormat, &mCallbackStreamId);
+                callbackFormat, HAL_DATASPACE_JFIF, &mCallbackStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index 2772267..5b387f9 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -145,7 +145,8 @@
         // Create stream for HAL production
         res = device->createStream(mCaptureWindow,
                 params.pictureWidth, params.pictureHeight,
-                HAL_PIXEL_FORMAT_BLOB, &mCaptureStreamId);
+                HAL_PIXEL_FORMAT_BLOB, HAL_DATASPACE_JFIF,
+                &mCaptureStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for capture: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index 470624b..a5a2fec 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -27,6 +27,7 @@
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
+#include <gui/BufferItem.h>
 #include <gui/Surface.h>
 #include <media/hardware/MetadataBufferType.h>
 
@@ -181,7 +182,8 @@
     if (mPreviewStreamId == NO_STREAM) {
         res = device->createStream(mPreviewWindow,
                 params.previewWidth, params.previewHeight,
-                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mPreviewStreamId);
+                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN,
+                &mPreviewStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
                     __FUNCTION__, mId, strerror(-res), res);
@@ -420,9 +422,12 @@
 
     if (mRecordingStreamId == NO_STREAM) {
         mRecordingFrameCount = 0;
+        // Selecting BT.709 colorspace by default
+        // TODO: Wire this in from encoder side
         res = device->createStream(mRecordingWindow,
                 params.videoWidth, params.videoHeight,
-                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mRecordingStreamId);
+                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE,
+                HAL_DATASPACE_BT709, &mRecordingStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for recording: "
                     "%s (%d)", __FUNCTION__, mId,
@@ -675,7 +680,7 @@
     sp<Camera2Client> client = mClient.promote();
     if (client == 0) {
         // Discard frames during shutdown
-        BufferItemConsumer::BufferItem imgBuffer;
+        BufferItem imgBuffer;
         res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0);
         if (res != OK) {
             if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) {
@@ -693,7 +698,7 @@
             with Camera2Client code calling into StreamingProcessor */
         SharedParameters::Lock l(client->getParameters());
         Mutex::Autolock m(mMutex);
-        BufferItemConsumer::BufferItem imgBuffer;
+        BufferItem imgBuffer;
         res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0);
         if (res != OK) {
             if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) {
@@ -819,8 +824,7 @@
 
     size_t itemIndex;
     for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) {
-        const BufferItemConsumer::BufferItem item =
-                mRecordingBuffers[itemIndex];
+        const BufferItem item = mRecordingBuffers[itemIndex];
         if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT &&
                 item.mGraphicBuffer->handle == imgHandle) {
             break;
@@ -864,8 +868,7 @@
 
     size_t releasedCount = 0;
     for (size_t itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) {
-        const BufferItemConsumer::BufferItem item =
-                mRecordingBuffers[itemIndex];
+        const BufferItem item = mRecordingBuffers[itemIndex];
         if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT) {
             res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]);
             if (res != OK) {
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
index 1d679a4..2474062 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
@@ -124,7 +124,7 @@
 
     static const size_t kDefaultRecordingHeapCount = 8;
     size_t mRecordingHeapCount;
-    Vector<BufferItemConsumer::BufferItem> mRecordingBuffers;
+    Vector<BufferItem> mRecordingBuffers;
     size_t mRecordingHeapHead, mRecordingHeapFree;
 
     virtual bool threadLoop();
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 8b7e4b4..68aca2d 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -186,7 +186,7 @@
                 (int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
         res = device->createStream(mZslWindow,
                 params.fastInfo.arrayWidth, params.fastInfo.arrayHeight,
-                streamType, &mZslStreamId);
+                streamType, HAL_DATASPACE_UNKNOWN, &mZslStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for ZSL: "
                     "%s (%d)", __FUNCTION__, mId,
@@ -440,7 +440,7 @@
         zslConsumer = mZslConsumer;
     }
     ALOGVV("Trying to get next buffer");
-    BufferItemConsumer::BufferItem item;
+    BufferItem item;
     res = zslConsumer->acquireBuffer(&item, 0);
     if (res != OK) {
         if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) {
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.h b/services/camera/libcameraservice/api1/client2/ZslProcessor.h
index 2099c38..5f50d7b 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.h
@@ -22,6 +22,7 @@
 #include <utils/Vector.h>
 #include <utils/Mutex.h>
 #include <utils/Condition.h>
+#include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
 #include <camera/CameraMetadata.h>
 #include <camera/CaptureResult.h>
@@ -103,7 +104,7 @@
     sp<ANativeWindow>      mZslWindow;
 
     struct ZslPair {
-        BufferItemConsumer::BufferItem buffer;
+        BufferItem buffer;
         CameraMetadata frame;
     };
 
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
index fc9f70c..2960478 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
@@ -22,6 +22,7 @@
 #include <utils/Vector.h>
 #include <utils/Mutex.h>
 #include <utils/Condition.h>
+#include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
 #include <camera/CameraMetadata.h>
 
@@ -104,7 +105,7 @@
     sp<camera3::Camera3ZslStream> mZslStream;
 
     struct ZslPair {
-        BufferItemConsumer::BufferItem buffer;
+        BufferItem buffer;
         CameraMetadata frame;
     };
 
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index acc092c..dde1779 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -314,11 +314,10 @@
     return res;
 }
 
-status_t CameraDeviceClient::createStream(int width, int height, int format,
+status_t CameraDeviceClient::createStream(
                       const sp<IGraphicBufferProducer>& bufferProducer)
 {
     ATRACE_CALL();
-    ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
 
     status_t res;
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
@@ -370,7 +369,8 @@
     sp<IBinder> binder = IInterface::asBinder(bufferProducer);
     sp<ANativeWindow> anw = new Surface(bufferProducer, useAsync);
 
-    // TODO: remove w,h,f since we are ignoring them
+    int width, height, format;
+    android_dataspace dataSpace;
 
     if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
         ALOGE("%s: Camera %d: Failed to query Surface width", __FUNCTION__,
@@ -387,6 +387,12 @@
               mCameraId);
         return res;
     }
+    if ((res = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE,
+                            reinterpret_cast<int*>(&dataSpace))) != OK) {
+        ALOGE("%s: Camera %d: Failed to query Surface dataSpace", __FUNCTION__,
+              mCameraId);
+        return res;
+    }
 
     // FIXME: remove this override since the default format should be
     //       IMPLEMENTATION_DEFINED. b/9487482
@@ -399,14 +405,15 @@
 
     // Round dimensions to the nearest dimensions available for this format
     if (flexibleConsumer && !CameraDeviceClient::roundBufferDimensionNearest(width, height,
-            format, mDevice->info(), /*out*/&width, /*out*/&height)) {
+            format, dataSpace, mDevice->info(), /*out*/&width, /*out*/&height)) {
         ALOGE("%s: No stream configurations with the format %#x defined, failed to create stream.",
                 __FUNCTION__, format);
         return BAD_VALUE;
     }
 
     int streamId = -1;
-    res = mDevice->createStream(anw, width, height, format, &streamId);
+    res = mDevice->createStream(anw, width, height, format, dataSpace,
+            &streamId);
 
     if (res == OK) {
         mStreamMap.add(binder, streamId);
@@ -441,10 +448,12 @@
 
 
 bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t height,
-        int32_t format, const CameraMetadata& info,
+        int32_t format, android_dataspace dataSpace, const CameraMetadata& info,
         /*out*/int32_t* outWidth, /*out*/int32_t* outHeight) {
 
     camera_metadata_ro_entry streamConfigs =
+            (dataSpace == HAL_DATASPACE_DEPTH) ?
+            info.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS) :
             info.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
 
     int32_t bestWidth = -1;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index e687175..c89c269 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -84,9 +84,6 @@
     virtual status_t      deleteStream(int streamId);
 
     virtual status_t      createStream(
-            int width,
-            int height,
-            int format,
             const sp<IGraphicBufferProducer>& bufferProducer);
 
     // Create a request object from a template.
@@ -161,7 +158,8 @@
     // a width <= ROUNDING_WIDTH_CAP
     static const int32_t ROUNDING_WIDTH_CAP = 1080;
     static bool roundBufferDimensionNearest(int32_t width, int32_t height, int32_t format,
-            const CameraMetadata& info, /*out*/int32_t* outWidth, /*out*/int32_t* outHeight);
+            android_dataspace dataSpace, const CameraMetadata& info,
+            /*out*/int32_t* outWidth, /*out*/int32_t* outHeight);
 
     // IGraphicsBufferProducer binder -> Stream ID
     KeyedVector<sp<IBinder>, int> mStreamMap;
diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
index 30a89c2..ba93554 100644
--- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
@@ -281,7 +281,7 @@
     }
 
     return mDevice->createStream(window, width, height, format,
-                                 streamId);
+                                 HAL_DATASPACE_UNKNOWN, streamId);
 }
 
 // Create a request object from a template.
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 06615f6..8764504 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -100,17 +100,14 @@
             nsecs_t timeout) = 0;
 
     /**
-     * Create an output stream of the requested size and format.
+     * Create an output stream of the requested size, format, and dataspace
      *
-     * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device selects
-     * an appropriate format; it can be queried with getStreamInfo.
-     *
-     * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must be
-     * equal to the size in bytes of the buffers to allocate for the stream. For
-     * other formats, the size parameter is ignored.
+     * For HAL_PIXEL_FORMAT_BLOB formats, the width and height should be the
+     * logical dimensions of the buffer, not the number of bytes.
      */
     virtual status_t createStream(sp<ANativeWindow> consumer,
-            uint32_t width, uint32_t height, int format, int *id) = 0;
+            uint32_t width, uint32_t height, int format,
+            android_dataspace dataSpace, int *id) = 0;
 
     /**
      * Create an input reprocess stream that uses buffers from an existing
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index 43c8307..ee862a2 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -241,7 +241,8 @@
 }
 
 status_t Camera2Device::createStream(sp<ANativeWindow> consumer,
-        uint32_t width, uint32_t height, int format, int *id) {
+        uint32_t width, uint32_t height, int format,
+        android_dataspace /*dataSpace*/, int *id) {
     ATRACE_CALL();
     status_t res;
     ALOGV("%s: E", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index 1cc5482..e4c2856 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -57,7 +57,8 @@
     virtual status_t clearStreamingRequest(int64_t *lastFrameNumber = NULL);
     virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout);
     virtual status_t createStream(sp<ANativeWindow> consumer,
-            uint32_t width, uint32_t height, int format, int *id);
+            uint32_t width, uint32_t height, int format,
+            android_dataspace dataSpace, int *id);
     virtual status_t createReprocessStreamFromStream(int outputId, int *id);
     virtual status_t getStreamInfo(int id,
             uint32_t *width, uint32_t *height, uint32_t *format);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index bca9bfd..529d249 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -801,12 +801,13 @@
 }
 
 status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
-        uint32_t width, uint32_t height, int format, int *id) {
+        uint32_t width, uint32_t height, int format, android_dataspace dataSpace,
+        int *id) {
     ATRACE_CALL();
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
-    ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d",
-            mId, mNextStreamId, width, height, format);
+    ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d",
+            mId, mNextStreamId, width, height, format, dataSpace);
 
     status_t res;
     bool wasActive = false;
@@ -846,10 +847,10 @@
         }
 
         newStream = new Camera3OutputStream(mNextStreamId, consumer,
-                width, height, jpegBufferSize, format);
+                width, height, jpegBufferSize, format, dataSpace);
     } else {
         newStream = new Camera3OutputStream(mNextStreamId, consumer,
-                width, height, format);
+                width, height, format, dataSpace);
     }
     newStream->setStatusTracker(mStatusTracker);
 
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index de10cfe..e2ad1fa 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -95,7 +95,8 @@
     // If adding streams while actively capturing, will pause device before adding
     // stream, reconfiguring device, and unpausing.
     virtual status_t createStream(sp<ANativeWindow> consumer,
-            uint32_t width, uint32_t height, int format, int *id);
+            uint32_t width, uint32_t height, int format,
+            android_dataspace dataSpace, int *id);
     virtual status_t createInputStream(
             uint32_t width, uint32_t height, int format,
             int *id);
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 6656b09..6201484 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -28,7 +28,7 @@
 
 Camera3DummyStream::Camera3DummyStream(int id) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT,
-                /*maxSize*/0, DUMMY_FORMAT) {
+                /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE) {
 
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 3e42623..7f52d84 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -75,6 +75,7 @@
     static const int DUMMY_WIDTH = 320;
     static const int DUMMY_HEIGHT = 240;
     static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+    static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN;
     static const uint32_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;
 
     /**
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index cc66459..ff0acbb 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -30,9 +30,10 @@
 namespace camera3 {
 
 Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type,
-        uint32_t width, uint32_t height, size_t maxSize, int format) :
+        uint32_t width, uint32_t height, size_t maxSize, int format,
+        android_dataspace dataSpace) :
         Camera3Stream(id, type,
-                width, height, maxSize, format),
+                width, height, maxSize, format, dataSpace),
         mTotalBufferCount(0),
         mHandoutTotalBufferCount(0),
         mHandoutOutputBufferCount(0),
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index a35c290..83d4350 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -33,7 +33,8 @@
         public Camera3Stream {
   protected:
     Camera3IOStreamBase(int id, camera3_stream_type_t type,
-            uint32_t width, uint32_t height, size_t maxSize, int format);
+            uint32_t width, uint32_t height, size_t maxSize, int format,
+            android_dataspace dataSpace);
 
   public:
 
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 319be1d..87907bf 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -18,6 +18,7 @@
 #define ATRACE_TAG ATRACE_TAG_CAMERA
 //#define LOG_NDEBUG 0
 
+#include <gui/BufferItem.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
 #include "Camera3InputStream.h"
@@ -29,7 +30,7 @@
 Camera3InputStream::Camera3InputStream(int id,
         uint32_t width, uint32_t height, int format) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height,
-                            /*maxSize*/0, format) {
+                            /*maxSize*/0, format, HAL_DATASPACE_UNKNOWN) {
 
     if (format == HAL_PIXEL_FORMAT_BLOB) {
         ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index ae49467..fd17f4f 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -48,8 +48,6 @@
 
   private:
 
-    typedef BufferItemConsumer::BufferItem BufferItem;
-
     sp<BufferItemConsumer> mConsumer;
     Vector<BufferItem> mBuffersInFlight;
 
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 77ad503..103d90b 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -33,9 +33,10 @@
 
 Camera3OutputStream::Camera3OutputStream(int id,
         sp<ANativeWindow> consumer,
-        uint32_t width, uint32_t height, int format) :
+        uint32_t width, uint32_t height, int format,
+        android_dataspace dataSpace) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
-                            /*maxSize*/0, format),
+                            /*maxSize*/0, format, dataSpace),
         mConsumer(consumer),
         mTransform(0),
         mTraceFirstBuffer(true) {
@@ -48,9 +49,10 @@
 
 Camera3OutputStream::Camera3OutputStream(int id,
         sp<ANativeWindow> consumer,
-        uint32_t width, uint32_t height, size_t maxSize, int format) :
+        uint32_t width, uint32_t height, size_t maxSize, int format,
+        android_dataspace dataSpace) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize,
-                            format),
+                            format, dataSpace),
         mConsumer(consumer),
         mTransform(0),
         mTraceFirstBuffer(true) {
@@ -69,10 +71,11 @@
 
 Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
                                          uint32_t width, uint32_t height,
-                                         int format) :
+                                         int format,
+                                         android_dataspace dataSpace) :
         Camera3IOStreamBase(id, type, width, height,
                             /*maxSize*/0,
-                            format),
+                            format, dataSpace),
         mTransform(0) {
 
     // Subclasses expected to initialize mConsumer themselves
@@ -323,6 +326,14 @@
         return res;
     }
 
+    res = native_window_set_buffers_data_space(mConsumer.get(),
+            camera3_stream::data_space);
+    if (res != OK) {
+        ALOGE("%s: Unable to configure stream dataspace %#x for stream %d",
+                __FUNCTION__, camera3_stream::data_space, mId);
+        return res;
+    }
+
     int maxConsumerBuffers;
     res = mConsumer->query(mConsumer.get(),
             NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index be278c5..f016d60 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -39,14 +39,16 @@
      * Set up a stream for formats that have 2 dimensions, such as RAW and YUV.
      */
     Camera3OutputStream(int id, sp<ANativeWindow> consumer,
-            uint32_t width, uint32_t height, int format);
+            uint32_t width, uint32_t height, int format,
+            android_dataspace dataSpace);
 
     /**
      * Set up a stream for formats that have a variable buffer size for the same
      * dimensions, such as compressed JPEG.
      */
     Camera3OutputStream(int id, sp<ANativeWindow> consumer,
-            uint32_t width, uint32_t height, size_t maxSize, int format);
+            uint32_t width, uint32_t height, size_t maxSize, int format,
+            android_dataspace dataSpace);
 
     virtual ~Camera3OutputStream();
 
@@ -64,7 +66,8 @@
 
   protected:
     Camera3OutputStream(int id, camera3_stream_type_t type,
-            uint32_t width, uint32_t height, int format);
+            uint32_t width, uint32_t height, int format,
+            android_dataspace dataSpace);
 
     /**
      * Note that we release the lock briefly in this function
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 3c0e908..f829741 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -46,7 +46,8 @@
 
 Camera3Stream::Camera3Stream(int id,
         camera3_stream_type type,
-        uint32_t width, uint32_t height, size_t maxSize, int format) :
+        uint32_t width, uint32_t height, size_t maxSize, int format,
+        android_dataspace dataSpace) :
     camera3_stream(),
     mId(id),
     mName(String8::format("Camera3Stream[%d]", id)),
@@ -58,6 +59,7 @@
     camera3_stream::width = width;
     camera3_stream::height = height;
     camera3_stream::format = format;
+    camera3_stream::data_space = dataSpace;
     camera3_stream::usage = 0;
     camera3_stream::max_buffers = 0;
     camera3_stream::priv = NULL;
@@ -84,6 +86,10 @@
     return camera3_stream::format;
 }
 
+android_dataspace Camera3Stream::getDataSpace() const {
+    return camera3_stream::data_space;
+}
+
 camera3_stream* Camera3Stream::startConfiguration() {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index d0e1337..72f3ee9 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -119,9 +119,10 @@
     /**
      * Get the stream's dimensions and format
      */
-    uint32_t         getWidth() const;
-    uint32_t         getHeight() const;
-    int              getFormat() const;
+    uint32_t          getWidth() const;
+    uint32_t          getHeight() const;
+    int               getFormat() const;
+    android_dataspace getDataSpace() const;
 
     /**
      * Start the stream configuration process. Returns a handle to the stream's
@@ -264,7 +265,8 @@
     mutable Mutex mLock;
 
     Camera3Stream(int id, camera3_stream_type type,
-            uint32_t width, uint32_t height, size_t maxSize, int format);
+            uint32_t width, uint32_t height, size_t maxSize, int format,
+            android_dataspace dataSpace);
 
     /**
      * Interface to be implemented by derived classes
diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
index 81330ea..5bf7a4c 100644
--- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
@@ -114,7 +114,8 @@
         int bufferCount) :
         Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL,
                             width, height,
-                            HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
+                            HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+                            HAL_DATASPACE_UNKNOWN),
         mDepth(bufferCount) {
 
     sp<IGraphicBufferProducer> producer;
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
index d0f29de..8cd6800 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
@@ -268,7 +268,7 @@
     return OK;
 }
 
-void RingBufferConsumer::onFrameAvailable(const android::BufferItem& item) {
+void RingBufferConsumer::onFrameAvailable(const BufferItem& item) {
     status_t err;
 
     {
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h
index 90fd734..83e7298 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.h
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_GUI_RINGBUFFERCONSUMER_H
 #define ANDROID_GUI_RINGBUFFERCONSUMER_H
 
+#include <gui/BufferItem.h>
 #include <gui/ConsumerBase.h>
 
 #include <ui/GraphicBuffer.h>
@@ -54,8 +55,6 @@
   public:
     typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
 
-    typedef BufferQueue::BufferItem BufferItem;
-
     enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT };
     enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE };
 
@@ -165,7 +164,7 @@
   private:
 
     // Override ConsumerBase::onFrameAvailable
-    virtual void onFrameAvailable(const android::BufferItem& item);
+    virtual void onFrameAvailable(const BufferItem& item);
 
     void pinBufferLocked(const BufferItem& item);
     void unpinBuffer(const BufferItem& item);
diff --git a/services/radio/Android.mk b/services/radio/Android.mk
new file mode 100644
index 0000000..5e89b22
--- /dev/null
+++ b/services/radio/Android.mk
@@ -0,0 +1,35 @@
+# Copyright 2014 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+
+LOCAL_SRC_FILES:=               \
+    RadioService.cpp
+
+LOCAL_SHARED_LIBRARIES:= \
+    libui \
+    liblog \
+    libutils \
+    libbinder \
+    libcutils \
+    libhardware \
+    libradio \
+    libradio_metadata
+
+LOCAL_MODULE:= libradioservice
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/radio/RadioRegions.h b/services/radio/RadioRegions.h
new file mode 100644
index 0000000..3335b8a
--- /dev/null
+++ b/services/radio/RadioRegions.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_HARDWARE_RADIO_REGIONS_H
+#define ANDROID_HARDWARE_RADIO_REGIONS_H
+
+namespace android {
+
+#define RADIO_BAND_LOWER_FM_ITU1    87500
+#define RADIO_BAND_UPPER_FM_ITU1    108000
+#define RADIO_BAND_SPACING_FM_ITU1  100
+
+#define RADIO_BAND_LOWER_FM_ITU2    87900
+#define RADIO_BAND_UPPER_FM_ITU2    107900
+#define RADIO_BAND_SPACING_FM_ITU2  200
+
+#define RADIO_BAND_LOWER_FM_JAPAN    76000
+#define RADIO_BAND_UPPER_FM_JAPAN    90000
+#define RADIO_BAND_SPACING_FM_JAPAN  100
+
+#define RADIO_BAND_LOWER_FM_OIRT    65800
+#define RADIO_BAND_UPPER_FM_OIRT    74000
+#define RADIO_BAND_SPACING_FM_OIRT  10
+
+#define RADIO_BAND_LOWER_LW         153
+#define RADIO_BAND_UPPER_LW         279
+#define RADIO_BAND_SPACING_LW       9
+
+#define RADIO_BAND_LOWER_MW_IUT1    531
+#define RADIO_BAND_UPPER_MW_ITU1    1611
+#define RADIO_BAND_SPACING_MW_ITU1  9
+
+#define RADIO_BAND_LOWER_MW_IUT2    540
+#define RADIO_BAND_UPPER_MW_ITU2    1610
+#define RADIO_BAND_SPACING_MW_ITU2  10
+
+#define RADIO_BAND_LOWER_SW         2300
+#define RADIO_BAND_UPPER_SW         26100
+#define RADIO_BAND_SPACING_SW       5
+
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+const radio_band_config_t sKnownRegionConfigs[] = {
+    {   // FM ITU 1
+        RADIO_REGION_ITU_1,
+        {
+        RADIO_BAND_FM,
+            false,
+            RADIO_BAND_LOWER_FM_ITU1,
+            RADIO_BAND_UPPER_FM_ITU1,
+            1,
+            {RADIO_BAND_SPACING_FM_ITU1},
+            {
+            RADIO_DEEMPHASIS_50,
+            true,
+            RADIO_RDS_WORLD,
+            true,
+            true,
+            }
+        }
+    },
+    {   // FM Americas
+        RADIO_REGION_ITU_2,
+        {
+        RADIO_BAND_FM,
+            false,
+            RADIO_BAND_LOWER_FM_ITU2,
+            RADIO_BAND_UPPER_FM_ITU2,
+            1,
+            {RADIO_BAND_SPACING_FM_ITU2},
+            {
+            RADIO_DEEMPHASIS_75,
+            true,
+            RADIO_RDS_US,
+            true,
+            true,
+            }
+        }
+    },
+    {   // FM Japan
+        RADIO_REGION_JAPAN,
+        {
+        RADIO_BAND_FM,
+            false,
+            RADIO_BAND_LOWER_FM_JAPAN,
+            RADIO_BAND_UPPER_FM_JAPAN,
+            1,
+            {RADIO_BAND_SPACING_FM_JAPAN},
+            {
+            RADIO_DEEMPHASIS_50,
+            true,
+            RADIO_RDS_WORLD,
+            true,
+            true,
+            }
+        }
+    },
+    {   // FM Korea
+        RADIO_REGION_KOREA,
+        {
+        RADIO_BAND_FM,
+            false,
+            RADIO_BAND_LOWER_FM_ITU1,
+            RADIO_BAND_UPPER_FM_ITU1,
+            1,
+            {RADIO_BAND_SPACING_FM_ITU1},
+            {
+            RADIO_DEEMPHASIS_75,
+            true,
+            RADIO_RDS_WORLD,
+            true,
+            true,
+            }
+        }
+    },
+    {   // FM OIRT
+        RADIO_REGION_OIRT,
+        {
+        RADIO_BAND_FM,
+            false,
+            RADIO_BAND_LOWER_FM_OIRT,
+            RADIO_BAND_UPPER_FM_OIRT,
+            1,
+            {RADIO_BAND_SPACING_FM_OIRT},
+            {
+            RADIO_DEEMPHASIS_50,
+            true,
+            RADIO_RDS_WORLD,
+            true,
+            true,
+            }
+        }
+    },
+    {   // FM US HD radio
+        RADIO_REGION_ITU_2,
+        {
+            RADIO_BAND_FM_HD,
+            false,
+            RADIO_BAND_LOWER_FM_ITU2,
+            RADIO_BAND_UPPER_FM_ITU2,
+            1,
+            {RADIO_BAND_SPACING_FM_ITU2},
+            {
+            RADIO_DEEMPHASIS_75,
+            true,
+            RADIO_RDS_US,
+            true,
+            true,
+            }
+        }
+    },
+    {   // AM LW
+        RADIO_REGION_ITU_1,
+        {
+            RADIO_BAND_AM,
+            false,
+            RADIO_BAND_LOWER_LW,
+            RADIO_BAND_UPPER_LW,
+            1,
+            {RADIO_BAND_SPACING_LW},
+            {
+            }
+        }
+    },
+    {   // AM SW
+        RADIO_REGION_ITU_1,
+        {
+            RADIO_BAND_AM,
+            false,
+            RADIO_BAND_LOWER_SW,
+            RADIO_BAND_UPPER_SW,
+            1,
+            {RADIO_BAND_SPACING_SW},
+            {
+            }
+        }
+    },
+    {   // AM MW ITU1
+        RADIO_REGION_ITU_1,
+        {
+            RADIO_BAND_AM,
+            false,
+            RADIO_BAND_LOWER_MW_IUT1,
+            RADIO_BAND_UPPER_MW_ITU1,
+            1,
+            {RADIO_BAND_SPACING_MW_ITU1},
+            {
+            }
+        }
+    },
+    {   // AM MW ITU2
+        RADIO_REGION_ITU_2,
+        {
+            RADIO_BAND_AM,
+            false,
+            RADIO_BAND_LOWER_MW_IUT2,
+            RADIO_BAND_UPPER_MW_ITU2,
+            1,
+            {RADIO_BAND_SPACING_MW_ITU2},
+            {
+            }
+        }
+    }
+};
+
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_REGIONS_H
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
new file mode 100644
index 0000000..152619b
--- /dev/null
+++ b/services/radio/RadioService.cpp
@@ -0,0 +1,845 @@
+/*
+ * Copyright (C) 2015 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_TAG "RadioService"
+//#define LOG_NDEBUG 0
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#include <system/radio.h>
+#include <system/radio_metadata.h>
+#include <cutils/atomic.h>
+#include <cutils/properties.h>
+#include <hardware/hardware.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <hardware/radio.h>
+#include "RadioService.h"
+#include "RadioRegions.h"
+
+namespace android {
+
+
+RadioService::RadioService()
+    : BnRadioService(), mNextUniqueId(1)
+{
+    ALOGI("%s", __FUNCTION__);
+}
+
+void RadioService::onFirstRef()
+{
+    const hw_module_t *mod;
+    int rc;
+    struct radio_hw_device *dev;
+
+    ALOGI("%s", __FUNCTION__);
+
+    rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod);
+    if (rc != 0) {
+        ALOGE("couldn't load radio module %s.%s (%s)",
+              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
+        return;
+    }
+    rc = radio_hw_device_open(mod, &dev);
+    if (rc != 0) {
+        ALOGE("couldn't open radio hw device in %s.%s (%s)",
+              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
+        return;
+    }
+    if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
+        ALOGE("wrong radio hw device version %04x", dev->common.version);
+        return;
+    }
+
+    struct radio_hal_properties halProperties;
+    rc = dev->get_properties(dev, &halProperties);
+    if (rc != 0) {
+        ALOGE("could not read implementation properties");
+        return;
+    }
+
+    radio_properties_t properties;
+    properties.handle =
+            (radio_handle_t)android_atomic_inc(&mNextUniqueId);
+
+    ALOGI("loaded default module %s, handle %d", properties.product, properties.handle);
+
+    convertProperties(&properties, &halProperties);
+    sp<Module> module = new Module(this, dev, properties);
+    mModules.add(properties.handle, module);
+}
+
+RadioService::~RadioService()
+{
+    for (size_t i = 0; i < mModules.size(); i++) {
+        radio_hw_device_close(mModules.valueAt(i)->hwDevice());
+    }
+}
+
+status_t RadioService::listModules(struct radio_properties *properties,
+                             uint32_t *numModules)
+{
+    ALOGV("listModules");
+
+    AutoMutex lock(mServiceLock);
+    if (numModules == NULL || (*numModules != 0 && properties == NULL)) {
+        return BAD_VALUE;
+    }
+    size_t maxModules = *numModules;
+    *numModules = mModules.size();
+    for (size_t i = 0; i < mModules.size() && i < maxModules; i++) {
+        properties[i] = mModules.valueAt(i)->properties();
+    }
+    return NO_ERROR;
+}
+
+status_t RadioService::attach(radio_handle_t handle,
+                        const sp<IRadioClient>& client,
+                        const struct radio_band_config *config,
+                        bool withAudio,
+                        sp<IRadio>& radio)
+{
+    ALOGV("%s %d config %p withAudio %d", __FUNCTION__, handle, config, withAudio);
+
+    AutoMutex lock(mServiceLock);
+    radio.clear();
+    if (client == 0) {
+        return BAD_VALUE;
+    }
+    ssize_t index = mModules.indexOfKey(handle);
+    if (index < 0) {
+        return BAD_VALUE;
+    }
+    sp<Module> module = mModules.valueAt(index);
+
+    if (config == NULL) {
+        config = module->getDefaultConfig();
+        if (config == NULL) {
+            return INVALID_OPERATION;
+        }
+    }
+    ALOGV("%s region %d type %d", __FUNCTION__, config->region, config->band.type);
+
+    radio = module->addClient(client, config, withAudio);
+
+    if (radio == 0) {
+        NO_INIT;
+    }
+    return NO_ERROR;
+}
+
+
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 60000;
+
+static bool tryLock(Mutex& mutex)
+{
+    bool locked = false;
+    for (int i = 0; i < kDumpLockRetries; ++i) {
+        if (mutex.tryLock() == NO_ERROR) {
+            locked = true;
+            break;
+        }
+        usleep(kDumpLockSleep);
+    }
+    return locked;
+}
+
+status_t RadioService::dump(int fd, const Vector<String16>& args __unused) {
+    String8 result;
+    if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+        result.appendFormat("Permission Denial: can't dump RadioService");
+        write(fd, result.string(), result.size());
+    } else {
+        bool locked = tryLock(mServiceLock);
+        // failed to lock - RadioService is probably deadlocked
+        if (!locked) {
+            result.append("RadioService may be deadlocked\n");
+            write(fd, result.string(), result.size());
+        }
+
+        if (locked) mServiceLock.unlock();
+    }
+    return NO_ERROR;
+}
+
+status_t RadioService::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+    return BnRadioService::onTransact(code, data, reply, flags);
+}
+
+
+// static
+void RadioService::callback(radio_hal_event_t *halEvent, void *cookie)
+{
+    CallbackThread *callbackThread = (CallbackThread *)cookie;
+    if (callbackThread == NULL) {
+        return;
+    }
+    callbackThread->sendEvent(halEvent);
+}
+
+/* static */
+void RadioService::convertProperties(radio_properties_t *properties,
+                                     const radio_hal_properties_t *halProperties)
+{
+    memset(properties, 0, sizeof(struct radio_properties));
+    properties->class_id = halProperties->class_id;
+    strlcpy(properties->implementor, halProperties->implementor,
+            RADIO_STRING_LEN_MAX);
+    strlcpy(properties->product, halProperties->product,
+            RADIO_STRING_LEN_MAX);
+    strlcpy(properties->version, halProperties->version,
+            RADIO_STRING_LEN_MAX);
+    strlcpy(properties->serial, halProperties->serial,
+            RADIO_STRING_LEN_MAX);
+    properties->num_tuners = halProperties->num_tuners;
+    properties->num_audio_sources = halProperties->num_audio_sources;
+    properties->supports_capture = halProperties->supports_capture;
+
+    for (size_t i = 0; i < ARRAY_SIZE(sKnownRegionConfigs); i++) {
+        const radio_hal_band_config_t *band = &sKnownRegionConfigs[i].band;
+        size_t j;
+        for (j = 0; j < halProperties->num_bands; j++) {
+            const radio_hal_band_config_t *halBand = &halProperties->bands[j];
+            size_t k;
+            if (band->type != halBand->type) continue;
+            if (band->lower_limit < halBand->lower_limit) continue;
+            if (band->upper_limit > halBand->upper_limit) continue;
+            for (k = 0; k < halBand->num_spacings; k++) {
+                if (band->spacings[0] == halBand->spacings[k]) break;
+            }
+            if (k == halBand->num_spacings) continue;
+            if (band->type == RADIO_BAND_AM) break;
+            if ((band->fm.deemphasis & halBand->fm.deemphasis) == 0) continue;
+            if (halBand->fm.rds == 0) break;
+            if ((band->fm.rds & halBand->fm.rds) != 0) break;
+        }
+        if (j == halProperties->num_bands) continue;
+
+        ALOGI("convertProperties() Adding band type %d region %d",
+              sKnownRegionConfigs[i].band.type , sKnownRegionConfigs[i].region);
+
+        memcpy(&properties->bands[properties->num_bands++],
+               &sKnownRegionConfigs[i],
+               sizeof(radio_band_config_t));
+    }
+}
+
+#undef LOG_TAG
+#define LOG_TAG "RadioService::CallbackThread"
+
+RadioService::CallbackThread::CallbackThread(const wp<ModuleClient>& moduleClient)
+    : mModuleClient(moduleClient), mMemoryDealer(new MemoryDealer(1024 * 1024, "RadioService"))
+{
+}
+
+RadioService::CallbackThread::~CallbackThread()
+{
+    mEventQueue.clear();
+}
+
+void RadioService::CallbackThread::onFirstRef()
+{
+    run("RadioService cbk", ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+bool RadioService::CallbackThread::threadLoop()
+{
+    while (!exitPending()) {
+        sp<IMemory> eventMemory;
+        sp<ModuleClient> moduleClient;
+        {
+            Mutex::Autolock _l(mCallbackLock);
+            while (mEventQueue.isEmpty() && !exitPending()) {
+                ALOGV("CallbackThread::threadLoop() sleep");
+                mCallbackCond.wait(mCallbackLock);
+                ALOGV("CallbackThread::threadLoop() wake up");
+            }
+            if (exitPending()) {
+                break;
+            }
+            eventMemory = mEventQueue[0];
+            mEventQueue.removeAt(0);
+            moduleClient = mModuleClient.promote();
+        }
+        if (moduleClient != 0) {
+            moduleClient->onCallbackEvent(eventMemory);
+            eventMemory.clear();
+        }
+    }
+    return false;
+}
+
+void RadioService::CallbackThread::exit()
+{
+    Mutex::Autolock _l(mCallbackLock);
+    requestExit();
+    mCallbackCond.broadcast();
+}
+
+sp<IMemory> RadioService::CallbackThread::prepareEvent(radio_hal_event_t *halEvent)
+{
+    sp<IMemory> eventMemory;
+
+    size_t headerSize =
+            (sizeof(struct radio_event) + sizeof(unsigned int) - 1) /sizeof(unsigned int);
+    size_t metadataSize = 0;
+    switch (halEvent->type) {
+    case RADIO_EVENT_TUNED:
+    case RADIO_EVENT_AF_SWITCH:
+        if (radio_metadata_check(halEvent->info.metadata) == 0) {
+            metadataSize = radio_metadata_get_size(halEvent->info.metadata);
+        }
+        break;
+    case RADIO_EVENT_METADATA:
+        if (radio_metadata_check(halEvent->metadata) != 0) {
+            return eventMemory;
+        }
+        metadataSize = radio_metadata_get_size(halEvent->metadata);
+        break;
+    default:
+        break;
+    }
+    size_t size = headerSize + metadataSize;
+    eventMemory = mMemoryDealer->allocate(size);
+    if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+        eventMemory.clear();
+        return eventMemory;
+    }
+    struct radio_event *event = (struct radio_event *)eventMemory->pointer();
+    event->type = halEvent->type;
+    event->status = halEvent->status;
+
+    switch (event->type) {
+    case RADIO_EVENT_CONFIG:
+        event->config.band = halEvent->config;
+        break;
+    case RADIO_EVENT_TUNED:
+    case RADIO_EVENT_AF_SWITCH:
+        event->info = halEvent->info;
+        if (metadataSize != 0) {
+            memcpy((char *)event + headerSize, halEvent->info.metadata, metadataSize);
+            // replace meta data pointer by offset while in shared memory so that receiving side
+            // can restore the pointer in destination process.
+            event->info.metadata = (radio_metadata_t *)headerSize;
+        }
+        break;
+    case RADIO_EVENT_TA:
+    case RADIO_EVENT_ANTENNA:
+    case RADIO_EVENT_CONTROL:
+        event->on = halEvent->on;
+        break;
+    case RADIO_EVENT_METADATA:
+        memcpy((char *)event + headerSize, halEvent->metadata, metadataSize);
+        // replace meta data pointer by offset while in shared memory so that receiving side
+        // can restore the pointer in destination process.
+        event->metadata = (radio_metadata_t *)headerSize;
+        break;
+    case RADIO_EVENT_HW_FAILURE:
+    default:
+        break;
+    }
+
+    return eventMemory;
+}
+
+void RadioService::CallbackThread::sendEvent(radio_hal_event_t *event)
+ {
+     sp<IMemory> eventMemory = prepareEvent(event);
+     if (eventMemory == 0) {
+         return;
+     }
+
+     AutoMutex lock(mCallbackLock);
+     mEventQueue.add(eventMemory);
+     mCallbackCond.signal();
+     ALOGV("%s DONE", __FUNCTION__);
+}
+
+
+#undef LOG_TAG
+#define LOG_TAG "RadioService::Module"
+
+RadioService::Module::Module(const sp<RadioService>& service,
+                                      radio_hw_device* hwDevice,
+                                      radio_properties properties)
+ : mService(service), mHwDevice(hwDevice), mProperties(properties), mMute(true)
+{
+}
+
+RadioService::Module::~Module() {
+    mModuleClients.clear();
+}
+
+status_t RadioService::Module::dump(int fd __unused, const Vector<String16>& args __unused) {
+    String8 result;
+    return NO_ERROR;
+}
+
+sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioClient>& client,
+                                    const struct radio_band_config *config,
+                                    bool audio)
+{
+    ALOGV("addClient() %p config %p product %s", this, config, mProperties.product);
+    AutoMutex lock(mLock);
+    sp<ModuleClient> moduleClient;
+    int ret;
+
+    for (size_t i = 0; i < mModuleClients.size(); i++) {
+        if (mModuleClients[i]->client() == client) {
+            // client already connected: reject
+            return moduleClient;
+        }
+    }
+    moduleClient = new ModuleClient(this, client, config, audio);
+
+    struct radio_hal_band_config halConfig;
+    halConfig = config->band;
+
+    sp<ModuleClient> oldestTuner;
+    sp<ModuleClient> oldestAudio;
+    size_t allocatedTuners = 0;
+    size_t allocatedAudio = 0;
+    for (size_t i = 0; i < mModuleClients.size(); i++) {
+        if (mModuleClients[i]->getTuner() != NULL) {
+            if (mModuleClients[i]->audio()) {
+                if (oldestAudio == 0) {
+                    oldestAudio = mModuleClients[i];
+                }
+                allocatedAudio++;
+            } else {
+                if (oldestTuner == 0) {
+                    oldestTuner = mModuleClients[i];
+                }
+                allocatedTuners++;
+            }
+        }
+    }
+
+    const struct radio_tuner *halTuner;
+    if (audio) {
+        if (allocatedAudio >= mProperties.num_audio_sources) {
+            ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
+            halTuner = oldestAudio->getTuner();
+            oldestAudio->setTuner(NULL);
+            mHwDevice->close_tuner(mHwDevice, halTuner);
+        }
+    } else {
+        if (allocatedAudio + allocatedTuners >= mProperties.num_tuners) {
+            if (allocatedTuners != 0) {
+                ALOG_ASSERT(oldestTuner != 0, "addClient() allocatedTuners/oldestTuner mismatch");
+                halTuner = oldestTuner->getTuner();
+                oldestTuner->setTuner(NULL);
+                mHwDevice->close_tuner(mHwDevice, halTuner);
+            } else {
+                ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
+                halTuner = oldestAudio->getTuner();
+                oldestAudio->setTuner(NULL);
+                mHwDevice->close_tuner(mHwDevice, halTuner);
+            }
+        }
+    }
+
+    ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio,
+                                RadioService::callback, moduleClient->callbackThread().get(),
+                                &halTuner);
+    if (ret == 0) {
+        ALOGV("addClient() setTuner %p", halTuner);
+        moduleClient->setTuner(halTuner);
+        mModuleClients.add(moduleClient);
+    } else {
+        moduleClient.clear();
+    }
+
+    //TODO notify audio device connection to audio policy manager if audio is on
+
+    ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
+
+    return moduleClient;
+}
+
+void RadioService::Module::removeClient(const sp<ModuleClient>& moduleClient) {
+    ALOGV("removeClient()");
+    AutoMutex lock(mLock);
+    int ret;
+    ssize_t index = -1;
+
+    for (size_t i = 0; i < mModuleClients.size(); i++) {
+        if (mModuleClients[i] == moduleClient) {
+            index = i;
+            break;
+        }
+    }
+    if (index == -1) {
+        return;
+    }
+
+    mModuleClients.removeAt(index);
+    const struct radio_tuner *halTuner = moduleClient->getTuner();
+    if (halTuner == NULL) {
+        return;
+    }
+
+    mHwDevice->close_tuner(mHwDevice, halTuner);
+
+    //TODO notify audio device disconnection to audio policy manager if audio was on
+    mMute = true;
+
+    if (mModuleClients.isEmpty()) {
+        return;
+    }
+
+    sp<ModuleClient> youngestClient;
+    sp<ModuleClient> youngestClientAudio;
+    size_t allocatedTuners = 0;
+    size_t allocatedAudio = 0;
+    for (ssize_t i = mModuleClients.size(); i >= 0; i--) {
+        if (mModuleClients[i]->getTuner() == NULL) {
+            if (mModuleClients[i]->audio()) {
+                if (youngestClientAudio == 0) {
+                    youngestClientAudio = mModuleClients[i];
+                }
+            } else {
+                if (youngestClient == 0) {
+                    youngestClient = mModuleClients[i];
+                }
+            }
+        } else {
+            if (mModuleClients[i]->audio()) {
+                allocatedAudio++;
+            } else {
+                allocatedTuners++;
+            }
+        }
+    }
+
+    ALOG_ASSERT(allocatedTuners + allocatedAudio < mProperties.num_tuners,
+                "removeClient() removed client but no tuner available");
+
+    ALOG_ASSERT(!moduleClient->audio() || allocatedAudio < mProperties.num_audio_sources,
+                "removeClient() removed audio client but no tuner with audio available");
+
+    if (allocatedAudio < mProperties.num_audio_sources && youngestClientAudio != 0) {
+        youngestClient = youngestClientAudio;
+    }
+
+    ALOG_ASSERT(youngestClient != 0, "removeClient() removed client no candidate found for tuner");
+
+    struct radio_hal_band_config halConfig = youngestClient->halConfig();
+    ret = mHwDevice->open_tuner(mHwDevice, &halConfig, youngestClient->audio(),
+                                RadioService::callback, moduleClient->callbackThread().get(),
+                                &halTuner);
+
+    //TODO notify audio device connection to audio policy manager if audio is on
+
+    if (ret == 0) {
+        youngestClient->setTuner(halTuner);
+    }
+}
+
+status_t RadioService::Module::setMute(bool mute)
+{
+    Mutex::Autolock _l(mLock);
+    if (mute != mMute) {
+        mMute = mute;
+        //TODO notifify audio policy manager of media activity on radio audio device
+    }
+    return NO_ERROR;
+}
+
+status_t RadioService::Module::getMute(bool *mute)
+{
+    Mutex::Autolock _l(mLock);
+    *mute = mMute;
+    return NO_ERROR;
+}
+
+
+const struct radio_band_config *RadioService::Module::getDefaultConfig() const
+{
+    if (mProperties.num_bands == 0) {
+        return NULL;
+    }
+    return &mProperties.bands[0];
+}
+
+#undef LOG_TAG
+#define LOG_TAG "RadioService::ModuleClient"
+
+RadioService::ModuleClient::ModuleClient(const sp<Module>& module,
+                                         const sp<IRadioClient>& client,
+                                         const struct radio_band_config *config,
+                                         bool audio)
+ : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(NULL)
+{
+}
+
+void RadioService::ModuleClient::onFirstRef()
+{
+    mCallbackThread = new CallbackThread(this);
+    IInterface::asBinder(mClient)->linkToDeath(this);
+}
+
+RadioService::ModuleClient::~ModuleClient() {
+    if (mClient != 0) {
+        IInterface::asBinder(mClient)->unlinkToDeath(this);
+        mClient.clear();
+    }
+    if (mCallbackThread != 0) {
+        mCallbackThread->exit();
+    }
+}
+
+status_t RadioService::ModuleClient::dump(int fd __unused,
+                                             const Vector<String16>& args __unused) {
+    String8 result;
+    return NO_ERROR;
+}
+
+void RadioService::ModuleClient::detach() {
+    ALOGV("%s", __FUNCTION__);
+    sp<ModuleClient> strongMe = this;
+    {
+        AutoMutex lock(mLock);
+        if (mClient != 0) {
+            IInterface::asBinder(mClient)->unlinkToDeath(this);
+            mClient.clear();
+        }
+    }
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return;
+    }
+    module->removeClient(this);
+}
+
+radio_hal_band_config_t RadioService::ModuleClient::halConfig() const
+{
+    AutoMutex lock(mLock);
+    ALOGV("%s locked", __FUNCTION__);
+    return mConfig.band;
+}
+
+const struct radio_tuner *RadioService::ModuleClient::getTuner() const
+{
+    AutoMutex lock(mLock);
+    ALOGV("%s locked", __FUNCTION__);
+    return mTuner;
+}
+
+void RadioService::ModuleClient::setTuner(const struct radio_tuner *tuner)
+{
+    ALOGV("%s %p", __FUNCTION__, this);
+
+    AutoMutex lock(mLock);
+    mTuner = tuner;
+    ALOGV("%s locked", __FUNCTION__);
+
+    radio_hal_event_t event;
+    event.type = RADIO_EVENT_CONTROL;
+    event.status = 0;
+    event.on = mTuner != NULL;
+    mCallbackThread->sendEvent(&event);
+    ALOGV("%s DONE", __FUNCTION__);
+
+}
+
+status_t RadioService::ModuleClient::setConfiguration(const struct radio_band_config *config)
+{
+    AutoMutex lock(mLock);
+    status_t status = NO_ERROR;
+    ALOGV("%s locked", __FUNCTION__);
+
+    if (mTuner != NULL) {
+        struct radio_hal_band_config halConfig;
+        halConfig = config->band;
+        status = (status_t)mTuner->set_configuration(mTuner, &halConfig);
+        if (status == NO_ERROR) {
+            mConfig = *config;
+        }
+    } else {
+        mConfig = *config;
+        status == INVALID_OPERATION;
+    }
+
+    return status;
+}
+
+status_t RadioService::ModuleClient::getConfiguration(struct radio_band_config *config)
+{
+    AutoMutex lock(mLock);
+    status_t status = NO_ERROR;
+    ALOGV("%s locked", __FUNCTION__);
+
+    if (mTuner != NULL) {
+        struct radio_hal_band_config halConfig;
+        status = (status_t)mTuner->get_configuration(mTuner, &halConfig);
+        if (status == NO_ERROR) {
+            mConfig.band = halConfig;
+        }
+    }
+    *config = mConfig;
+
+    return status;
+}
+
+status_t RadioService::ModuleClient::setMute(bool mute)
+{
+    sp<Module> module;
+    {
+        Mutex::Autolock _l(mLock);
+        ALOGV("%s locked", __FUNCTION__);
+        if (mTuner == NULL || !mAudio) {
+            return INVALID_OPERATION;
+        }
+        module = mModule.promote();
+        if (module == 0) {
+            return NO_INIT;
+        }
+    }
+    module->setMute(mute);
+    return NO_ERROR;
+}
+
+status_t RadioService::ModuleClient::getMute(bool *mute)
+{
+    sp<Module> module;
+    {
+        Mutex::Autolock _l(mLock);
+        ALOGV("%s locked", __FUNCTION__);
+        module = mModule.promote();
+        if (module == 0) {
+            return NO_INIT;
+        }
+    }
+    return module->getMute(mute);
+}
+
+status_t RadioService::ModuleClient::scan(radio_direction_t direction, bool skipSubChannel)
+{
+    AutoMutex lock(mLock);
+    ALOGV("%s locked", __FUNCTION__);
+    status_t status;
+    if (mTuner != NULL) {
+        status = (status_t)mTuner->scan(mTuner, direction, skipSubChannel);
+    } else {
+        status = INVALID_OPERATION;
+    }
+    return status;
+}
+
+status_t RadioService::ModuleClient::step(radio_direction_t direction, bool skipSubChannel)
+{
+    AutoMutex lock(mLock);
+    ALOGV("%s locked", __FUNCTION__);
+    status_t status;
+    if (mTuner != NULL) {
+        status = (status_t)mTuner->step(mTuner, direction, skipSubChannel);
+    } else {
+        status = INVALID_OPERATION;
+    }
+    return status;
+}
+
+status_t RadioService::ModuleClient::tune(unsigned int channel, unsigned int subChannel)
+{
+    AutoMutex lock(mLock);
+    ALOGV("%s locked", __FUNCTION__);
+    status_t status;
+    if (mTuner != NULL) {
+        status = (status_t)mTuner->tune(mTuner, channel, subChannel);
+    } else {
+        status = INVALID_OPERATION;
+    }
+    return status;
+}
+
+status_t RadioService::ModuleClient::cancel()
+{
+    AutoMutex lock(mLock);
+    ALOGV("%s locked", __FUNCTION__);
+    status_t status;
+    if (mTuner != NULL) {
+        status = (status_t)mTuner->cancel(mTuner);
+    } else {
+        status = INVALID_OPERATION;
+    }
+    return status;
+}
+
+status_t RadioService::ModuleClient::getProgramInformation(struct radio_program_info *info)
+{
+    AutoMutex lock(mLock);
+    ALOGV("%s locked", __FUNCTION__);
+    status_t status;
+    if (mTuner != NULL) {
+        status = (status_t)mTuner->get_program_information(mTuner, info);
+    } else {
+        status = INVALID_OPERATION;
+    }
+    return status;
+}
+
+status_t RadioService::ModuleClient::hasControl(bool *hasControl)
+{
+    Mutex::Autolock lock(mLock);
+    ALOGV("%s locked", __FUNCTION__);
+    *hasControl = mTuner != NULL;
+    return NO_ERROR;
+}
+
+void RadioService::ModuleClient::onCallbackEvent(const sp<IMemory>& eventMemory)
+{
+    if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+        return;
+    }
+
+    sp<IRadioClient> client;
+    {
+        AutoMutex lock(mLock);
+        ALOGV("%s locked", __FUNCTION__);
+        radio_event_t *event = (radio_event_t *)eventMemory->pointer();
+        switch (event->type) {
+        case RADIO_EVENT_CONFIG:
+            mConfig.band = event->config.band;
+            event->config.region = mConfig.region;
+            break;
+        default:
+            break;
+        }
+
+        client = mClient;
+    }
+    if (client != 0) {
+        client->onEvent(eventMemory);
+    }
+}
+
+
+void RadioService::ModuleClient::binderDied(
+    const wp<IBinder> &who __unused) {
+    ALOGW("client binder died for client %p", this);
+    detach();
+}
+
+}; // namespace android
diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h
new file mode 100644
index 0000000..9ede020
--- /dev/null
+++ b/services/radio/RadioService.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_HARDWARE_RADIO_SERVICE_H
+#define ANDROID_HARDWARE_RADIO_SERVICE_H
+
+#include <utils/Vector.h>
+//#include <binder/AppOpsManager.h>
+#include <binder/MemoryDealer.h>
+#include <binder/BinderService.h>
+#include <binder/IAppOpsCallback.h>
+#include <radio/IRadioService.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioClient.h>
+#include <system/radio.h>
+#include <hardware/radio.h>
+
+namespace android {
+
+class MemoryHeapBase;
+
+class RadioService :
+    public BinderService<RadioService>,
+    public BnRadioService
+{
+    friend class BinderService<RadioService>;
+
+public:
+    class ModuleClient;
+    class Module;
+
+    static char const* getServiceName() { return "media.radio"; }
+
+                        RadioService();
+    virtual             ~RadioService();
+
+    // IRadioService
+    virtual status_t listModules(struct radio_properties *properties,
+                                 uint32_t *numModules);
+
+    virtual status_t attach(radio_handle_t handle,
+                            const sp<IRadioClient>& client,
+                            const struct radio_band_config *config,
+                            bool withAudio,
+                            sp<IRadio>& radio);
+
+    virtual status_t    onTransact(uint32_t code, const Parcel& data,
+                                   Parcel* reply, uint32_t flags);
+
+    virtual status_t    dump(int fd, const Vector<String16>& args);
+
+
+    class Module : public virtual RefBase {
+    public:
+
+       Module(const sp<RadioService>& service,
+              radio_hw_device* hwDevice,
+              struct radio_properties properties);
+
+       virtual ~Module();
+
+               sp<ModuleClient> addClient(const sp<IRadioClient>& client,
+                                  const struct radio_band_config *config,
+                                  bool audio);
+
+               void removeClient(const sp<ModuleClient>& moduleClient);
+
+               status_t setMute(bool mute);
+
+               status_t getMute(bool *mute);
+
+       virtual status_t dump(int fd, const Vector<String16>& args);
+
+       const struct radio_hw_device *hwDevice() const { return mHwDevice; }
+       const struct radio_properties properties() const { return mProperties; }
+       const struct radio_band_config *getDefaultConfig() const ;
+
+       wp<RadioService> service() const { return mService; }
+
+    private:
+
+        Mutex                         mLock;
+        wp<RadioService>              mService;
+        const struct radio_hw_device        *mHwDevice;
+        const struct radio_properties       mProperties;
+        Vector< sp<ModuleClient> >    mModuleClients;
+        bool                          mMute;
+    }; // class Module
+
+    class CallbackThread : public Thread {
+    public:
+
+        CallbackThread(const wp<ModuleClient>& moduleClient);
+
+        virtual ~CallbackThread();
+
+
+        // Thread virtuals
+        virtual bool threadLoop();
+
+        // RefBase
+        virtual void onFirstRef();
+
+                void exit();
+
+                void sendEvent(radio_hal_event_t *halEvent);
+                sp<IMemory> prepareEvent(radio_hal_event_t *halEvent);
+
+    private:
+        wp<ModuleClient>      mModuleClient;
+        Condition             mCallbackCond;
+        Mutex                 mCallbackLock;
+        Vector< sp<IMemory> > mEventQueue;
+        sp<MemoryDealer>      mMemoryDealer;
+    }; // class CallbackThread
+
+    class ModuleClient : public BnRadio,
+                   public IBinder::DeathRecipient {
+    public:
+
+       ModuleClient(const sp<Module>& module,
+              const sp<IRadioClient>& client,
+              const struct radio_band_config *config,
+              bool audio);
+
+       virtual ~ModuleClient();
+
+       // IRadio
+       virtual void detach();
+
+       virtual status_t setConfiguration(const struct radio_band_config *config);
+
+       virtual status_t getConfiguration(struct radio_band_config *config);
+
+       virtual status_t setMute(bool mute);
+
+       virtual status_t getMute(bool *mute);
+
+       virtual status_t scan(radio_direction_t direction, bool skipSubChannel);
+
+       virtual status_t step(radio_direction_t direction, bool skipSubChannel);
+
+       virtual status_t tune(unsigned int channel, unsigned int subChannel);
+
+       virtual status_t cancel();
+
+       virtual status_t getProgramInformation(struct radio_program_info *info);
+
+       virtual status_t hasControl(bool *hasControl);
+
+       virtual status_t dump(int fd, const Vector<String16>& args);
+
+               sp<IRadioClient> client() const { return mClient; }
+               wp<Module> module() const { return mModule; }
+               radio_hal_band_config_t halConfig() const;
+               sp<CallbackThread> callbackThread() const { return mCallbackThread; }
+               void setTuner(const struct radio_tuner *tuner);
+               const struct radio_tuner *getTuner() const;
+               bool audio() const { return mAudio; }
+
+               void onCallbackEvent(const sp<IMemory>& event);
+
+       virtual void onFirstRef();
+
+
+       // IBinder::DeathRecipient implementation
+       virtual void        binderDied(const wp<IBinder> &who);
+
+    private:
+
+        mutable Mutex               mLock;
+        wp<Module>                  mModule;
+        sp<IRadioClient>            mClient;
+        radio_band_config_t         mConfig;
+        sp<CallbackThread>          mCallbackThread;
+        const bool                  mAudio;
+        const struct radio_tuner    *mTuner;
+    }; // class ModuleClient
+
+
+    static void callback(radio_hal_event_t *halEvent, void *cookie);
+
+private:
+
+    virtual void onFirstRef();
+
+    static void convertProperties(radio_properties_t *properties,
+                                  const radio_hal_properties_t *halProperties);
+    Mutex               mServiceLock;
+    volatile int32_t    mNextUniqueId;
+    DefaultKeyedVector< radio_handle_t, sp<Module> > mModules;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_SERVICE_H