Merge "camera: fixup htc params" into jellybean
diff --git a/include/media/AudioParameter.h b/include/media/AudioParameter.h
index d29c699..2476e66 100644
--- a/include/media/AudioParameter.h
+++ b/include/media/AudioParameter.h
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2008-2011 The Android Open Source Project
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@
     static const char * const keyFluenceType;
     static const char * const keySSR;
     static const char * const keyHandleA2dpDevice;
+    static const char * const keyADSPStatus;
 #endif
 
     String8 toString();
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index d8c57d3..31b1b08 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
  * Not a Contribution, Apache license notifications and license are retained
  * for attribution purposes only.
  *
- * Copyright (C) 2008 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
@@ -120,6 +120,9 @@
         audio_channel_mask_t channelMask, size_t* buffSize);
 
     static status_t setVoiceVolume(float volume);
+#ifdef QCOM_FM_ENABLED
+    static status_t setFmVolume(float volume);
+#endif
 
     // return the number of audio frames written by AudioFlinger to audio HAL and
     // audio dsp to DAC since the output on which the specified stream is playing
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 77a0b26..108f1a7 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -64,7 +65,8 @@
         EVENT_LOOP_END = 2,         // Sample loop end was reached; playback restarted from loop start if loop count was not 0.
         EVENT_MARKER = 3,           // Playback head is at the specified marker position (See setMarkerPosition()).
         EVENT_NEW_POS = 4,          // Playback head is at a new position (See setPositionUpdatePeriod()).
-        EVENT_BUFFER_END = 5        // Playback head is at the end of the buffer.
+        EVENT_BUFFER_END = 5,       // Playback head is at the end of the buffer.
+        EVENT_HW_FAIL = 6,          // ADSP failure.
     };
 
     /* Client should declare Buffer on the stack and pass address to obtainBuffer()
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 0700a68..396f87b 100755
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -1,10 +1,10 @@
 /*
+ * Copyright (C) 2007 The Android Open Source Project
  * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
  * Not a Contribution, Apache license notifications and license are retained
  * for attribution purposes only.
  *
- * Copyright (C) 2007 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
@@ -219,6 +219,9 @@
     // that looks on primary device for a stream with fast flag, primary flag, or first one.
     virtual int32_t getPrimaryOutputSamplingRate() = 0;
     virtual int32_t getPrimaryOutputFrameCount() = 0;
+#ifdef QCOM_FM_ENABLED
+    virtual status_t setFmVolume(float volume) = 0;
+#endif
 
 };
 
diff --git a/include/media/stagefright/FMA2DPWriter.h b/include/media/stagefright/FMA2DPWriter.h
new file mode 100644
index 0000000..f1c1694
--- /dev/null
+++ b/include/media/stagefright/FMA2DPWriter.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only
+ *
+ * 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 FM_A2DP_WRITER_H_
+
+#define FM_A2DP_WRITER_H_
+
+#include <stdio.h>
+
+#include <media/stagefright/MediaWriter.h>
+#include <utils/threads.h>
+#include <media/AudioRecord.h>
+#include <utils/List.h>
+#include <semaphore.h>
+#include <media/mediarecorder.h>
+
+namespace android {
+
+struct MediaSource;
+struct MetaData;
+
+struct audioBufferstruct {
+   public:
+   audioBufferstruct (void *buff, size_t bufflen)
+      :audioBuffer(buff), bufferlen(bufflen){}
+
+   void  *audioBuffer;
+   size_t bufferlen;
+ };
+
+struct FMA2DPWriter : public MediaWriter {
+    FMA2DPWriter();
+
+    status_t initCheck() const;
+    virtual status_t addSource(const sp<MediaSource> &source);
+    virtual bool reachedEOS();
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+    virtual status_t pause();
+    virtual status_t allocateBufferPool();
+
+protected:
+    virtual ~FMA2DPWriter();
+
+private:
+    List<audioBufferstruct > mFreeQ,mDataQ;
+    Mutex mFreeQLock,mDataQLock;
+    sem_t mReaderThreadWakeupsem,mWriterThreadWakeupsem;
+    pthread_t mReaderThread,mWriterThread;
+    bool mStarted;
+    volatile bool mDone;
+    int32_t mAudioChannels;
+    int32_t mSampleRate;
+    audio_format_t mAudioFormat;
+    audio_source_t mAudioSource;
+    size_t mBufferSize;
+    static void *ReaderThreadWrapper(void *);
+    static void *WriterThreadWrapper(void *);
+    status_t readerthread();
+    status_t writerthread();
+    FMA2DPWriter(const FMA2DPWriter &);
+    FMA2DPWriter &operator=(const FMA2DPWriter &);
+};
+
+}  // namespace android
+
+#endif  // FM_A2DP_WRITER_H_
diff --git a/include/media/stagefright/FMRadioSource.h b/include/media/stagefright/FMRadioSource.h
new file mode 100644
index 0000000..32db156
--- /dev/null
+++ b/include/media/stagefright/FMRadioSource.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ * Copyright (C) 2012 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.
+ *
+ * Author: Stefan Ekenberg (stefan.ekenberg@stericsson.com) for ST-Ericsson
+ */
+
+#ifndef FMRADIO_SOURCE_H_
+
+#define FMRADIO_SOURCE_H_
+
+#include <media/AudioRecord.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <system/audio.h>
+
+namespace android {
+
+class FMRadioSource : public DataSource {
+public:
+    FMRadioSource();
+
+    virtual status_t initCheck() const;
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+    virtual status_t getSize(off64_t *size);
+
+protected:
+    virtual ~FMRadioSource();
+
+private:
+    struct Buffer {
+        size_t  frameCount;
+        size_t  size;
+        int8_t* data;
+    };
+
+    status_t openRecord(int frameCount, audio_io_handle_t input);
+    status_t obtainBuffer(Buffer* audioBuffer);
+
+    status_t mInitCheck;
+    bool mStarted;
+    int mSessionId;
+    sp<IAudioRecord> mAudioRecord;
+    sp<IMemory> mCblkMemory;
+    audio_track_cblk_t* mCblk;
+
+    DISALLOW_EVIL_CONSTRUCTORS(FMRadioSource);
+};
+
+}  // namespace android
+
+#endif  // FMRADIO_SOURCE_H_
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index fbb34f4..367d9f6 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2006-2011 The Android Open Source Project
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
 const char * const AudioParameter::keyFluenceType = AUDIO_PARAMETER_KEY_FLUENCE_TYPE;
 const char * const AudioParameter::keySSR = AUDIO_PARAMETER_KEY_SSR;
 const char * const AudioParameter::keyHandleA2dpDevice = AUDIO_PARAMETER_KEY_HANDLE_A2DP_DEVICE;
+const char * const AudioParameter::keyADSPStatus = AUDIO_PARAMETER_KEY_ADSP_STATUS;
 #endif
 
 AudioParameter::AudioParameter(const String8& keyValuePairs)
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index fd8c320..aaf2ddd 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -1,5 +1,9 @@
 /*
  * Copyright (C) 2006-2007 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -411,6 +415,15 @@
     }
 }
 
+#ifdef QCOM_FM_ENABLED
+status_t AudioSystem::setFmVolume(float value)
+{
+    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+    if (af == 0) return PERMISSION_DENIED;
+    return af->setFmVolume(value);
+}
+#endif
+
 // ---------------------------------------------------------------------------
 
 void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 36b1469..0d7d733 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -2,7 +2,7 @@
 **
 ** Copyright 2007, The Android Open Source Project
 **
-** Copyright (c) 2012, The Linux Foundation. All rights reserved.
+** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
 ** Not a Contribution, Apache license notifications and license are retained
 ** for attribution purposes only.
 
@@ -1620,6 +1620,10 @@
         ALOGV("Posting event underrun to Audio Sink.");
         mCbf(EVENT_UNDERRUN, mUserData, 0);
     }
+    if (msg == EVENT_HW_FAIL) {
+        ALOGV("Posting event HW fail to Audio Sink.");
+        mCbf(EVENT_HW_FAIL, mUserData, 0);
+    }
 }
 
 status_t AudioTrack::getTimeStamp(uint64_t *tstamp) {
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index cc6a75c..9d5691f 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -1,7 +1,8 @@
 /*
 **
 ** Copyright 2007, The Android Open Source Project
-** Copyright (c) 2012, The Linux Foundation. All rights reserved.
+** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+**
 ** Not a Contribution, Apache license notifications and license are retained
 ** for attribution purposes only.
 **
@@ -73,6 +74,9 @@
     GET_EFFECT_DESCRIPTOR,
     CREATE_EFFECT,
     MOVE_EFFECTS,
+#ifdef QCOM_FM_ENABLED
+    SET_FM_VOLUME,
+#endif
     LOAD_HW_MODULE,
     GET_PRIMARY_OUTPUT_SAMPLING_RATE,
     GET_PRIMARY_OUTPUT_FRAME_COUNT,
@@ -730,6 +734,17 @@
         return reply.readInt32();
     }
 
+#ifdef QCOM_FM_ENABLED
+    virtual status_t setFmVolume(float volume)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.writeFloat(volume);
+        remote()->transact(SET_FM_VOLUME, data, &reply);
+        return reply.readInt32();
+    }
+#endif
+
     virtual audio_module_handle_t loadHwModule(const char *name)
     {
         Parcel data, reply;
@@ -1128,6 +1143,14 @@
             reply->writeInt32(moveEffects(session, srcOutput, dstOutput));
             return NO_ERROR;
         } break;
+#ifdef QCOM_FM_ENABLED
+        case SET_FM_VOLUME: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            float volume = data.readFloat();
+            reply->writeInt32( setFmVolume(volume) );
+            return NO_ERROR;
+        } break;
+#endif
         case LOAD_HW_MODULE: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             reply->writeInt32(loadHwModule(data.readCString()));
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 414c262..f238e89 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1,6 +1,6 @@
 /*
 **
-** Copyright (c) 2012, The Linux Foundation. All rights reserved.
+** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
 ** Not a Contribution, Apache license notifications and license are retained
 ** for attribution purposes only.
 **
@@ -1735,6 +1735,27 @@
         data->unlock();
         return;
     }
+    if (event == AudioTrack::EVENT_HW_FAIL) {
+        ALOGW("Event hardware failure");
+        CallbackData *data = (CallbackData*)cookie;
+        if (data != NULL) {
+            data->lock();
+            AudioOutput *me = data->getOutput();
+            if (me == NULL) {
+                // no output set, likely because the track was
+                // scheduled to be reused
+                // by another player, but the format turned out
+                // to be incompatible.
+                data->unlock();
+                return;
+            }
+            ALOGV("Callback!!!");
+            (*me->mCallback)(me, NULL, (size_t)AudioTrack::EVENT_HW_FAIL,
+                             me->mCallbackCookie);
+            data->unlock();
+        }
+        return;
+    }
 #endif
     if (event == AudioTrack::EVENT_MORE_DATA) {
         CallbackData *data = (CallbackData*)cookie;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 335dd43..94886e8 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1,6 +1,9 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
- * Copyright (c) 2010 - 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,6 +37,9 @@
 #include <media/stagefright/ExtendedWriter.h>
 #include <media/stagefright/WAVEWriter.h>
 #endif
+#ifdef QCOM_FM_ENABLED
+#include <media/stagefright/FMA2DPWriter.h>
+#endif
 #include <media/stagefright/CameraSource.h>
 #include <media/stagefright/CameraSourceTimeLapse.h>
 #include <media/stagefright/MPEG2TSWriter.h>
@@ -771,6 +777,10 @@
     }
 
     status_t status = OK;
+#ifdef QCOM_FM_ENABLED
+    if(AUDIO_SOURCE_FM_RX_A2DP == mAudioSource)
+        return startFMA2DPWriter();
+#endif
 
     switch (mOutputFormat) {
         case OUTPUT_FORMAT_DEFAULT:
@@ -1036,6 +1046,23 @@
     return OK;
 }
 
+#ifdef QCOM_FM_ENABLED
+status_t StagefrightRecorder::startFMA2DPWriter() {
+    /* FM soc outputs at 48k */
+    mSampleRate = 48000;
+    mAudioChannels = 2;
+
+    sp<MetaData> meta = new MetaData;
+    meta->setInt32(kKeyChannelCount, mAudioChannels);
+    meta->setInt32(kKeySampleRate, mSampleRate);
+
+    mWriter = new FMA2DPWriter();
+    mWriter->setListener(mListener);
+    mWriter->start(meta.get());
+    return OK;
+}
+#endif
+
 status_t StagefrightRecorder::startRTPRecording() {
     CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_RTP_AVP);
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 3f0b821..fd58115 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -1,6 +1,9 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -132,6 +135,9 @@
         sp<MetaData> *meta);
     status_t startMPEG4Recording();
     status_t startAMRRecording();
+#ifdef QCOM_FM_ENABLED
+    status_t startFMA2DPWriter();
+#endif
     status_t startAACRecording();
 #ifdef QCOM_HARDWARE
     status_t startWAVERecording();
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index bbc338c..d416862 100755
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -61,6 +61,11 @@
         mp4/FragmentedMP4Parser.cpp       \
         mp4/TrackFragment.cpp             \
 
+ifeq ($(BOARD_HAVE_QCOM_FM),true)
+LOCAL_SRC_FILES+=                         \
+        FMA2DPWriter.cpp
+endif
+
 LOCAL_C_INCLUDES:= \
         $(TOP)/frameworks/av/include/media/stagefright/timedtext \
         $(TOP)/frameworks/native/include/media/hardware \
@@ -69,6 +74,12 @@
         $(TOP)/external/tremolo \
         $(TOP)/external/openssl/include
 
+ifeq ($(BOARD_USES_STE_FMRADIO),true)
+LOCAL_SRC_FILES += \
+        FMRadioSource.cpp                 \
+        PCMExtractor.cpp
+endif
+
 ifeq ($(BOARD_USES_QCOM_HARDWARE),true)
 LOCAL_SRC_FILES += \
         ExtendedWriter.cpp                \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index acc2aaf..34e1007 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -50,6 +50,7 @@
 #endif
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/FileSource.h>
+#include <media/stagefright/FMRadioSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -2408,6 +2409,15 @@
                 return UNKNOWN_ERROR;
             }
         }
+#ifdef STE_FM
+    } else if (!strncasecmp("fmradio://rx", mUri.string(), 12)) {
+        sniffedMIME = MEDIA_MIMETYPE_AUDIO_RAW;
+        dataSource = new FMRadioSource();
+        status_t err = dataSource->initCheck();
+        if (err != OK) {
+            return err;
+        }
+#endif
     } else {
         dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
     }
diff --git a/media/libstagefright/FMA2DPWriter.cpp b/media/libstagefright/FMA2DPWriter.cpp
new file mode 100644
index 0000000..1bcec17
--- /dev/null
+++ b/media/libstagefright/FMA2DPWriter.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only
+ *
+ * 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 "FMA2DPWriter"
+#include <utils/Log.h>
+
+
+#include <media/stagefright/FMA2DPWriter.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/mediarecorder.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+
+#include <media/AudioRecord.h>
+#include <media/AudioTrack.h>
+namespace android {
+
+#define BUFFER_POOL_SIZE 5
+static int kMaxBufferSize = 2048;
+
+FMA2DPWriter::FMA2DPWriter()
+    :mStarted(false),
+    mAudioChannels(0),
+    mSampleRate(0),
+    mAudioFormat(AUDIO_FORMAT_PCM_16_BIT),
+    mAudioSource(AUDIO_SOURCE_FM_RX_A2DP),
+    mBufferSize(0){
+    sem_init(&mReaderThreadWakeupsem,0,0);
+    sem_init(&mWriterThreadWakeupsem,0,0);
+}
+
+
+
+FMA2DPWriter::~FMA2DPWriter() {
+    if (mStarted) {
+        stop();
+    }
+    sem_destroy(&mReaderThreadWakeupsem);
+    sem_destroy(&mWriterThreadWakeupsem);
+}
+
+status_t FMA2DPWriter::initCheck() const {
+// API not need for FMA2DPWriter
+    return OK;
+}
+
+
+status_t FMA2DPWriter::addSource(const sp<MediaSource> &source) {
+// API not need for FMA2DPWriter
+    return OK;
+}
+
+status_t FMA2DPWriter::allocateBufferPool()
+{
+    Mutex::Autolock lock(mFreeQLock);
+
+    for (int i = 0; i < BUFFER_POOL_SIZE; ++i) {
+        int *buffer = (int*)malloc(mBufferSize);
+        if(buffer){
+            audioBufferstruct audioBuffer(buffer,mBufferSize);
+            mFreeQ.push_back(audioBuffer);
+        }
+        else{
+            ALOGE("fatal:failed to alloate buffer pool");
+            return  NO_INIT;
+        }
+    }
+    return OK;
+}
+
+status_t FMA2DPWriter::start(MetaData *params) {
+
+    if (mStarted) {
+        // Already started, does nothing
+        return OK;
+    }
+
+    if(!mStarted){
+        if(!params){
+            ALOGE("fatal:params cannot be null");
+            return NO_INIT;
+        }
+        CHECK( params->findInt32( kKeyChannelCount, &mAudioChannels ) );
+        CHECK(mAudioChannels  == 1 || mAudioChannels  == 2);
+        CHECK( params->findInt32( kKeySampleRate, &mSampleRate ) );
+
+        if ( NO_ERROR != AudioSystem::getInputBufferSize(
+                    mSampleRate, mAudioFormat, mAudioChannels, &mBufferSize) ){
+            mBufferSize = kMaxBufferSize ;
+        }
+        ALOGV("mBufferSize = %d", mBufferSize);
+    }
+
+    status_t err = allocateBufferPool();
+
+    if(err != OK)
+        return err;
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+    mDone = false;
+
+    pthread_create(&mReaderThread, &attr, ReaderThreadWrapper, this);
+    pthread_create(&mWriterThread, &attr, WriterThreadWrapper, this);
+
+    pthread_attr_destroy(&attr);
+
+
+    mStarted = true;
+
+    return OK;
+}
+
+status_t FMA2DPWriter::pause() {
+// API not need for FMA2DPWriter
+    return OK;
+}
+
+status_t FMA2DPWriter::stop() {
+    if (!mStarted) {
+        return OK;
+    }
+
+    mDone = true;
+
+    void *dummy;
+    pthread_join(mReaderThread, &dummy);
+    pthread_join(mWriterThread, &dummy);
+
+    for ( List<audioBufferstruct>::iterator it = mDataQ.begin();
+         it != mDataQ.end(); ++it){
+            free(it->audioBuffer);
+    }
+    for ( List<audioBufferstruct>::iterator it = mFreeQ.begin();
+         it != mFreeQ.end(); ++it){
+            free(it->audioBuffer);
+    }
+    mStarted = false;
+
+    return OK;
+}
+
+void *FMA2DPWriter::ReaderThreadWrapper(void *me) {
+    return (void *) static_cast<FMA2DPWriter *>(me)->readerthread();
+}
+
+void *FMA2DPWriter::WriterThreadWrapper(void *me) {
+    return (void *) static_cast<FMA2DPWriter *>(me)->writerthread();
+}
+
+status_t FMA2DPWriter::readerthread() {
+    status_t err = OK;
+    int framecount =((4*mBufferSize)/mAudioChannels)/sizeof(int16_t);
+    //sizeof(int16_t) is frame size for PCM stream
+    int inChannel =
+        (mAudioChannels == 2) ? AUDIO_CHANNEL_IN_STEREO :
+        AUDIO_CHANNEL_IN_MONO;
+
+    prctl(PR_SET_NAME, (unsigned long)"FMA2DPReaderThread", 0, 0, 0);
+
+    AudioRecord* record = new AudioRecord(
+                     mAudioSource,
+                     mSampleRate,
+                     mAudioFormat,
+                     inChannel,
+                     framecount);
+    if(!record){
+        ALOGE("fatal:Not able to open audiorecord");
+        return UNKNOWN_ERROR;
+    }
+
+    status_t res = record->initCheck();
+    if (res == NO_ERROR)
+        res = record->start();
+    else{
+        ALOGE("fatal:record init check failure");
+        return UNKNOWN_ERROR;
+    }
+
+
+    while (!mDone) {
+
+        mFreeQLock.lock();
+        if(mFreeQ.empty()){
+            mFreeQLock.unlock();
+            ALOGV("FreeQ empty");
+            sem_wait(&mReaderThreadWakeupsem);
+            ALOGV("FreeQ filled up");
+            continue;
+        }
+        List<audioBufferstruct>::iterator it = mFreeQ.begin();
+        audioBufferstruct buff ( it->audioBuffer,it->bufferlen);
+        mFreeQ.erase(it);
+        mFreeQLock.unlock();
+
+        buff.bufferlen = record->read(buff.audioBuffer, mBufferSize);
+        ALOGV("read %d bytes", buff.bufferlen);
+        if (buff.bufferlen <= 0){
+            ALOGE("error in reading from audiorecord..bailing out.");
+            this ->notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN,
+                           ERROR_MALFORMED);
+            err = INVALID_OPERATION ;
+            break;
+        }
+
+        mDataQLock.lock();
+        if(mDataQ.empty()){
+            ALOGV("waking up reader");
+            sem_post(&mWriterThreadWakeupsem);
+        }
+        mDataQ.push_back(buff);
+        mDataQLock.unlock();
+    }
+    record->stop();
+    delete record;
+
+    return err;
+}
+
+
+status_t FMA2DPWriter::writerthread(){
+    status_t err = OK;
+    int framecount =(16*mBufferSize)/sizeof(int16_t);
+    //sizeof(int16_t) is frame size for PCM stream
+    int outChannel = (mAudioChannels== 2) ? AUDIO_CHANNEL_OUT_STEREO :
+        AUDIO_CHANNEL_OUT_MONO;
+
+    prctl(PR_SET_NAME, (unsigned long)"FMA2DPWriterThread", 0, 0, 0);
+
+    AudioTrack *audioTrack= new AudioTrack(
+                AUDIO_STREAM_FM,
+                mSampleRate,
+                mAudioFormat,
+                outChannel,
+                framecount, 0);
+
+    if(!audioTrack){
+        ALOGE("fatal:Not able to open audiotrack");
+        return UNKNOWN_ERROR;
+    }
+    status_t res = audioTrack->initCheck();
+    if (res == NO_ERROR) {
+        audioTrack->setVolume(1, 1);
+        audioTrack->start();
+    }
+    else{
+        ALOGE("fatal:audiotrack init check failure");
+        return UNKNOWN_ERROR;
+    }
+
+
+    while (!mDone) {
+
+        mDataQLock.lock();
+        if(mDataQ.empty()){
+            mDataQLock.unlock();
+            ALOGV("dataQ empty");
+            sem_wait(&mWriterThreadWakeupsem);
+            ALOGV("dataQ filled up");
+            continue;
+        }
+        List<audioBufferstruct>::iterator it = mDataQ.begin();
+        audioBufferstruct buff ( it->audioBuffer,it->bufferlen);
+        mDataQ.erase(it);
+        mDataQLock.unlock();
+
+       size_t retval = audioTrack->write(buff.audioBuffer, buff.bufferlen);
+       if(!retval){
+            ALOGE("audio track write failure..bailing out");
+            this ->notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN,
+                           ERROR_MALFORMED);
+            err = INVALID_OPERATION ;
+            break;
+        }
+        ALOGV("wrote %d bytes", buff.bufferlen);
+
+        mFreeQLock.lock();
+        if(mFreeQ.empty()){
+            ALOGV("WAKING UP READER");
+            sem_post(&mReaderThreadWakeupsem);
+        }
+        mFreeQ.push_back(buff);
+        mFreeQLock.unlock();
+    }
+    audioTrack->stop();
+    delete audioTrack;
+
+    return err;
+}
+
+bool FMA2DPWriter::reachedEOS() {
+// API not need for FMA2DPWriter
+    return OK;
+}
+
+
+}  // namespace android
diff --git a/media/libstagefright/FMRadioSource.cpp b/media/libstagefright/FMRadioSource.cpp
new file mode 100644
index 0000000..c657e0c
--- /dev/null
+++ b/media/libstagefright/FMRadioSource.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ * Copyright (C) 2012 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.
+ *
+ * Author: Stefan Ekenberg (stefan.ekenberg@stericsson.com) for ST-Ericsson
+ */
+
+#define LOG_TAG "FMRadioSource"
+#include <utils/Log.h>
+
+#include <media/stagefright/FMRadioSource.h>
+#include <media/AudioSystem.h>
+#include <private/media/AudioTrackShared.h>
+#include <cutils/compiler.h>
+
+namespace android {
+
+static const int kSampleRate = 48000;
+static const audio_format_t kAudioFormat = AUDIO_FORMAT_PCM_16_BIT;
+static const audio_channel_mask_t kChannelMask = AUDIO_CHANNEL_IN_STEREO;
+static const int kBufferTimeoutMs = 3000;
+
+FMRadioSource::FMRadioSource()
+    : mInitCheck(NO_INIT),
+      mStarted(false),
+      mSessionId(AudioSystem::newAudioSessionId()) {
+
+    // get FM Radio RX input
+    audio_io_handle_t input = AudioSystem::getInput(AUDIO_SOURCE_FM_RX,
+                                                    kSampleRate,
+                                                    kAudioFormat,
+                                                    kChannelMask,
+                                                    mSessionId);
+    if (input == 0) {
+        ALOGE("Could not get audio input for FM Radio source");
+        mInitCheck = UNKNOWN_ERROR;
+        return;
+    }
+
+    // get frame count
+    int frameCount = 0;
+    status_t status = AudioRecord::getMinFrameCount(&frameCount, kSampleRate,
+                                                    kAudioFormat, popcount(kChannelMask));
+    if (status != NO_ERROR) {
+        mInitCheck = status;
+        return;
+    }
+
+    // create the IAudioRecord
+    status = openRecord(frameCount, input);
+    if (status != NO_ERROR) {
+        mInitCheck = status;
+        return;
+    }
+
+    AudioSystem::acquireAudioSessionId(mSessionId);
+
+    mInitCheck = OK;
+    return;
+}
+
+FMRadioSource::~FMRadioSource() {
+    AudioSystem::releaseAudioSessionId(mSessionId);
+}
+
+status_t FMRadioSource::initCheck() const {
+    return mInitCheck;
+}
+
+ssize_t FMRadioSource::readAt(off64_t offset, void *data, size_t size) {
+    Buffer audioBuffer;
+
+    if (!mStarted) {
+        status_t err = mAudioRecord->start(AudioSystem::SYNC_EVENT_NONE, 0);
+        if (err == OK) {
+            mStarted = true;
+        } else {
+            ALOGE("Failed to start audio source");
+            return 0;
+        }
+    }
+
+    // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
+    // while we are accessing the cblk
+    sp<IAudioRecord> audioRecord = mAudioRecord;
+    sp<IMemory> iMem = mCblkMemory;
+    audio_track_cblk_t* cblk = mCblk;
+
+    audioBuffer.frameCount = size / cblk->frameSize;
+
+    status_t err = obtainBuffer(&audioBuffer);
+    if (err != NO_ERROR) {
+        ALOGE("Error obtaining an audio buffer, giving up (err:%d).", err);
+        return 0;
+    }
+
+    memcpy(data, audioBuffer.data, audioBuffer.size);
+    mCblk->stepUser(audioBuffer.frameCount);
+
+    return audioBuffer.size;
+}
+
+status_t FMRadioSource::getSize(off64_t *size) {
+    *size = 0;
+    return OK;
+}
+
+// -------------------------------------------------------------------------
+
+status_t FMRadioSource::openRecord(int frameCount, audio_io_handle_t input)
+{
+    status_t status;
+    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
+    if (audioFlinger == 0) {
+        return NO_INIT;
+    }
+
+    sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), input,
+                                                       kSampleRate,
+                                                       kAudioFormat,
+                                                       kChannelMask,
+                                                       frameCount,
+                                                       IAudioFlinger::TRACK_DEFAULT,
+                                                       gettid(),
+                                                       &mSessionId,
+                                                       &status);
+
+    if (record == 0) {
+        ALOGE("AudioFlinger could not create record track, status: %d", status);
+        return status;
+    }
+
+    sp<IMemory> cblk = record->getCblk();
+    if (cblk == 0) {
+        ALOGE("Could not get control block");
+        return NO_INIT;
+    }
+    mAudioRecord = record;
+    mCblkMemory = cblk;
+    mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
+    mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
+    android_atomic_and(~CBLK_DIRECTION_MSK, &mCblk->flags);
+    return NO_ERROR;
+}
+
+status_t FMRadioSource::obtainBuffer(Buffer* audioBuffer)
+{
+    status_t result = NO_ERROR;
+    uint32_t framesReq = audioBuffer->frameCount;
+
+    audioBuffer->frameCount = 0;
+    audioBuffer->size       = 0;
+
+    mCblk->lock.lock();
+    uint32_t framesReady = mCblk->framesReady();
+    if (framesReady == 0) {
+        do {
+            result = mCblk->cv.waitRelative(mCblk->lock, milliseconds(kBufferTimeoutMs));
+            if (CC_UNLIKELY(result != NO_ERROR)) {
+                ALOGE("obtainBuffer timed out (is the CPU pegged?) "
+                        "user=%08x, server=%08x", mCblk->user, mCblk->server);
+                mCblk->lock.unlock();
+                return TIMED_OUT;
+            }
+
+            framesReady = mCblk->framesReady();
+        } while (framesReady == 0);
+    }
+    mCblk->lock.unlock();
+
+    if (framesReq > framesReady) {
+        framesReq = framesReady;
+    }
+
+    uint32_t u = mCblk->user;
+    uint32_t bufferEnd = mCblk->userBase + mCblk->frameCount;
+
+    if (framesReq > bufferEnd - u) {
+        framesReq = bufferEnd - u;
+    }
+
+    audioBuffer->frameCount = framesReq;
+    audioBuffer->size       = framesReq * mCblk->frameSize;
+    audioBuffer->data       = (int8_t*)mCblk->buffer(u);
+
+    return NO_ERROR;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 5ab898b..1eb5c19 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -26,6 +26,7 @@
 #include "include/FragmentedMP4Extractor.h"
 #include "include/WAVExtractor.h"
 #include "include/OggExtractor.h"
+#include "include/PCMExtractor.h"
 #include "include/MPEG2PSExtractor.h"
 #include "include/MPEG2TSExtractor.h"
 #include "include/DRMExtractor.h"
@@ -131,6 +132,10 @@
         ret = new AACExtractor(source, meta);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
         ret = new MPEG2PSExtractor(source);
+#ifdef STE_FM
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
+        ret = new PCMExtractor(source);
+#endif
     }
 
     if (ret != NULL) {
diff --git a/media/libstagefright/PCMExtractor.cpp b/media/libstagefright/PCMExtractor.cpp
new file mode 100644
index 0000000..bb26bcd
--- /dev/null
+++ b/media/libstagefright/PCMExtractor.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Andreas Gustafsson (andreas.a.gustafsson@stericsson.com)
+ *         for ST-Ericsson
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "PCMExtractor"
+#include <utils/Log.h>
+
+#include "include/PCMExtractor.h"
+
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+/**
+* The default buffer size.
+*/
+static const uint16_t kDefaultNumChannels = 2;
+
+/**
+* The default Sample rate.
+*/
+static const uint32_t kDefaultSampleRate = 48000;
+
+/**
+* Bits per sample.
+*/
+static const uint16_t kDefaultBitsPerSample = 16;
+
+/**
+* The default buffer size.
+*/
+static const uint32_t kDefaultBufferSize = 4800;
+
+/**
+* Buffer duration in ms, to be used for input
+*/
+static const uint16_t kInputBufferDuration = 64;
+
+/**
+* Buffer granulairity in samples to be used for input.
+*/
+static const uint16_t kBufferGranularityInSamples = 16;
+
+struct PCMSource : public MediaSource {
+    PCMSource(
+            const sp<DataSource> &dataSource,
+            const sp<MetaData> &meta,
+            int32_t bitsPerSample,
+            off_t offset, size_t size);
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+    virtual ~PCMSource();
+
+private:
+    static const size_t kMaxFrameSize;
+
+    sp<DataSource> mDataSource;
+    sp<MetaData> mMeta;
+    int32_t mSampleRate;
+    int32_t mNumChannels;
+    int32_t mBitsPerSample;
+    off_t mOffset;
+    size_t mSize;
+    bool mStarted;
+    MediaBufferGroup *mGroup;
+    off_t mCurrentPos;
+    uint32_t mBufferSize;
+
+    DISALLOW_EVIL_CONSTRUCTORS(PCMSource);
+};
+
+PCMExtractor::PCMExtractor(const sp<DataSource> &source)
+    : mDataSource(source),
+      mValidFormat(false) {
+    mInitCheck = init();
+}
+
+PCMExtractor::~PCMExtractor() {
+}
+
+sp<MetaData> PCMExtractor::getMetaData() {
+    sp<MetaData> meta = new MetaData;
+
+    if (mInitCheck != OK) {
+        return meta;
+    }
+
+    meta->setCString(kKeyMIMEType, "audio/raw");
+
+    return meta;
+}
+
+size_t PCMExtractor::countTracks() {
+    return mInitCheck == OK ? 1 : 0;
+}
+
+sp<MediaSource> PCMExtractor::getTrack(size_t index) {
+    if (mInitCheck != OK || index > 0) {
+        return NULL;
+    }
+
+    return new PCMSource(
+            mDataSource, mTrackMeta,
+            kDefaultBitsPerSample, mDataOffset, mDataSize);
+}
+
+sp<MetaData> PCMExtractor::getTrackMetaData(
+        size_t index, uint32_t flags) {
+    if (mInitCheck != OK || index > 0) {
+        return NULL;
+    }
+
+    return mTrackMeta;
+}
+
+status_t PCMExtractor::init() {
+    mDataOffset = 0;
+    mDataSize = 0;
+    mValidFormat = true;
+    mTrackMeta = new MetaData;mTrackMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+    mTrackMeta->setInt32(kKeyChannelCount, kDefaultNumChannels);
+    mTrackMeta->setInt32(kKeySampleRate, kDefaultSampleRate);
+    return OK;
+}
+
+const size_t PCMSource::kMaxFrameSize = 4800;
+
+PCMSource::PCMSource(
+        const sp<DataSource> &dataSource,
+        const sp<MetaData> &meta,
+        int32_t bitsPerSample,
+        off_t offset, size_t size)
+    : mDataSource(dataSource),
+      mMeta(meta),
+      mSampleRate(0),
+      mNumChannels(0),
+      mBitsPerSample(bitsPerSample),
+      mOffset(offset),
+      mSize(size),
+      mStarted(false),
+      mGroup(NULL),
+      mBufferSize(0) {
+    CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate));
+    CHECK(mMeta->findInt32(kKeyChannelCount, &mNumChannels));
+}
+
+PCMSource::~PCMSource() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+status_t PCMSource::start(MetaData *params) {
+    CHECK(!mStarted);
+
+    size_t size = kDefaultBufferSize;
+
+    if (mSampleRate != 0 && mNumChannels != 0) {
+        mBufferSize = mSampleRate * kInputBufferDuration / 1000 * mNumChannels * 2;
+        size_t granularity = kBufferGranularityInSamples * 2 * mNumChannels;
+        mBufferSize = (mBufferSize / granularity) * granularity;
+    }
+    mGroup = new MediaBufferGroup;
+    mGroup->add_buffer(new MediaBuffer(mBufferSize));
+
+    if (mBitsPerSample == 8) {
+        // As a temporary buffer for 8->16 bit conversion.
+        mGroup->add_buffer(new MediaBuffer(mBufferSize));
+    }
+
+    mCurrentPos = mOffset;
+
+    mStarted = true;
+    return OK;
+}
+
+status_t PCMSource::stop() {
+
+    CHECK(mStarted);
+    delete mGroup;
+    mGroup = NULL;
+
+    mStarted = false;
+    return OK;
+}
+
+sp<MetaData> PCMSource::getFormat() {
+   return mMeta;
+}
+
+status_t PCMSource::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    *out = NULL;
+    int64_t seekTimeUs;
+    ReadOptions::SeekMode seek = ReadOptions::SEEK_CLOSEST_SYNC;
+    if (options != NULL && options->getSeekTo(&seekTimeUs,&seek)) {
+        int64_t pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * 2;
+        if (pos > mSize) {
+            pos = mSize;
+        }
+        mCurrentPos = pos + mOffset;
+    }
+
+    MediaBuffer *buffer;
+    status_t err = mGroup->acquire_buffer(&buffer);
+    if (err != OK) {
+        return err;
+    }
+
+    ssize_t n = mDataSource->readAt(
+            mCurrentPos, buffer->data(), mBufferSize);
+    if (n <= 0) {
+        buffer->release();
+        buffer = NULL;
+        return ERROR_END_OF_STREAM;
+    }
+
+    mCurrentPos += n;
+
+    buffer->set_range(0, n);
+
+    if (mBitsPerSample == 8) {
+        // Convert 8-bit unsigned samples to 16-bit signed.
+
+        MediaBuffer *tmp;
+        CHECK_EQ(mGroup->acquire_buffer(&tmp), (status_t)OK);
+
+        // The new buffer holds the sample number of samples, but each
+        // one is 2 bytes wide.
+        tmp->set_range(0, 2 * n);
+
+        int16_t *dst = (int16_t *)tmp->data();
+        const uint8_t *src = (const uint8_t *)buffer->data();
+        while (n-- > 0) {
+            *dst++ = ((int16_t)(*src) - 128) * 256;
+            ++src;
+        }
+
+        buffer->release();
+        buffer = tmp;
+    } else if (mBitsPerSample == 24) {
+        // Convert 24-bit signed samples to 16-bit signed.
+
+        const uint8_t *src =
+            (const uint8_t *)buffer->data() + buffer->range_offset();
+        int16_t *dst = (int16_t *)src;
+
+        size_t numSamples = buffer->range_length() / 3;
+        for (size_t i = 0; i < numSamples; ++i) {
+            int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16);
+            x = (x << 8) >> 8;  // sign extension
+
+            x = x >> 8;
+            *dst++ = (int16_t)x;
+            src += 3;
+        }
+
+        buffer->set_range(buffer->range_offset(), 2 * numSamples);
+    }
+
+    size_t bytesPerSample = mBitsPerSample >> 3;
+
+    buffer->meta_data()->setInt64(
+            kKeyTime,
+            1000000LL * (mCurrentPos - mOffset)
+                / (mNumChannels * bytesPerSample) / mSampleRate);
+
+
+    *out = buffer;
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/TunnelPlayer.cpp b/media/libstagefright/TunnelPlayer.cpp
index 3b45db4..f9d0976 100644
--- a/media/libstagefright/TunnelPlayer.cpp
+++ b/media/libstagefright/TunnelPlayer.cpp
@@ -501,6 +501,28 @@
 size_t TunnelPlayer::AudioSinkCallback(
         MediaPlayerBase::AudioSink *audioSink,
         void *buffer, size_t size, void *cookie) {
+#ifdef QCOM_HARDWARE
+    TunnelPlayer *me = (TunnelPlayer *)cookie;
+    if(me != NULL) {
+        ALOGV("postAudioEOS mSeeking %d", me->mSeeking);
+        if (buffer == NULL && size == AudioTrack::EVENT_UNDERRUN) {
+            if(me->mReachedEOS == true) {
+                //in the case of seek all these flags will be reset
+                me->mReachedOutputEOS = true;
+                ALOGV("postAudioEOS mSeeking %d", me->mSeeking);
+                me->mObserver->postAudioEOS(0);
+            }else {
+                ALOGV("postAudioEOS ignored since %d", me->mSeeking);
+            }
+        } else if (size == AudioTrack::EVENT_HW_FAIL) {
+            ALOGV("postAudioEOS in SSR " );
+            me->mReachedOutputEOS = true;
+            me->mReachedEOS = true;
+            me->killExtractorThread = true;
+            me->mObserver->postAudioEOS(0);
+        }
+    }
+#else
     if (buffer == NULL && size == AudioTrack::EVENT_UNDERRUN) {
         TunnelPlayer *me = (TunnelPlayer *)cookie;
         if(me->mReachedEOS == true) {
@@ -512,6 +534,7 @@
             ALOGV("postAudioEOS ignored since %d", me->mSeeking);
         }
     }
+#endif
     return 1;
 }
 
diff --git a/media/libstagefright/include/PCMExtractor.h b/media/libstagefright/include/PCMExtractor.h
new file mode 100644
index 0000000..4717d10
--- /dev/null
+++ b/media/libstagefright/include/PCMExtractor.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Andreas Gustafsson (andreas.a.gustafsson@stericsson.com)
+ *         for ST-Ericsson
+ */
+
+#ifndef PCM_EXTRACTOR_H_
+
+#define PCM_EXTRACTOR_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+class PCMExtractor : public MediaExtractor {
+public:
+    // Extractor assumes ownership of "source".
+    PCMExtractor(const sp<DataSource> &source);
+
+    virtual size_t countTracks();
+    virtual sp<MediaSource> getTrack(size_t index);
+    virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+    virtual sp<MetaData> getMetaData();
+
+protected:
+    virtual ~PCMExtractor();
+
+private:
+    sp<DataSource> mDataSource;
+    status_t mInitCheck;
+    bool mValidFormat;
+    off_t mDataOffset;
+    size_t mDataSize;
+    sp<MetaData> mTrackMeta;
+
+    status_t init();
+
+    DISALLOW_EVIL_CONSTRUCTORS(PCMExtractor);
+};
+
+}  // namespace android
+
+#endif  // PCM_EXTRACTOR_H_
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index e4c055a..88da88c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1,9 +1,8 @@
 /*
 **
 ** Copyright 2007, The Android Open Source Project
-** Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+** Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
 **
-** Copyright (c) 2012, The Linux Foundation. All rights reserved.
 ** Not a Contribution, Apache license notifications and license are retained
 ** for attribution purposes only.
 **
@@ -107,6 +106,7 @@
 
 #ifdef QCOM_HARDWARE
 #define DIRECT_TRACK_EOS 1
+#define DIRECT_TRACK_HW_FAIL 6
 static const char lockName[] = "DirectTrack";
 #endif
 
@@ -1162,9 +1162,29 @@
             }
             mHardwareStatus = AUDIO_HW_IDLE;
         }
+#ifdef QCOM_HARDWARE
+        AudioParameter param = AudioParameter(keyValuePairs);
+        String8 value, key;
+        int i = 0;
+
+        key = String8(AudioParameter::keyADSPStatus);
+        if (param.get(key, value) == NO_ERROR) {
+            ALOGV("Set keyADSPStatus:%s", value.string());
+            if (value == "ONLINE" || value == "OFFLINE") {
+               if (!mDirectAudioTracks.isEmpty()) {
+                   for (i=0; i < mDirectAudioTracks.size(); i++) {
+                       mDirectAudioTracks.valueAt(i)->stream->common.set_parameters(
+                          &mDirectAudioTracks.valueAt(i)->stream->common, keyValuePairs.string());
+                   }
+               }
+           }
+        }
+#else
         // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
         AudioParameter param = AudioParameter(keyValuePairs);
         String8 value;
+#endif
+
         if (param.get(String8(AUDIO_PARAMETER_KEY_BT_NREC), value) == NO_ERROR) {
             bool btNrecIsOff = (value == AUDIO_PARAMETER_VALUE_OFF);
             if (mBtNrecIsOff != btNrecIsOff) {
@@ -1356,6 +1376,29 @@
     return BAD_VALUE;
 }
 
+#ifdef QCOM_FM_ENABLED
+status_t AudioFlinger::setFmVolume(float value)
+{
+    status_t ret = initCheck();
+    if (ret != NO_ERROR) {
+        return ret;
+    }
+
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    AutoMutex lock(mHardwareLock);
+    audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
+    mHardwareStatus = AUDIO_SET_FM_VOLUME;
+    ret = dev->set_fm_volume(dev, value);
+    mHardwareStatus = AUDIO_HW_IDLE;
+
+    return ret;
+}
+#endif
+
 void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
 {
 
@@ -6262,8 +6305,18 @@
 }
 
 void AudioFlinger::DirectAudioTrack::postEOS(int64_t delayUs) {
+#ifdef QCOM_HARDWARE
+    if (delayUs == 0 ) {
+       ALOGV("Notify Audio Track of EOS event");
+       mClient->notify(DIRECT_TRACK_EOS);
+    } else {
+       ALOGV("Notify Audio Track of hardware failure event");
+       mClient->notify(DIRECT_TRACK_HW_FAIL);
+    }
+#else
     ALOGV("Notify Audio Track of EOS event");
     mClient->notify(DIRECT_TRACK_EOS);
+#endif
 }
 
 void AudioFlinger::DirectAudioTrack::allocateBufPool() {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index cbda3fe..d6a3815 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -1,7 +1,8 @@
 /*
 **
 ** Copyright 2007, The Android Open Source Project
-** Copyright (c) 2012, The Linux Foundation. All rights reserved.
+** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+**
 ** Not a Contribution, Apache license notifications and license are retained
 ** for attribution purposes only.
 **
@@ -228,6 +229,10 @@
     virtual status_t moveEffects(int sessionId, audio_io_handle_t srcOutput,
                         audio_io_handle_t dstOutput);
 
+#ifdef QCOM_FM_ENABLED
+    virtual status_t setFmVolume(float volume);
+#endif
+
     virtual audio_module_handle_t loadHwModule(const char *name);
 
     virtual int32_t getPrimaryOutputSamplingRate();
@@ -2209,6 +2214,9 @@
         AUDIO_HW_SET_MIC_MUTE,          // set_mic_mute
         AUDIO_HW_SET_VOICE_VOLUME,      // set_voice_volume
         AUDIO_HW_SET_PARAMETER,         // set_parameters
+#ifdef QCOM_FM_ENABLED
+        AUDIO_SET_FM_VOLUME,
+#endif
         AUDIO_HW_GET_INPUT_BUFFER_SIZE, // get_input_buffer_size
         AUDIO_HW_GET_MASTER_VOLUME,     // get_master_volume
         AUDIO_HW_GET_PARAMETER,         // get_parameters
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 7dd46f2..388ad6c 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -1,5 +1,9 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -730,6 +734,18 @@
                     }
                     delete data;
                     }break;
+#ifdef QCOM_FM_ENABLED
+                case SET_FM_VOLUME: {
+                    FmVolumeData *data = (FmVolumeData *)command->mParam;
+                    ALOGV("AudioCommandThread() processing set fm volume volume %f", data->mVolume);
+                    command->mStatus = AudioSystem::setFmVolume(data->mVolume);
+                    if (command->mWaitStatus) {
+                        command->mCond.signal();
+                        mWaitWorkCV.wait(mLock);
+                    }
+                    delete data;
+                    }break;
+#endif
                 default:
                     ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                 }
@@ -885,6 +901,34 @@
     return status;
 }
 
+#ifdef QCOM_FM_ENABLED
+status_t AudioPolicyService::AudioCommandThread::fmVolumeCommand(float volume, int delayMs)
+{
+    status_t status = NO_ERROR;
+
+    AudioCommand *command = new AudioCommand();
+    command->mCommand = SET_FM_VOLUME;
+    FmVolumeData *data = new FmVolumeData();
+    data->mVolume = volume;
+    command->mParam = data;
+    if (delayMs == 0) {
+        command->mWaitStatus = true;
+    } else {
+        command->mWaitStatus = false;
+    }
+    Mutex::Autolock _l(mLock);
+    insertCommand_l(command, delayMs);
+    ALOGV("AudioCommandThread() adding set fm volume volume %f", volume);
+    mWaitWorkCV.signal();
+    if (command->mWaitStatus) {
+        command->mCond.wait(mLock);
+        status =  command->mStatus;
+        mWaitWorkCV.signal();
+    }
+    return status;
+}
+#endif
+
 // insertCommand_l() must be called with mLock held
 void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
 {
@@ -950,6 +994,12 @@
             removedCommands.add(command2);
             time = command2->mTime;
         } break;
+#ifdef QCOM_FM_ENABLED
+        case SET_FM_VOLUME: {
+            removedCommands.add(command2);
+            time = command2->mTime;
+        } break;
+#endif
         case START_TONE:
         case STOP_TONE:
         default:
@@ -1026,6 +1076,13 @@
                                                    output, delayMs);
 }
 
+#ifdef QCOM_FM_ENABLED
+status_t AudioPolicyService::setFmVolume(float volume, int delayMs)
+{
+    return mAudioCommandThread->fmVolumeCommand(volume, delayMs);
+}
+#endif
+
 int AudioPolicyService::startTone(audio_policy_tone_t tone,
                                   audio_stream_type_t stream)
 {
@@ -1546,6 +1603,15 @@
     return audioPolicyService->setVoiceVolume(volume, delay_ms);
 }
 
+#ifdef QCOM_FM_ENABLED
+static int aps_set_fm_volume(void *service, float volume, int delay_ms)
+{
+    AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+    return audioPolicyService->setFmVolume(volume, delay_ms);
+}
+#endif
+
 }; // extern "C"
 
 namespace {
@@ -1565,6 +1631,9 @@
         stop_tone             : aps_stop_tone,
         set_voice_volume      : aps_set_voice_volume,
         move_effects          : aps_move_effects,
+#ifdef QCOM_FM_ENABLED
+        set_fm_volume         : aps_set_fm_volume,
+#endif
         load_hw_module        : aps_load_hw_module,
         open_output_on_module : aps_open_output_on_module,
         open_input_on_module  : aps_open_input_on_module,
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 63f9549..1176411 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -1,5 +1,9 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -134,6 +138,9 @@
     virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream);
     virtual status_t stopTone();
     virtual status_t setVoiceVolume(float volume, int delayMs = 0);
+#ifdef QCOM_FM_ENABLED
+    virtual status_t setFmVolume(float volume, int delayMs = 0);
+#endif
 
 private:
                         AudioPolicyService();
@@ -157,7 +164,10 @@
             STOP_TONE,
             SET_VOLUME,
             SET_PARAMETERS,
-            SET_VOICE_VOLUME
+            SET_VOICE_VOLUME,
+#ifdef QCOM_FM_ENABLED
+            SET_FM_VOLUME
+#endif
         };
 
         AudioCommandThread (String8 name);
@@ -178,6 +188,9 @@
                     status_t    parametersCommand(audio_io_handle_t ioHandle,
                                             const char *keyValuePairs, int delayMs = 0);
                     status_t    voiceVolumeCommand(float volume, int delayMs = 0);
+#ifdef QCOM_FM_ENABLED
+                    status_t    fmVolumeCommand(float volume, int delayMs = 0);
+#endif
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
 
     private:
@@ -222,6 +235,13 @@
             float mVolume;
         };
 
+#ifdef QCOM_FM_ENABLED
+        class FmVolumeData {
+        public:
+            float mVolume;
+        };
+#endif
+
         Mutex   mLock;
         Condition mWaitWorkCV;
         Vector <AudioCommand *> mAudioCommands; // list of pending commands