Merge "OMX codecs: send out EOS when input buffer has non-zero data and EOS."
diff --git a/camera/Android.bp b/camera/Android.bp
index d49dd2c..64185e1 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -21,7 +21,6 @@
         export_aidl_headers: true,
         local_include_dirs: ["aidl"],
         include_dirs: [
-            "frameworks/base/core/java",
             "frameworks/native/aidl/gui",
         ],
     },
@@ -76,4 +75,5 @@
         "-Wall",
         "-Wextra",
     ],
+
 }
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index e1718eb..e56f675 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -958,11 +958,9 @@
 
 void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused)
 {
-    mEventLock.lock();
-    mListener.clear();
-    mEventLock.unlock();
-
     Mutex::Autolock autoLock(mLock);
+    setListener(NULL);
+    mPlugin->setListener(NULL);
     mPlugin.clear();
 }
 
diff --git a/media/libaaudio/examples/input_monitor/static/Android.mk b/media/libaaudio/examples/input_monitor/static/Android.mk
index 45b740a..80a3906 100644
--- a/media/libaaudio/examples/input_monitor/static/Android.mk
+++ b/media/libaaudio/examples/input_monitor/static/Android.mk
@@ -12,7 +12,7 @@
 
 LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
                           libbinder libcutils libutils \
-                          libaudioclient liblog libtinyalsa
+                          libaudioclient liblog libtinyalsa libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 
 LOCAL_MODULE := input_monitor
@@ -30,7 +30,7 @@
 
 LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
                           libbinder libcutils libutils \
-                          libaudioclient liblog
+                          libaudioclient liblog libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 
 LOCAL_MODULE := input_monitor_callback
diff --git a/media/libaaudio/examples/write_sine/static/Android.mk b/media/libaaudio/examples/write_sine/static/Android.mk
index bf1b274..1f8dcd9 100644
--- a/media/libaaudio/examples/write_sine/static/Android.mk
+++ b/media/libaaudio/examples/write_sine/static/Android.mk
@@ -13,7 +13,7 @@
 
 LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
                           libbinder libcutils libutils \
-                          libaudioclient liblog libtinyalsa
+                          libaudioclient liblog libtinyalsa libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 
 LOCAL_MODULE := write_sine
@@ -31,7 +31,7 @@
 
 LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
                           libbinder libcutils libutils \
-                          libaudioclient liblog
+                          libaudioclient liblog libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 
 LOCAL_MODULE := write_sine_callback
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 4b8da79..e1886ac 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -42,7 +42,6 @@
  * and would accept whatever it was given.
  */
 #define AAUDIO_UNSPECIFIED           0
-#define AAUDIO_DEVICE_UNSPECIFIED    0
 
 enum {
     AAUDIO_DIRECTION_OUTPUT,
@@ -63,14 +62,14 @@
     AAUDIO_ERROR_BASE = -900, // TODO review
     AAUDIO_ERROR_DISCONNECTED,
     AAUDIO_ERROR_ILLEGAL_ARGUMENT,
-    AAUDIO_ERROR_INCOMPATIBLE,
-    AAUDIO_ERROR_INTERNAL, // an underlying API returned an error code
+    // reserved
+    AAUDIO_ERROR_INTERNAL = AAUDIO_ERROR_ILLEGAL_ARGUMENT + 2,
     AAUDIO_ERROR_INVALID_STATE,
-    AAUDIO_ERROR_UNEXPECTED_STATE,
-    AAUDIO_ERROR_UNEXPECTED_VALUE,
-    AAUDIO_ERROR_INVALID_HANDLE,
-    AAUDIO_ERROR_INVALID_QUERY,
-    AAUDIO_ERROR_UNIMPLEMENTED,
+    // reserved
+    // reserved
+    AAUDIO_ERROR_INVALID_HANDLE = AAUDIO_ERROR_INVALID_STATE + 3,
+    // reserved
+    AAUDIO_ERROR_UNIMPLEMENTED = AAUDIO_ERROR_INVALID_HANDLE + 2,
     AAUDIO_ERROR_UNAVAILABLE,
     AAUDIO_ERROR_NO_FREE_HANDLES,
     AAUDIO_ERROR_NO_MEMORY,
@@ -191,11 +190,11 @@
  * Request an audio device identified device using an ID.
  * On Android, for example, the ID could be obtained from the Java AudioManager.
  *
- * The default, if you do not call this function, is AAUDIO_DEVICE_UNSPECIFIED,
+ * The default, if you do not call this function, is AAUDIO_UNSPECIFIED,
  * in which case the primary device will be used.
  *
  * @param builder reference provided by AAudio_createStreamBuilder()
- * @param deviceId device identifier or AAUDIO_DEVICE_UNSPECIFIED
+ * @param deviceId device identifier or AAUDIO_UNSPECIFIED
  */
 AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder,
                                                      int32_t deviceId);
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index a452c1d..28c4d7f 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -18,6 +18,7 @@
     frameworks/native/media/libaaudio/include/include \
     frameworks/av/media/libaaudio/include \
     frameworks/native/include \
+    frameworks/av/media/libaudioclient/include \
     $(LOCAL_PATH) \
     $(LOCAL_PATH)/binding \
     $(LOCAL_PATH)/client \
@@ -120,6 +121,6 @@
 # AAUDIO_API is used to explicitly export a function or a variable as a visible symbol.
 LOCAL_CFLAGS += -DAAUDIO_API='__attribute__((visibility("default")))'
 
-LOCAL_SHARED_LIBRARIES := libaudioclient liblog libcutils libutils libbinder
+LOCAL_SHARED_LIBRARIES := libaudioclient liblog libcutils libutils libbinder libaudiomanager
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.h b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
index acb6744..144595a 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.h
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
@@ -91,7 +91,7 @@
     void dump() const;
 
 private:
-    int32_t               mDeviceId        = AAUDIO_DEVICE_UNSPECIFIED;
+    int32_t               mDeviceId        = AAUDIO_UNSPECIFIED;
     int32_t               mSampleRate      = AAUDIO_UNSPECIFIED;
     int32_t               mSamplesPerFrame = AAUDIO_UNSPECIFIED;
     aaudio_sharing_mode_t mSharingMode     = AAUDIO_SHARING_MODE_SHARED;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index ca36e1a..3a827f0 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -81,7 +81,7 @@
     }
 
     // We have to do volume scaling. So we prefer FLOAT format.
-    if (getFormat() == AAUDIO_UNSPECIFIED) {
+    if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
         setFormat(AAUDIO_FORMAT_PCM_FLOAT);
     }
     // Request FLOAT for the shared mixer.
@@ -480,7 +480,7 @@
         default:
             ALOGE("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
                  (int) message.what);
-            result = AAUDIO_ERROR_UNEXPECTED_VALUE;
+            result = AAUDIO_ERROR_INTERNAL;
             break;
         }
     }
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 04bf2de..76f98fa 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -51,13 +51,13 @@
         AAUDIO_CASE_ENUM(AAUDIO_OK);
         AAUDIO_CASE_ENUM(AAUDIO_ERROR_DISCONNECTED);
         AAUDIO_CASE_ENUM(AAUDIO_ERROR_ILLEGAL_ARGUMENT);
-        AAUDIO_CASE_ENUM(AAUDIO_ERROR_INCOMPATIBLE);
+        // reserved
         AAUDIO_CASE_ENUM(AAUDIO_ERROR_INTERNAL);
         AAUDIO_CASE_ENUM(AAUDIO_ERROR_INVALID_STATE);
-        AAUDIO_CASE_ENUM(AAUDIO_ERROR_UNEXPECTED_STATE);
-        AAUDIO_CASE_ENUM(AAUDIO_ERROR_UNEXPECTED_VALUE);
+        // reserved
+        // reserved
         AAUDIO_CASE_ENUM(AAUDIO_ERROR_INVALID_HANDLE);
-        AAUDIO_CASE_ENUM(AAUDIO_ERROR_INVALID_QUERY);
+         // reserved
         AAUDIO_CASE_ENUM(AAUDIO_ERROR_UNIMPLEMENTED);
         AAUDIO_CASE_ENUM(AAUDIO_ERROR_UNAVAILABLE);
         AAUDIO_CASE_ENUM(AAUDIO_ERROR_NO_FREE_HANDLES);
@@ -114,7 +114,7 @@
 
 AAUDIO_API aaudio_result_t AAudio_createStreamBuilder(AAudioStreamBuilder** builder)
 {
-    AudioStreamBuilder *audioStreamBuilder =  new AudioStreamBuilder();
+    AudioStreamBuilder *audioStreamBuilder =  new(std::nothrow) AudioStreamBuilder();
     if (audioStreamBuilder == nullptr) {
         return AAUDIO_ERROR_NO_MEMORY;
     }
@@ -235,7 +235,7 @@
         delete streamBuilder;
         return AAUDIO_OK;
     }
-    return AAUDIO_ERROR_INVALID_HANDLE;
+    return AAUDIO_ERROR_NULL;
 }
 
 AAUDIO_API aaudio_result_t  AAudioStream_close(AAudioStream* stream)
@@ -247,7 +247,7 @@
         delete audioStream;
         return AAUDIO_OK;
     }
-    return AAUDIO_ERROR_INVALID_HANDLE;
+    return AAUDIO_ERROR_NULL;
 }
 
 AAUDIO_API aaudio_result_t  AAudioStream_requestStart(AAudioStream* stream)
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 03f1a5c..e1e3c55 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -92,7 +92,7 @@
             break;
         default:
             ALOGE("AudioStream::open(): illegal performanceMode %d", mPerformanceMode);
-            return AAUDIO_ERROR_UNEXPECTED_VALUE;
+            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
     }
 
     return AAUDIO_OK;
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index 476f909..fd416c4 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -168,7 +168,7 @@
 private:
     int32_t                    mSamplesPerFrame = AAUDIO_UNSPECIFIED;
     int32_t                    mSampleRate = AAUDIO_UNSPECIFIED;
-    int32_t                    mDeviceId = AAUDIO_DEVICE_UNSPECIFIED;
+    int32_t                    mDeviceId = AAUDIO_UNSPECIFIED;
     aaudio_sharing_mode_t      mSharingMode = AAUDIO_SHARING_MODE_SHARED;
     bool                       mSharingModeMatchRequired = false; // must match sharing mode requested
     aaudio_format_t            mFormat = AAUDIO_FORMAT_UNSPECIFIED;
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index dfac4fb..dd5e3c0 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -133,7 +133,7 @@
             break;
         default:
             ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
-            return AAUDIO_ERROR_UNEXPECTED_VALUE;
+            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
             break;
     }
     status_t status = extendedTimestamp->getBestTimestamp(framePosition, timeNanoseconds, timebase);
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 0918c2b..156e83d 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -64,7 +64,7 @@
                         : builder.getBufferCapacity();
 
     // TODO implement an unspecified Android format then use that.
-    audio_format_t format = (getFormat() == AAUDIO_UNSPECIFIED)
+    audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED)
             ? AUDIO_FORMAT_PCM_FLOAT
             : AAudioConvert_aaudioToAndroidDataFormat(getFormat());
 
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index a0c1d45..7e39908 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -143,6 +143,9 @@
         return AAudioConvert_androidToAAudioResult(status);
     }
 
+    //TrackPlayerBase init
+    init(mAudioTrack.get(), PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
+
     // Get the actual values from the AudioTrack.
     setSamplesPerFrame(mAudioTrack->channelCount());
     aaudio_format_t aaudioFormat =
@@ -194,7 +197,7 @@
 aaudio_result_t AudioStreamTrack::close()
 {
     if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
-        mAudioTrack.clear();
+        destroy();
         setState(AAUDIO_STREAM_STATE_CLOSED);
     }
     mFixedBlockReader.close();
@@ -232,7 +235,7 @@
         return AAudioConvert_androidToAAudioResult(err);
     }
 
-    err = mAudioTrack->start();
+    err = startWithStatus();
     if (err != OK) {
         return AAudioConvert_androidToAAudioResult(err);
     } else {
@@ -256,7 +259,7 @@
     }
     onStop();
     setState(AAUDIO_STREAM_STATE_PAUSING);
-    mAudioTrack->pause();
+    pause();
     status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
     if (err != OK) {
         return AAudioConvert_androidToAAudioResult(err);
@@ -288,7 +291,7 @@
     onStop();
     setState(AAUDIO_STREAM_STATE_STOPPING);
     incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
-    mAudioTrack->stop();
+    stop();
     mFramesWritten.reset32();
     return AAUDIO_OK;
 }
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 990f005..ff429ea 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -18,7 +18,7 @@
 #define LEGACY_AUDIO_STREAM_TRACK_H
 
 #include <math.h>
-#include <media/AudioTrack.h>
+#include <media/TrackPlayerBase.h>
 #include <aaudio/AAudio.h>
 
 #include "AudioStreamBuilder.h"
@@ -32,7 +32,7 @@
 /**
  * Internal stream that uses the legacy AudioTrack path.
  */
-class AudioStreamTrack : public AudioStreamLegacy {
+class AudioStreamTrack : public AudioStreamLegacy, public android::TrackPlayerBase {
 public:
     AudioStreamTrack();
 
@@ -78,7 +78,6 @@
 
 private:
 
-    android::sp<android::AudioTrack> mAudioTrack;
     // adapts between variable sized blocks and fixed size blocks
     FixedBlockReader                 mFixedBlockReader;
 
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 2c7634e..164784d 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -212,10 +212,8 @@
         status = DEAD_OBJECT;
         break;
     case AAUDIO_ERROR_INVALID_STATE:
-    case AAUDIO_ERROR_UNEXPECTED_STATE:
         status = INVALID_OPERATION;
         break;
-    case AAUDIO_ERROR_UNEXPECTED_VALUE:
     case AAUDIO_ERROR_INVALID_RATE:
     case AAUDIO_ERROR_INVALID_FORMAT:
     case AAUDIO_ERROR_ILLEGAL_ARGUMENT:
@@ -229,9 +227,7 @@
         status = UNEXPECTED_NULL;
         break;
     // TODO translate these result codes
-    case AAUDIO_ERROR_INCOMPATIBLE:
     case AAUDIO_ERROR_INTERNAL:
-    case AAUDIO_ERROR_INVALID_QUERY:
     case AAUDIO_ERROR_UNIMPLEMENTED:
     case AAUDIO_ERROR_UNAVAILABLE:
     case AAUDIO_ERROR_NO_FREE_HANDLES:
@@ -265,7 +261,7 @@
             result = AAUDIO_ERROR_NULL;
             break;
         case BAD_VALUE:
-            result = AAUDIO_ERROR_UNEXPECTED_VALUE;
+            result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
             break;
     case WOULD_BLOCK:
         result = AAUDIO_ERROR_WOULD_BLOCK;
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index fba81f2..afcdebf 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -7,7 +7,7 @@
     frameworks/av/media/libaaudio/src
 LOCAL_SRC_FILES:= test_handle_tracker.cpp
 LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
-                          libcutils liblog libmedia libutils
+                          libcutils liblog libmedia libutils libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 LOCAL_MODULE := test_handle_tracker
 include $(BUILD_NATIVE_TEST)
@@ -19,7 +19,7 @@
     frameworks/av/media/libaaudio/src
 LOCAL_SRC_FILES:= test_marshalling.cpp
 LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
-                          libcutils liblog libmedia libutils
+                          libcutils liblog libmedia libutils libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 LOCAL_MODULE := test_aaudio_marshalling
 include $(BUILD_NATIVE_TEST)
@@ -31,7 +31,7 @@
     frameworks/av/media/libaaudio/src
 LOCAL_SRC_FILES:= test_block_adapter.cpp
 LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
-                          libcutils liblog libmedia libutils
+                          libcutils liblog libmedia libutils libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 LOCAL_MODULE := test_block_adapter
 include $(BUILD_NATIVE_TEST)
@@ -43,7 +43,7 @@
     frameworks/av/media/libaaudio/src
 LOCAL_SRC_FILES:= test_linear_ramp.cpp
 LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
-                          libcutils liblog libmedia libutils
+                          libcutils liblog libmedia libutils libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 LOCAL_MODULE := test_linear_ramp
 include $(BUILD_NATIVE_TEST)
@@ -55,7 +55,7 @@
     frameworks/av/media/libaaudio/src
 LOCAL_SRC_FILES:= test_open_params.cpp
 LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
-                          libcutils liblog libmedia libutils
+                          libcutils liblog libmedia libutils libaudiomanager
 LOCAL_STATIC_LIBRARIES := libaaudio
 LOCAL_MODULE := test_open_params
 include $(BUILD_NATIVE_TEST)
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 4c24401..61c946c 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -22,6 +22,8 @@
         "IEffect.cpp",
         "IEffectClient.cpp",
         "ToneGenerator.cpp",
+        "PlayerBase.cpp",
+        "TrackPlayerBase.cpp",
     ],
     shared_libs: [
         "liblog",
@@ -30,6 +32,7 @@
         "libbinder",
         "libdl",
         "libaudioutils",
+        "libaudiomanager",
     ],
     export_shared_lib_headers: ["libbinder"],
 
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 605b19e..409c9a3 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1247,9 +1247,27 @@
     return mStreamType;
 }
 
+uint32_t AudioTrack::latency()
+{
+    AutoMutex lock(mLock);
+    updateLatency_l();
+    return mLatency;
+}
+
 // -------------------------------------------------------------------------
 
 // must be called with mLock held
+void AudioTrack::updateLatency_l()
+{
+    status_t status = AudioSystem::getLatency(mOutput, &mAfLatency);
+    if (status != NO_ERROR) {
+        ALOGW("getLatency(%d) failed status %d", mOutput, status);
+    } else {
+        // FIXME don't believe this lie
+        mLatency = mAfLatency + (1000 * mFrameCount) / mSampleRate;
+    }
+}
+
 status_t AudioTrack::createTrack_l()
 {
     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -1544,9 +1562,7 @@
     }
 
     mAudioTrack->attachAuxEffect(mAuxEffectId);
-    // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack)
-    // FIXME don't believe this lie
-    mLatency = mAfLatency + (1000*frameCount) / mSampleRate;
+    updateLatency_l();  // this refetches mAfLatency and sets mLatency
 
     mFrameCount = frameCount;
     // If IAudioTrack is re-created, don't let the requested frameCount
@@ -2318,8 +2334,9 @@
     return mPosition;
 }
 
-bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const
+bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed)
 {
+    updateLatency_l();
     // applicable for mixing tracks only (not offloaded or direct)
     if (mStaticProxy != 0) {
         return true; // static tracks do not have issues with buffer sizing.
@@ -2327,9 +2344,14 @@
     const size_t minFrameCount =
             calculateMinFrameCount(mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed
                 /*, 0 mNotificationsPerBufferReq*/);
-    ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu  minFrameCount %zu",
+    const bool allowed = mFrameCount >= minFrameCount;
+    ALOGD_IF(!allowed,
+            "isSampleRateSpeedAllowed_l denied "
+            "mAfLatency:%u  mAfFrameCount:%zu  mAfSampleRate:%u  sampleRate:%u  speed:%f "
+            "mFrameCount:%zu < minFrameCount:%zu",
+            mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed,
             mFrameCount, minFrameCount);
-    return mFrameCount >= minFrameCount;
+    return allowed;
 }
 
 status_t AudioTrack::setParameters(const String8& keyValuePairs)
@@ -2473,6 +2495,7 @@
             status = ets.getBestTimestamp(&timestamp, &location);
 
             if (status == OK) {
+                updateLatency_l();
                 // It is possible that the best location has moved from the kernel to the server.
                 // In this case we adjust the position from the previous computed latency.
                 if (location == ExtendedTimestamp::LOCATION_SERVER) {
diff --git a/media/libaudioclient/PlayerBase.cpp b/media/libaudioclient/PlayerBase.cpp
new file mode 100644
index 0000000..cbef1b3
--- /dev/null
+++ b/media/libaudioclient/PlayerBase.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2017 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 <binder/IServiceManager.h>
+#include <media/PlayerBase.h>
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+namespace android {
+
+//--------------------------------------------------------------------------------------------------
+PlayerBase::PlayerBase() : BnPlayer(),
+        mPanMultiplierL(1.0f), mPanMultiplierR(1.0f),
+        mVolumeMultiplierL(1.0f), mVolumeMultiplierR(1.0f),
+        mPIId(PLAYER_PIID_INVALID), mLastReportedEvent(PLAYER_STATE_UNKNOWN)
+{
+    ALOGD("PlayerBase::PlayerBase()");
+    // use checkService() to avoid blocking if audio service is not up yet
+    sp<IBinder> binder = defaultServiceManager()->checkService(String16("audio"));
+    if (binder == 0) {
+        ALOGE("PlayerBase(): binding to audio service failed, service up?");
+    } else {
+        mAudioManager = interface_cast<IAudioManager>(binder);
+    }
+}
+
+
+PlayerBase::~PlayerBase() {
+    ALOGD("PlayerBase::~PlayerBase()");
+    baseDestroy();
+}
+
+void PlayerBase::init(player_type_t playerType, audio_usage_t usage) {
+    if (mAudioManager == 0) {
+                ALOGE("AudioPlayer realize: no audio service, player will not be registered");
+    } else {
+        mPIId = mAudioManager->trackPlayer(playerType, usage, AUDIO_CONTENT_TYPE_UNKNOWN, this);
+    }
+}
+
+void PlayerBase::baseDestroy() {
+    serviceReleasePlayer();
+    if (mAudioManager != 0) {
+        mAudioManager.clear();
+    }
+}
+
+//------------------------------------------------------------------------------
+void PlayerBase::servicePlayerEvent(player_state_t event) {
+    if (mAudioManager != 0) {
+        // only report state change
+        Mutex::Autolock _l(mPlayerStateLock);
+        if (event != mLastReportedEvent
+                && mPIId != PLAYER_PIID_INVALID) {
+            mLastReportedEvent = event;
+            mAudioManager->playerEvent(mPIId, event);
+        }
+    }
+}
+
+void PlayerBase::serviceReleasePlayer() {
+    if (mAudioManager != 0
+            && mPIId != PLAYER_PIID_INVALID) {
+        mAudioManager->releasePlayer(mPIId);
+    }
+}
+
+//FIXME temporary method while some AudioTrack state is outside of this class
+void PlayerBase::reportEvent(player_state_t event) {
+    servicePlayerEvent(event);
+}
+
+status_t PlayerBase::startWithStatus() {
+    status_t status = playerStart();
+    if (status == NO_ERROR) {
+        ALOGD("PlayerBase::start() from IPlayer");
+        servicePlayerEvent(PLAYER_STATE_STARTED);
+    } else {
+        ALOGD("PlayerBase::start() no AudioTrack to start from IPlayer");
+    }
+    return status;
+}
+
+//------------------------------------------------------------------------------
+// Implementation of IPlayer
+void PlayerBase::start() {
+    (void)startWithStatus();
+}
+
+void PlayerBase::pause() {
+    if (playerPause() == NO_ERROR) {
+        ALOGD("PlayerBase::pause() from IPlayer");
+        servicePlayerEvent(PLAYER_STATE_PAUSED);
+    } else {
+        ALOGD("PlayerBase::pause() no AudioTrack to pause from IPlayer");
+    }
+}
+
+
+void PlayerBase::stop() {
+    if (playerStop() == NO_ERROR) {
+        ALOGD("PlayerBase::stop() from IPlayer");
+        servicePlayerEvent(PLAYER_STATE_STOPPED);
+    } else {
+        ALOGD("PlayerBase::stop() no AudioTrack to stop from IPlayer");
+    }
+}
+
+void PlayerBase::setVolume(float vol) {
+    {
+        Mutex::Autolock _l(mSettingsLock);
+        mVolumeMultiplierL = vol;
+        mVolumeMultiplierR = vol;
+    }
+    if (playerSetVolume() == NO_ERROR) {
+        ALOGD("PlayerBase::setVolume() from IPlayer");
+    } else {
+        ALOGD("PlayerBase::setVolume() no AudioTrack for volume control from IPlayer");
+    }
+}
+
+void PlayerBase::setPan(float pan) {
+    {
+        Mutex::Autolock _l(mSettingsLock);
+        pan = min(max(-1.0f, pan), 1.0f);
+        if (pan >= 0.0f) {
+            mPanMultiplierL = 1.0f - pan;
+            mPanMultiplierR = 1.0f;
+        } else {
+            mPanMultiplierL = 1.0f;
+            mPanMultiplierR = 1.0f + pan;
+        }
+    }
+    if (playerSetVolume() == NO_ERROR) {
+        ALOGD("PlayerBase::setPan() from IPlayer");
+    } else {
+        ALOGD("PlayerBase::setPan() no AudioTrack for volume control from IPlayer");
+    }
+}
+
+void PlayerBase::setStartDelayMs(int32_t delayMs __unused) {
+    ALOGW("setStartDelay() is not supported");
+}
+
+void PlayerBase::applyVolumeShaper(
+        const sp<VolumeShaper::Configuration>& configuration  __unused,
+        const sp<VolumeShaper::Operation>& operation __unused) {
+    ALOGW("applyVolumeShaper() is not supported");
+}
+
+status_t PlayerBase::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    return BnPlayer::onTransact(code, data, reply, flags);
+}
+
+} // namespace android
diff --git a/media/libaudioclient/TrackPlayerBase.cpp b/media/libaudioclient/TrackPlayerBase.cpp
new file mode 100644
index 0000000..48cd803
--- /dev/null
+++ b/media/libaudioclient/TrackPlayerBase.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/TrackPlayerBase.h>
+
+namespace android {
+
+//--------------------------------------------------------------------------------------------------
+TrackPlayerBase::TrackPlayerBase() : PlayerBase(),
+        mPlayerVolumeL(1.0f), mPlayerVolumeR(1.0f)
+{
+    ALOGD("TrackPlayerBase::TrackPlayerBase()");
+}
+
+
+TrackPlayerBase::~TrackPlayerBase() {
+    ALOGD("TrackPlayerBase::~TrackPlayerBase()");
+    doDestroy();
+}
+
+void TrackPlayerBase::init(AudioTrack* pat, player_type_t playerType, audio_usage_t usage) {
+    PlayerBase::init(playerType, usage);
+    mAudioTrack = pat;
+}
+
+void TrackPlayerBase::destroy() {
+    doDestroy();
+    baseDestroy();
+}
+
+void TrackPlayerBase::doDestroy() {
+    if (mAudioTrack != 0) {
+        mAudioTrack->stop();
+        // Note that there may still be another reference in post-unlock phase of SetPlayState
+        mAudioTrack.clear();
+    }
+}
+
+void TrackPlayerBase::setPlayerVolume(float vl, float vr) {
+    {
+        Mutex::Autolock _l(mSettingsLock);
+        mPlayerVolumeL = vl;
+        mPlayerVolumeR = vr;
+    }
+    doSetVolume();
+}
+
+//------------------------------------------------------------------------------
+// Implementation of IPlayer
+status_t TrackPlayerBase::playerStart() {
+    status_t status = NO_INIT;
+    if (mAudioTrack != 0) {
+        status = mAudioTrack->start();
+    }
+    return status;
+}
+
+status_t TrackPlayerBase::playerPause() {
+    status_t status = NO_INIT;
+    if (mAudioTrack != 0) {
+        mAudioTrack->pause();
+        status = NO_ERROR;
+    }
+    return status;
+}
+
+
+status_t TrackPlayerBase::playerStop() {
+    status_t status = NO_INIT;
+    if (mAudioTrack != 0) {
+        mAudioTrack->stop();
+        status = NO_ERROR;
+    }
+    return status;
+}
+
+status_t TrackPlayerBase::playerSetVolume() {
+    return doSetVolume();
+}
+
+status_t TrackPlayerBase::doSetVolume() {
+    status_t status = NO_INIT;
+    if (mAudioTrack != 0) {
+        float tl = mPlayerVolumeL * mPanMultiplierL * mVolumeMultiplierL;
+        float tr = mPlayerVolumeR * mPanMultiplierR * mVolumeMultiplierR;
+        mAudioTrack->setVolume(tl, tr);
+        status = NO_ERROR;
+    }
+    return status;
+}
+
+
+void TrackPlayerBase::applyVolumeShaper(
+        const sp<VolumeShaper::Configuration>& configuration,
+        const sp<VolumeShaper::Operation>& operation) {
+    if (mAudioTrack != 0) {
+        ALOGD("TrackPlayerBase::applyVolumeShaper() from IPlayer");
+        VolumeShaper::Status status = mAudioTrack->applyVolumeShaper(configuration, operation);
+        if (status < 0) { // a non-negative value is the volume shaper id.
+            ALOGE("TrackPlayerBase::applyVolumeShaper() failed with status %d", status);
+        }
+    } else {
+        ALOGD("TrackPlayerBase::applyVolumeShaper()"
+                " no AudioTrack for volume control from IPlayer");
+    }
+}
+
+} // namespace android
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index a4c8d53..5d73df3 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -326,7 +326,7 @@
      * This includes the latency due to AudioTrack buffer size, AudioMixer (if any)
      * and audio hardware driver.
      */
-            uint32_t    latency() const     { return mLatency; }
+            uint32_t    latency();
 
     /* Returns the number of application-level buffer underruns
      * since the AudioTrack was created.
@@ -927,6 +927,8 @@
 
             // caller must hold lock on mLock for all _l methods
 
+            void updateLatency_l(); // updates mAfLatency and mLatency from AudioSystem cache
+
             status_t createTrack_l();
 
             // can only be called when mState != STATE_ACTIVE
@@ -962,7 +964,7 @@
             Modulo<uint32_t> updateAndGetPosition_l();
 
             // check sample rate and speed is compatible with AudioTrack
-            bool     isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const;
+            bool     isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed);
 
             void     restartIfDisabled();
 
diff --git a/media/libaudioclient/include/media/PlayerBase.h b/media/libaudioclient/include/media/PlayerBase.h
new file mode 100644
index 0000000..fe1db7b
--- /dev/null
+++ b/media/libaudioclient/include/media/PlayerBase.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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_PLAYER_BASE_H__
+#define __ANDROID_PLAYER_BASE_H__
+
+#include <audiomanager/IPlayer.h>
+#include <audiomanager/AudioManager.h>
+#include <audiomanager/IAudioManager.h>
+
+
+namespace android {
+
+class PlayerBase : public BnPlayer
+{
+public:
+    explicit PlayerBase();
+    virtual ~PlayerBase();
+
+    virtual void destroy() = 0;
+
+    //IPlayer implementation
+    virtual void start();
+    virtual void pause();
+    virtual void stop();
+    virtual void setVolume(float vol);
+    virtual void setPan(float pan);
+    virtual void setStartDelayMs(int32_t delayMs);
+    virtual void applyVolumeShaper(
+            const sp<VolumeShaper::Configuration>& configuration,
+            const sp<VolumeShaper::Operation>& operation) override;
+
+    virtual status_t onTransact(
+                uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+
+
+            status_t startWithStatus();
+
+            //FIXME temporary method while some player state is outside of this class
+            void reportEvent(player_state_t event);
+
+protected:
+
+            void init(player_type_t playerType, audio_usage_t usage);
+            void baseDestroy();
+
+    //IPlayer methods handlers for derived classes
+    virtual status_t playerStart()  { return NO_ERROR; }
+    virtual status_t playerPause()  { return NO_ERROR; }
+    virtual status_t playerStop()  { return NO_ERROR; }
+    virtual status_t playerSetVolume()  { return NO_ERROR; }
+
+    // mutex for IPlayer volume and pan, and player-specific volume
+    Mutex mSettingsLock;
+
+    // volume multipliers coming from the IPlayer volume and pan controls
+    float mPanMultiplierL, mPanMultiplierR;
+    float mVolumeMultiplierL, mVolumeMultiplierR;
+
+private:
+            // report events to AudioService
+            void servicePlayerEvent(player_state_t event);
+            void serviceReleasePlayer();
+
+    // native interface to AudioService
+    android::sp<android::IAudioManager> mAudioManager;
+
+    // player interface ID, uniquely identifies the player in the system
+    audio_unique_id_t mPIId;
+
+    // Mutex for state reporting
+    Mutex mPlayerStateLock;
+    player_state_t mLastReportedEvent;
+};
+
+} // namespace android
+
+#endif /* __ANDROID_PLAYER_BASE_H__ */
diff --git a/media/libaudioclient/include/media/TrackPlayerBase.h b/media/libaudioclient/include/media/TrackPlayerBase.h
new file mode 100644
index 0000000..2d113c0
--- /dev/null
+++ b/media/libaudioclient/include/media/TrackPlayerBase.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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_TRACK_PLAYER_BASE_H__
+#define __ANDROID_TRACK_PLAYER_BASE_H__
+
+#include <media/AudioTrack.h>
+#include <media/PlayerBase.h>
+
+namespace android {
+
+class TrackPlayerBase : public PlayerBase
+{
+public:
+    explicit TrackPlayerBase();
+    virtual ~TrackPlayerBase();
+
+            void init(AudioTrack* pat, player_type_t playerType, audio_usage_t usage);
+    virtual void destroy();
+
+    //IPlayer implementation
+    virtual void applyVolumeShaper(
+            const sp<VolumeShaper::Configuration>& configuration,
+            const sp<VolumeShaper::Operation>& operation);
+
+    //FIXME move to protected field, so far made public to minimize changes to AudioTrack logic
+    sp<AudioTrack> mAudioTrack;
+
+            void setPlayerVolume(float vl, float vr);
+
+protected:
+
+    //PlayerBase virtuals
+    virtual status_t playerStart();
+    virtual status_t playerPause();
+    virtual status_t playerStop();
+    virtual status_t playerSetVolume();
+
+private:
+            void doDestroy();
+            status_t doSetVolume();
+
+    // volume coming from the player volume API
+    float mPlayerVolumeL, mPlayerVolumeR;
+};
+
+} // namespace android
+
+#endif /* __ANDROID_TRACK_PLAYER_BASE_H__ */
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 685065a..b976721 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -1023,9 +1023,12 @@
         return NO_INIT;
     }
 
-    // Only allowing it in player's prepared state
-    if (!(mCurrentState & MEDIA_PLAYER_PREPARED)) {
-        ALOGE("prepareDrm must only be called in the prepared state.");
+    // Only allowed it in player's preparing/prepared state.
+    // We get here only if MEDIA_DRM_INFO has already arrived (e.g., prepare is half-way through or
+    // completed) so the state change to "prepared" might not have happened yet (e.g., buffering).
+    // Still, we can allow prepareDrm for the use case of being called in OnDrmInfoListener.
+    if (!(mCurrentState & (MEDIA_PLAYER_PREPARING | MEDIA_PLAYER_PREPARED))) {
+        ALOGE("prepareDrm is called in the wrong state (%d).", mCurrentState);
         return INVALID_OPERATION;
     }
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 00a1f9c..d83c406 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1979,7 +1979,8 @@
     // The legacy mDecryptHandle!=NULL check (for FLAG_PROTECTED) is equivalent to mIsDrmProtected.
     notifyFlagsChanged(
             (mIsSecure ? FLAG_SECURE : 0) |
-            (mIsDrmProtected ? FLAG_PROTECTED : 0) |
+            // Setting "protected screen" only for L1: b/38390836
+            (mIsSecure ? FLAG_PROTECTED : 0) |
             FLAG_CAN_PAUSE |
             FLAG_CAN_SEEK_BACKWARD |
             FLAG_CAN_SEEK_FORWARD |
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index b0c6695..4064133 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -222,6 +222,7 @@
     int32_t mFetchTimedTextDataGeneration;
     int64_t mDurationUs;
     bool mAudioIsVorbis;
+    // Secure codec is required.
     bool mIsSecure;
     bool mIsStreaming;
     bool mUIDValid;
@@ -310,7 +311,9 @@
             bool seeking, bool formatChange, media_track_type trackType, Track *track);
 
     // Modular DRM
+    // The source is DRM protected and is prepared for DRM.
     bool mIsDrmProtected;
+    // releaseDrm has been processed.
     bool mIsDrmReleased;
     Vector<String8> mMimes;
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index e7cca27..8ba9c0d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -38,8 +38,8 @@
         FLAG_CAN_SEEK_FORWARD   = 4,  // the "10 sec forward button"
         FLAG_CAN_SEEK           = 8,  // the "seek bar"
         FLAG_DYNAMIC_DURATION   = 16,
-        FLAG_SECURE             = 32,
-        FLAG_PROTECTED          = 64,
+        FLAG_SECURE             = 32, // Secure codec is required.
+        FLAG_PROTECTED          = 64, // The screen needs to be protected (screenshot is disabled).
     };
 
     enum {
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 0e5693d..48ffb01 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -84,10 +84,15 @@
 #define LOG_TAG "NBLog"
 //#define LOG_NDEBUG 0
 
+#include <algorithm>
 #include <climits>
 #include <deque>
+#include <fstream>
+// #include <inttypes.h>
+#include <iostream>
 #include <math.h>
 #include <numeric>
+#include <vector>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -906,6 +911,34 @@
 
 }
 
+// writes sample deltas to file, either truncating or appending
+inline void writeHistToFile(const std::vector<int64_t> &samples, bool append) {
+    // name of file on audioserver
+    static const char* const kName = (char *)"/data/misc/audioserver/sample_results.txt";
+    // stores deltas between the samples
+    std::vector<int64_t> intervals;
+    if (samples.size() == 0) return;
+    for (size_t i = 1; i < samples.size(); ++i) {
+        intervals.push_back(deltaMs(samples[i - 1], samples[i]));
+    }
+    // Deletes maximum value in a histogram. Temp quick fix.
+    // FIXME: need to find root cause of approx. 35th element from the end
+    // consistently being an outlier in the first histogram of a flush
+    // ALOGW("%" PRId64 "before", (int64_t) *(std::max_element(intervals.begin(), intervals.end())));
+    intervals.erase(std::max_element(intervals.begin(), intervals.end()));
+    // ALOGW("%" PRId64 "after", (int64_t) *(std::max_element(intervals.begin(), intervals.end())));
+    std::ofstream ofs;
+    ofs.open(kName, append ? std::ios::app : std::ios::trunc);
+    if (!ofs) {
+        ALOGW("couldn't open file %s", kName);
+        return;
+    }
+    for (size_t i = 0; i < intervals.size(); ++i) {
+        ofs << intervals[i] << "\n";
+    }
+    ofs.close();
+}
+
 void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
 {
   //  CallStack cs(LOG_TAG);
@@ -1056,7 +1089,8 @@
                     if (findGlitch) {
                         alertIfGlitch(hist->second);
                     }
-                    // validateFirstTimestamp(hist->second);
+                    // set file to empty and write data for all histograms in this set
+                    writeHistToFile(hist->second, hist != mHists.begin());
                     drawHistogram(&body, hist->second, true, indent);
                     hist = mHists.erase(hist);
                 } else {
@@ -1079,12 +1113,8 @@
 
         if (!body.isEmpty()) {
             dumpLine(timestamp, body);
-            // deferredTimestamp = false;
         }
     }
-    // if (deferredTimestamp) {
-    //     dumpLine(timestamp, body);
-    // }
 }
 
 void NBLog::Reader::dump(int fd, size_t indent)
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 5b8a0d1..5fcb1fe 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -97,6 +97,7 @@
         "libnativewindow",
 
         "libmedia_helper",
+        "libstagefright_flacdec",
         "libstagefright_foundation",
         "libdl",
         "libRScpp",
@@ -159,6 +160,7 @@
     "codecs/*",
     "colorconversion",
     "filters",
+    "flac/dec",
     "foundation",
     "http",
     "httplive",
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 0bb4dbb..bbdcf0b 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -2967,8 +2967,10 @@
                     int32_t delay, padding;
                     if (sscanf(mLastCommentData,
                                " %*x %x %x %*x", &delay, &padding) == 2) {
-                        if (mLastTrack == NULL)
+                        if (mLastTrack == NULL) {
+                            delete[] buffer;
                             return ERROR_MALFORMED;
+                        }
 
                         mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
                         mLastTrack->meta->setInt32(kKeyEncoderPadding, padding);
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 621c2ce..0aea8e1 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1069,6 +1069,16 @@
         buffer->meta()->setInt32("csd", true);
         buffer->meta()->setInt64("timeUs", 0);
         msg->setBuffer("csd-2", buffer);
+    } else if (meta->findData(kKeyFlacMetadata, &type, &data, &size)) {
+        sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+        if (buffer.get() == NULL || buffer->base() == NULL) {
+            return NO_MEMORY;
+        }
+        memcpy(buffer->data(), data, size);
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-0", buffer);
     } else if (meta->findData(kKeyVp9CodecPrivate, &type, &data, &size)) {
         sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
         if (buffer.get() == NULL || buffer->base() == NULL) {
@@ -1552,6 +1562,7 @@
     { MEDIA_MIMETYPE_AUDIO_VORBIS,      AUDIO_FORMAT_VORBIS },
     { MEDIA_MIMETYPE_AUDIO_OPUS,        AUDIO_FORMAT_OPUS},
     { MEDIA_MIMETYPE_AUDIO_AC3,         AUDIO_FORMAT_AC3},
+    { MEDIA_MIMETYPE_AUDIO_FLAC,        AUDIO_FORMAT_FLAC},
     { 0, AUDIO_FORMAT_INVALID }
 };
 
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 7193435..a745692 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -80,7 +80,16 @@
     for (size_t j = 0; j < sizeOfScalingList; ++j) {
         if (nextScale != 0) {
             signed delta_scale = parseSE(br);
-            nextScale = (lastScale + delta_scale + 256) % 256;
+            // ISO_IEC_14496-10_201402-ITU, 7.4.2.1.1.1, The value of delta_scale
+            // shall be in the range of −128 to +127, inclusive.
+            if (delta_scale < -128) {
+                ALOGW("delta_scale (%d) is below range, capped to -128", delta_scale);
+                delta_scale = -128;
+            } else if (delta_scale > 127) {
+                ALOGW("delta_scale (%d) is above range, capped to 127", delta_scale);
+                delta_scale = 127;
+            }
+            nextScale = (lastScale + (delta_scale + 256)) % 256;
         }
 
         lastScale = (nextScale == 0) ? lastScale : nextScale;
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
new file mode 100644
index 0000000..6ac264d
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -0,0 +1,36 @@
+cc_library_shared {
+    name: "libstagefright_soft_flacdec",
+
+    srcs: [
+        "SoftFlacDecoder.cpp",
+    ],
+
+    include_dirs: [
+        "external/flac/include",
+        "frameworks/av/media/libstagefright/flac/dec",
+        "frameworks/av/media/libstagefright/include",
+        "frameworks/native/include/media/openmax",
+    ],
+
+    cflags: ["-Werror"],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libstagefright_flacdec",
+        "libstagefright_omx",
+        "libstagefright_foundation",
+        "libutils",
+    ],
+}
diff --git a/media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2
diff --git a/media/libstagefright/codecs/flac/dec/NOTICE b/media/libstagefright/codecs/flac/dec/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
new file mode 100644
index 0000000..f89688c
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2017 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 "SoftFlacDecoder"
+#include <utils/Log.h>
+
+#include "SoftFlacDecoder.h"
+#include <OMX_AudioExt.h>
+#include <OMX_IndexExt.h>
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/misc.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftFlacDecoder::SoftFlacDecoder(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mFLACDecoder(NULL),
+      mHasStreamInfo(false),
+      mInputBufferCount(0),
+      mSignalledError(false),
+      mOutputPortSettingsChange(NONE) {
+    ALOGV("ctor:");
+    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+    initPorts();
+    initDecoder();
+}
+
+SoftFlacDecoder::~SoftFlacDecoder() {
+    ALOGV("dtor:");
+}
+
+void SoftFlacDecoder::initPorts() {
+    ALOGV("initPorts:");
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumInputBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/flac");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingFLAC;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumOutputBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 4096 * FLACDecoder::kMaxChannels;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+    addPort(def);
+}
+
+void SoftFlacDecoder::initDecoder() {
+    ALOGV("initDecoder:");
+    mFLACDecoder = FLACDecoder::Create();
+    if (mFLACDecoder == NULL) {
+        ALOGE("initDecoder: failed to create FLACDecoder");
+        mSignalledError = true;
+    }
+}
+
+OMX_ERRORTYPE SoftFlacDecoder::initCheck() const {
+    if (mSignalledError) {
+        if (mFLACDecoder == NULL) {
+            ALOGE("initCheck: failed due to NULL encoder");
+            return OMX_ErrorDynamicResourcesUnavailable;
+        }
+        return OMX_ErrorUndefined;
+    }
+
+    return SimpleSoftOMXComponent::initCheck();
+}
+
+OMX_ERRORTYPE SoftFlacDecoder::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    ALOGV("internalGetParameter: index(%x)", index);
+    switch ((OMX_U32)index) {
+        case OMX_IndexParamAudioFlac:
+        {
+            OMX_AUDIO_PARAM_FLACTYPE *flacParams =
+                (OMX_AUDIO_PARAM_FLACTYPE *)params;
+
+            if (!isValidOMXParam(flacParams)) {
+                ALOGE("internalGetParameter(OMX_IndexParamAudioFlac): invalid omx params");
+                return OMX_ErrorBadParameter;
+            }
+
+            if (flacParams->nPortIndex != 0) {
+                ALOGE("internalGetParameter(OMX_IndexParamAudioFlac): bad port index");
+                return OMX_ErrorBadPortIndex;
+            }
+
+            flacParams->nCompressionLevel = 0;
+
+            if (isConfigured()) {
+                flacParams->nChannels = mStreamInfo.channels;
+                flacParams->nSampleRate = mStreamInfo.sample_rate;
+            } else {
+                flacParams->nChannels = 1;
+                flacParams->nSampleRate = 44100;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (!isValidOMXParam(pcmParams)) {
+                ALOGE("internalGetParameter(OMX_IndexParamAudioPcm): invalid omx params");
+                return OMX_ErrorBadParameter;
+            }
+
+            if (pcmParams->nPortIndex != 1) {
+                ALOGE("internalGetParameter(OMX_IndexParamAudioPcm): bad port index");
+                return OMX_ErrorBadPortIndex;
+            }
+
+            pcmParams->eNumData = OMX_NumericalDataSigned;
+            pcmParams->eEndian = OMX_EndianBig;
+            pcmParams->bInterleaved = OMX_TRUE;
+            pcmParams->nBitPerSample = 16;
+            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+            pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF;
+            pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
+            pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS;
+            pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS;
+
+            if (isConfigured()) {
+                pcmParams->nChannels = mStreamInfo.channels;
+                pcmParams->nSamplingRate = mStreamInfo.sample_rate;
+            } else {
+                pcmParams->nChannels = 1;
+                pcmParams->nSamplingRate = 44100;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftFlacDecoder::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    ALOGV("internalSetParameter: index(%x)", (int)index);
+    switch ((int)index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (!isValidOMXParam(roleParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
+            if (strncmp((const char *)roleParams->cRole,
+                        "audio_decoder.flac",
+                        OMX_MAX_STRINGNAME_SIZE - 1) != 0) {
+                return OMX_ErrorInvalidComponentName;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioPcm:
+        {
+            const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (!isValidOMXParam(pcmParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
+            if (pcmParams->nPortIndex != 1) {
+                return OMX_ErrorBadPortIndex;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+bool SoftFlacDecoder::isConfigured() const {
+    return mHasStreamInfo;
+}
+
+void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) {
+    ALOGV("onQueueFilled:");
+    if (mSignalledError || mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    while (!inQueue.empty() && !outQueue.empty()) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+        uint8_t* inBuffer = inHeader->pBuffer + inHeader->nOffset;
+        uint32_t inBufferLength = inHeader->nFilledLen;
+        bool endOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;
+
+        if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
+            ALOGE("onQueueFilled: first buffer should have OMX_BUFFERFLAG_CODECCONFIG set");
+            inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+        }
+        if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) {
+            status_t decoderErr = mFLACDecoder->parseMetadata(inBuffer, inBufferLength);
+            mInputBufferCount++;
+
+            if (decoderErr != OK && decoderErr != WOULD_BLOCK) {
+                ALOGE("onQueueFilled: FLACDecoder parseMetaData returns error %d", decoderErr);
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL);
+                return;
+            }
+
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            notifyEmptyBufferDone(inHeader);
+
+            if (decoderErr == WOULD_BLOCK) {
+                continue;
+            }
+            mStreamInfo = mFLACDecoder->getStreamInfo();
+            mHasStreamInfo = true;
+
+            // Only send out port settings changed event if both sample rate
+            // and numChannels are valid.
+            if (mStreamInfo.sample_rate && mStreamInfo.channels) {
+                ALOGD("onQueueFilled: initially configuring decoder: %d Hz, %d channels",
+                    mStreamInfo.sample_rate, mStreamInfo.channels);
+
+                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                mOutputPortSettingsChange = AWAITING_DISABLED;
+            }
+            return;
+        }
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+        short *outBuffer =
+                reinterpret_cast<short *>(outHeader->pBuffer + outHeader->nOffset);
+        size_t outBufferSize = outHeader->nAllocLen - outHeader->nOffset;
+
+        status_t decoderErr = mFLACDecoder->decodeOneFrame(
+                inBuffer, inBufferLength, outBuffer, &outBufferSize);
+        if (decoderErr != OK) {
+            ALOGE("onQueueFilled: FLACDecoder decodeOneFrame returns error %d", decoderErr);
+            mSignalledError = true;
+            notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL);
+            return;
+        }
+
+        mInputBufferCount++;
+        int64_t ts = inHeader->nTimeStamp;
+        inInfo->mOwnedByUs = false;
+        inQueue.erase(inQueue.begin());
+        notifyEmptyBufferDone(inHeader);
+
+        if (endOfInput) {
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+        } else if (outBufferSize == 0) {
+            continue;
+        } else {
+            outHeader->nFlags = 0;
+        }
+
+        outHeader->nFilledLen = outBufferSize;
+        outHeader->nTimeStamp = ts;
+
+        outInfo->mOwnedByUs = false;
+        outQueue.erase(outQueue.begin());
+        notifyFillBufferDone(outHeader);
+    }
+}
+
+void SoftFlacDecoder::onPortFlushCompleted(OMX_U32 portIndex) {
+    ALOGV("onPortFlushCompleted: portIndex(%u)", portIndex);
+    if (portIndex == 0) {
+        drainDecoder();
+    }
+}
+
+void SoftFlacDecoder::drainDecoder() {
+    mFLACDecoder->flush();
+}
+
+void SoftFlacDecoder::onReset() {
+    drainDecoder();
+
+    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+    mHasStreamInfo = false;
+    mInputBufferCount = 0;
+    mSignalledError = false;
+    mOutputPortSettingsChange = NONE;
+}
+
+void SoftFlacDecoder::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+    ALOGV("onPortEnableCompleted: portIndex(%u), enabled(%d)", portIndex, enabled);
+    if (portIndex != 1) {
+        return;
+    }
+
+    switch (mOutputPortSettingsChange) {
+        case NONE:
+            break;
+
+        case AWAITING_DISABLED:
+        {
+            CHECK(!enabled);
+            mOutputPortSettingsChange = AWAITING_ENABLED;
+            PortInfo *info = editPortInfo(1 /* portIndex */);
+            if (!info->mDef.bEnabled) {
+                info->mDef.nBufferSize = mStreamInfo.max_blocksize * mStreamInfo.channels * 2;
+            }
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+            CHECK(enabled);
+            mOutputPortSettingsChange = NONE;
+            break;
+        }
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    ALOGV("createSoftOMXComponent: flac decoder");
+    return new android::SoftFlacDecoder(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
new file mode 100644
index 0000000..c09081d
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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 SOFT_FLAC_DECODER_H
+#define SOFT_FLAC_DECODER_H
+
+#include "FLACDecoder.h"
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftFlacDecoder : public SimpleSoftOMXComponent {
+    SoftFlacDecoder(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+    virtual OMX_ERRORTYPE initCheck() const override;
+
+protected:
+    virtual ~SoftFlacDecoder();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params) override;
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params) override;
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex) override;
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled) override;
+    virtual void onReset() override;
+
+private:
+    enum {
+        kNumInputBuffers   = 4,
+        kNumOutputBuffers  = 4,
+    };
+
+    sp<FLACDecoder> mFLACDecoder;
+    FLAC__StreamMetadata_StreamInfo mStreamInfo;
+    bool mHasStreamInfo;
+    size_t mInputBufferCount;
+    bool mSignalledError;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    void initPorts();
+    void initDecoder();
+    bool isConfigured() const;
+    void drainDecoder();
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftFlacDecoder);
+};
+
+}  // namespace android
+
+#endif  // SOFT_FLAC_DECODER_H
diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml
index b957b0c..632088a 100644
--- a/media/libstagefright/data/media_codecs_google_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_audio.xml
@@ -61,6 +61,11 @@
             <Limit name="sample-rate" ranges="8000-96000" />
             <Limit name="bitrate" range="1-10000000" />
         </MediaCodec>
+        <MediaCodec name="OMX.google.flac.decoder" type="audio/flac">
+            <Limit name="channel-count" max="8" />
+            <Limit name="sample-rate" ranges="1-655350" />
+            <Limit name="bitrate" range="1-21000000" />
+        </MediaCodec>
     </Decoders>
     <Encoders>
         <MediaCodec name="OMX.google.aac.encoder" type="audio/mp4a-latm">
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
new file mode 100644
index 0000000..284c25f
--- /dev/null
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -0,0 +1,34 @@
+cc_library_shared {
+    name: "libstagefright_flacdec",
+
+    srcs: [
+        "FLACDecoder.cpp",
+    ],
+
+    include_dirs: [
+        "external/flac/include",
+        "frameworks/av/media/libstagefright/include",
+    ],
+
+    cflags: ["-Werror"],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    static_libs: ["libFLAC"],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libstagefright_foundation",
+        "libutils",
+    ],
+}
diff --git a/media/libstagefright/flac/dec/FLACDecoder.cpp b/media/libstagefright/flac/dec/FLACDecoder.cpp
new file mode 100644
index 0000000..8c7137c
--- /dev/null
+++ b/media/libstagefright/flac/dec/FLACDecoder.cpp
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2017 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 "FLACDecoder"
+#include <utils/Log.h>
+
+#include "FLACDecoder.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+// These are the corresponding callbacks with C++ calling conventions
+FLAC__StreamDecoderReadStatus FLACDecoder::readCallback(
+        FLAC__byte buffer[], size_t *bytes) {
+    if (mBuffer == nullptr || mBufferLen == 0) {
+        *bytes = 0;
+        return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+    }
+
+    size_t actual = *bytes;
+    if (actual > mBufferDataSize - mBufferPos) {
+        actual = mBufferDataSize - mBufferPos;
+    }
+    memcpy(buffer, mBuffer + mBufferPos, actual);
+    mBufferPos += actual;
+    *bytes = actual;
+    return (actual == 0 ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM
+                        : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE);
+}
+
+FLAC__StreamDecoderWriteStatus FLACDecoder::writeCallback(
+        const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
+{
+    if (!mWriteRequested) {
+        ALOGE("writeCallback: unexpected");
+        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+    }
+
+    mWriteRequested = false;
+    // FLAC decoder doesn't free or realloc buffer until next frame or finish
+    mWriteHeader = frame->header;
+    memmove(mWriteBuffer, buffer, sizeof(const FLAC__int32 * const) * getChannels());
+    mWriteCompleted = true;
+    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+void FLACDecoder::metadataCallback(const FLAC__StreamMetadata *metadata)
+{
+    switch (metadata->type) {
+        case FLAC__METADATA_TYPE_STREAMINFO:
+        {
+            if (mStreamInfoValid) {
+                ALOGE("metadataCallback: unexpected STREAMINFO");
+            } else {
+                mStreamInfo = metadata->data.stream_info;
+                mStreamInfoValid = true;
+            }
+            break;
+        }
+
+        /* TODO: enable metadata parsing below.
+        case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+        {
+            const FLAC__StreamMetadata_VorbisComment *vc;
+            vc = &metadata->data.vorbis_comment;
+            for (FLAC__uint32 i = 0; i < vc->num_comments; ++i) {
+                FLAC__StreamMetadata_VorbisComment_Entry *vce;
+                vce = &vc->comments[i];
+                if (mFileMetadata != 0 && vce->entry != NULL) {
+                    parseVorbisComment(mFileMetadata, (const char *) vce->entry,
+                            vce->length);
+                }
+            }
+            break;
+        }
+
+        case FLAC__METADATA_TYPE_PICTURE:
+        {
+            if (mFileMetadata != 0) {
+                const FLAC__StreamMetadata_Picture *p = &metadata->data.picture;
+                mFileMetadata->setData(kKeyAlbumArt,
+                        MetaData::TYPE_NONE, p->data, p->data_length);
+                mFileMetadata->setCString(kKeyAlbumArtMIME, p->mime_type);
+            }
+            break;
+        }
+        */
+
+        default:
+            ALOGW("metadataCallback: unexpected type %u", metadata->type);
+            break;
+    }
+}
+
+void FLACDecoder::errorCallback(FLAC__StreamDecoderErrorStatus status)
+{
+    ALOGE("errorCallback: status=%d", status);
+    mErrorStatus = status;
+}
+
+// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved.
+// These are candidates for optimization if needed.
+static void copyMono8(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned /* nChannels */) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        *dst++ = src[0][i] << 8;
+    }
+}
+
+static void copyStereo8(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned /* nChannels */) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        *dst++ = src[0][i] << 8;
+        *dst++ = src[1][i] << 8;
+    }
+}
+
+static void copyMultiCh8(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned nChannels) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        for (unsigned c = 0; c < nChannels; ++c) {
+            *dst++ = src[c][i] << 8;
+        }
+    }
+}
+
+static void copyMono16(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned /* nChannels */) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        *dst++ = src[0][i];
+    }
+}
+
+static void copyStereo16(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned /* nChannels */) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        *dst++ = src[0][i];
+        *dst++ = src[1][i];
+    }
+}
+
+static void copyMultiCh16(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned nChannels) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        for (unsigned c = 0; c < nChannels; ++c) {
+            *dst++ = src[c][i];
+        }
+    }
+}
+
+// TODO: 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger
+static void copyMono24(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned /* nChannels */) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        *dst++ = src[0][i] >> 8;
+    }
+}
+
+static void copyStereo24(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned /* nChannels */) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        *dst++ = src[0][i] >> 8;
+        *dst++ = src[1][i] >> 8;
+    }
+}
+
+static void copyMultiCh24(
+        short *dst,
+        const int * src[FLACDecoder::kMaxChannels],
+        unsigned nSamples,
+        unsigned nChannels) {
+    for (unsigned i = 0; i < nSamples; ++i) {
+        for (unsigned c = 0; c < nChannels; ++c) {
+            *dst++ = src[c][i] >> 8;
+        }
+    }
+}
+
+// static
+sp<FLACDecoder> FLACDecoder::Create() {
+    sp<FLACDecoder> decoder = new FLACDecoder();
+    if (decoder->init() != OK) {
+        return NULL;
+    }
+    return decoder;
+}
+
+FLACDecoder::FLACDecoder()
+    : mDecoder(NULL),
+      mBuffer(NULL),
+      mBufferLen(0),
+      mBufferPos(0),
+      mBufferDataSize(0),
+      mStreamInfoValid(false),
+      mWriteRequested(false),
+      mWriteCompleted(false),
+      mErrorStatus((FLAC__StreamDecoderErrorStatus) -1),
+      mCopy(nullptr) {
+    ALOGV("ctor:");
+    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+    memset(&mWriteHeader, 0, sizeof(mWriteHeader));
+    memset(&mWriteBuffer, 0, sizeof(mWriteBuffer));
+}
+
+FLACDecoder::~FLACDecoder() {
+    ALOGV("dtor:");
+    if (mDecoder != NULL) {
+        FLAC__stream_decoder_delete(mDecoder);
+        mDecoder = NULL;
+    }
+    if (mBuffer != NULL) {
+        free(mBuffer);
+    }
+}
+
+status_t FLACDecoder::init() {
+    ALOGV("init:");
+    // setup libFLAC stream decoder
+    mDecoder = FLAC__stream_decoder_new();
+    if (mDecoder == NULL) {
+        ALOGE("init: failed to create libFLAC stream decoder");
+        return NO_INIT;
+    }
+    FLAC__stream_decoder_set_md5_checking(mDecoder, false);
+    FLAC__stream_decoder_set_metadata_ignore_all(mDecoder);
+    FLAC__stream_decoder_set_metadata_respond(
+            mDecoder, FLAC__METADATA_TYPE_STREAMINFO);
+    /*
+    FLAC__stream_decoder_set_metadata_respond(
+            mDecoder, FLAC__METADATA_TYPE_PICTURE);
+    FLAC__stream_decoder_set_metadata_respond(
+            mDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+    */
+    static auto read_callback =
+        [] (const FLAC__StreamDecoder * /* decoder */,
+            FLAC__byte buffer[],
+            size_t *bytes,
+            void *client_data) -> FLAC__StreamDecoderReadStatus {
+            return ((FLACDecoder *) client_data)->readCallback(buffer, bytes); };
+
+    static auto write_callback =
+        [] (const FLAC__StreamDecoder * /* decoder */,
+            const FLAC__Frame *frame,
+            const FLAC__int32 * const buffer[],
+            void *client_data) -> FLAC__StreamDecoderWriteStatus {
+            return ((FLACDecoder *) client_data)->writeCallback(frame, buffer); };
+
+    static auto metadata_callback =
+        [] (const FLAC__StreamDecoder * /* decoder */,
+            const FLAC__StreamMetadata *metadata,
+            void *client_data) {
+            ((FLACDecoder *) client_data)->metadataCallback(metadata); };
+
+    static auto error_callback =
+        [] (const FLAC__StreamDecoder * /* decoder */,
+            FLAC__StreamDecoderErrorStatus status,
+            void *client_data) {
+            ((FLACDecoder *) client_data)->errorCallback(status); };
+
+    FLAC__StreamDecoderInitStatus initStatus =
+        FLAC__stream_decoder_init_stream(
+                mDecoder,
+                read_callback,
+                NULL /* seek_callback */,
+                NULL /* tell_callback */,
+                NULL /* length_callback */,
+                NULL /* eof_callback */,
+                write_callback,
+                metadata_callback,
+                error_callback,
+                (void *)this);
+    if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+        ALOGE("init: init_stream failed, returned %d", initStatus);
+        return NO_INIT;
+    }
+    return OK;
+}
+
+void FLACDecoder::flush() {
+    ALOGV("flush:");
+    mBufferPos = 0;
+    mBufferDataSize = 0;
+    mStreamInfoValid = false;
+    if (!FLAC__stream_decoder_reset(mDecoder)) {
+        ALOGE("flush: failed to reset FLAC stream decoder");
+    }
+}
+
+status_t FLACDecoder::parseMetadata(const uint8_t *inBuffer, size_t inBufferLen) {
+    ALOGV("parseMetadata: input size(%zu)", inBufferLen);
+    //hexdump(inBuffer, inBufferLen);
+
+    if (mStreamInfoValid) {
+        ALOGE("parseMetadata: already have full metadata blocks");
+        return ERROR_MALFORMED;
+    }
+
+    status_t err = addDataToBuffer(inBuffer, inBufferLen);
+    if (err != OK) {
+        ALOGE("parseMetadata: addDataToBuffer returns error %d", err);
+        return err;
+    }
+
+    if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) {
+        if (!FLAC__stream_decoder_reset(mDecoder)) {
+            ALOGE("parseMetadata: failed to reset FLAC stream decoder");
+            return FAILED_TRANSACTION;
+        }
+        mBufferPos = 0;
+        ALOGV("parseMetadata: do not have full metadata blocks yet");
+        return WOULD_BLOCK;
+    }
+
+    if (!mStreamInfoValid) {
+        ALOGE("parseMetadata: missing STREAMINFO");
+        return ERROR_MALFORMED;
+    }
+
+    // check block size
+    if (getMaxBlockSize() == 0) {
+        ALOGE("wrong max blocksize %u", getMaxBlockSize());
+        mStreamInfoValid = false;
+        return ERROR_MALFORMED;
+    }
+
+    // check channel count
+    if (getChannels() == 0 || getChannels() > kMaxChannels) {
+        ALOGE("unsupported channel count %u", getChannels());
+        mStreamInfoValid = false;
+        return ERROR_MALFORMED;
+    }
+
+    // check bit depth
+    switch (getBitsPerSample()) {
+        case 8:
+        case 16:
+        case 24:
+            break;
+
+        default:
+            ALOGE("parseMetadata: unsupported bits per sample %u", getBitsPerSample());
+            mStreamInfoValid = false;
+            return ERROR_MALFORMED;
+    }
+
+    // configure the appropriate copy function, defaulting to trespass
+    static const struct {
+        unsigned mChannels;
+        unsigned mBitsPerSample;
+        void (*mCopy)(short *dst, const int * src[kMaxChannels],
+                unsigned nSamples, unsigned nChannels);
+    } table[] = {
+        { 1,  8, copyMono8     },
+        { 2,  8, copyStereo8   },
+        { 8,  8, copyMultiCh8  },
+        { 1, 16, copyMono16    },
+        { 2, 16, copyStereo16  },
+        { 8, 16, copyMultiCh16 },
+        { 1, 24, copyMono24    },
+        { 2, 24, copyStereo24  },
+        { 8, 24, copyMultiCh24 },
+    };
+    for (const auto &entry : table) {
+        if (entry.mChannels >= getChannels() &&
+                entry.mBitsPerSample == getBitsPerSample()) {
+            mCopy = entry.mCopy;
+            break;
+        }
+    }
+
+    // Now we have all metadata blocks.
+    mBufferPos = 0;
+    mBufferDataSize = 0;
+
+    return OK;
+}
+
+status_t FLACDecoder::decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen,
+        short *outBuffer, size_t *outBufferLen) {
+    ALOGV("decodeOneFrame: input size(%zu)", inBufferLen);
+
+    if (inBufferLen == 0) {
+        ALOGV("decodeOneFrame: no input data");
+        if (outBufferLen) {
+            *outBufferLen = 0;
+        }
+        return OK;
+    }
+
+    if (!mStreamInfoValid) {
+        ALOGW("decodeOneFrame: no streaminfo metadata block");
+    }
+
+    status_t err = addDataToBuffer(inBuffer, inBufferLen);
+    if (err != OK) {
+        ALOGW("decodeOneFrame: addDataToBuffer returns error %d", err);
+        return err;
+    }
+
+    mWriteRequested = true;
+    mWriteCompleted = false;
+    if (!FLAC__stream_decoder_process_single(mDecoder)) {
+        ALOGE("decodeOneFrame: process_single failed");
+        return ERROR_MALFORMED;
+    }
+    if (!mWriteCompleted) {
+        ALOGV("decodeOneFrame: write did not complete");
+        if (outBufferLen) {
+            *outBufferLen = 0;
+        }
+        return OK;
+    }
+
+    // frame header should be consistent with STREAMINFO
+    unsigned blocksize = mWriteHeader.blocksize;
+    if (blocksize == 0 || blocksize > getMaxBlockSize()) {
+        ALOGE("decodeOneFrame: write invalid blocksize %u", blocksize);
+        return ERROR_MALFORMED;
+    }
+    if (mWriteHeader.sample_rate != getSampleRate() ||
+        mWriteHeader.channels != getChannels() ||
+        mWriteHeader.bits_per_sample != getBitsPerSample()) {
+        ALOGE("decodeOneFrame: parameters are changed mid-stream: %d/%d/%d -> %d/%d/%d",
+                getSampleRate(), getChannels(), getBitsPerSample(),
+                mWriteHeader.sample_rate, mWriteHeader.channels, mWriteHeader.bits_per_sample);
+        return ERROR_MALFORMED;
+    }
+    if (mWriteHeader.number_type != FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) {
+        ALOGE("decodeOneFrame: number type is %d, expected %d",
+                mWriteHeader.number_type, FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
+        return ERROR_MALFORMED;
+    }
+
+    size_t bufferSize = blocksize * getChannels() * sizeof(short);
+    if (bufferSize > *outBufferLen) {
+        ALOGW("decodeOneFrame: output buffer holds only partial frame %zu:%zu",
+                *outBufferLen, bufferSize);
+        blocksize = *outBufferLen / (getChannels() * sizeof(short));
+        bufferSize = blocksize * getChannels() * sizeof(short);
+    }
+
+    if (mCopy == nullptr) {
+        ALOGE("decodeOneFrame: format is not supported: channels(%d), BitsPerSample(%d)",
+                getChannels(), getBitsPerSample());
+        return ERROR_UNSUPPORTED;
+    }
+    // copy PCM from FLAC write buffer to output buffer, with interleaving
+    (*mCopy)(outBuffer, mWriteBuffer, blocksize, getChannels());
+    *outBufferLen = bufferSize;
+    return OK;
+}
+
+status_t FLACDecoder::addDataToBuffer(const uint8_t *inBuffer, size_t inBufferLen) {
+    // mBufferPos should be no larger than mBufferDataSize
+    if (inBufferLen > SIZE_MAX - (mBufferDataSize - mBufferPos)) {
+        ALOGE("addDataToBuffer: input buffer is too large");
+        return ERROR_MALFORMED;
+    }
+
+    if (inBufferLen > mBufferLen - mBufferDataSize) {
+        if (mBufferPos > 0) {
+            memmove(mBuffer, mBuffer + mBufferPos, mBufferDataSize - mBufferPos);
+            mBufferDataSize -= mBufferPos;
+            mBufferPos = 0;
+        }
+        if (inBufferLen > mBufferLen - mBufferDataSize) {
+            mBuffer = (uint8_t*)realloc(mBuffer, mBufferDataSize + inBufferLen);
+            if (mBuffer == nullptr) {
+                mBufferDataSize = 0;
+                mBufferLen = 0;
+                ALOGE("decodeOneFrame: failed to allocate memory for input buffer");
+                return NO_MEMORY;
+            }
+            mBufferLen = mBufferDataSize + inBufferLen;
+        }
+    }
+
+    memcpy(mBuffer + mBufferDataSize, inBuffer, inBufferLen);
+    mBufferDataSize += inBufferLen;
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/flac/dec/FLACDecoder.h b/media/libstagefright/flac/dec/FLACDecoder.h
new file mode 100644
index 0000000..36282a8
--- /dev/null
+++ b/media/libstagefright/flac/dec/FLACDecoder.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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 FLAC_DECODER_H_
+#define FLAC_DECODER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+#include "FLAC/stream_decoder.h"
+
+namespace android {
+
+// packet based FLAC decoder, wrapps libFLAC stream decoder.
+class FLACDecoder : public RefBase {
+
+public:
+    enum {
+        kMaxChannels = 8,
+    };
+
+    static sp<FLACDecoder> Create();
+
+    FLAC__StreamMetadata_StreamInfo getStreamInfo() const {
+        return mStreamInfo;
+    }
+
+    status_t parseMetadata(const uint8_t *inBuffer, size_t inBufferLen);
+    status_t decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen,
+            short *outBuffer, size_t *outBufferLen);
+    void flush();
+
+protected:
+    FLACDecoder();
+    virtual ~FLACDecoder() override;
+
+private:
+    // stream properties
+    unsigned getMaxBlockSize() const {
+        return mStreamInfo.max_blocksize;
+    }
+    unsigned getSampleRate() const {
+        return mStreamInfo.sample_rate;
+    }
+    unsigned getChannels() const {
+        return mStreamInfo.channels;
+    }
+    unsigned getBitsPerSample() const {
+        return mStreamInfo.bits_per_sample;
+    }
+    FLAC__uint64 getTotalSamples() const {
+        return mStreamInfo.total_samples;
+    }
+
+    status_t addDataToBuffer(const uint8_t *inBuffer, size_t inBufferLen);
+
+    FLAC__StreamDecoder *mDecoder;
+
+    uint8_t *mBuffer;  // cache input bit stream data
+    size_t mBufferLen;  // the memory size of |mBuffer|
+    size_t mBufferPos;  // next byte to read in |mBuffer|
+    // size of input data stored in |mBuffer|, always started at offset 0
+    size_t mBufferDataSize;
+
+    // cached when the STREAMINFO metadata is parsed by libFLAC
+    FLAC__StreamMetadata_StreamInfo mStreamInfo;
+    bool mStreamInfoValid;
+
+    // cached when a decoded PCM block is "written" by libFLAC decoder
+    bool mWriteRequested;
+    bool mWriteCompleted;
+    FLAC__FrameHeader mWriteHeader;
+    FLAC__int32 const * mWriteBuffer[kMaxChannels];
+
+    // most recent error reported by libFLAC decoder
+    FLAC__StreamDecoderErrorStatus mErrorStatus;
+
+    void (*mCopy)(short *dst, const int *src[kMaxChannels], unsigned nSamples, unsigned nChannels);
+
+    status_t init();
+
+    // FLAC stream decoder callbacks as C++ instance methods
+    FLAC__StreamDecoderReadStatus readCallback(FLAC__byte buffer[], size_t *bytes);
+    FLAC__StreamDecoderWriteStatus writeCallback(
+            const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
+    void metadataCallback(const FLAC__StreamMetadata *metadata);
+    void errorCallback(FLAC__StreamDecoderErrorStatus status);
+
+    DISALLOW_EVIL_CONSTRUCTORS(FLACDecoder);
+};
+
+}  // namespace android
+
+#endif  // FLAC_DECODER_H_
diff --git a/media/libstagefright/flac/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/flac/dec/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/libstagefright/flac/dec/MODULE_LICENSE_APACHE2
diff --git a/media/libstagefright/flac/dec/NOTICE b/media/libstagefright/flac/dec/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/media/libstagefright/flac/dec/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
index 7afd22d..9676b97 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -64,6 +64,7 @@
     kKeyOpusHeader        = 'ohdr',  // raw data
     kKeyOpusCodecDelay    = 'ocod',  // uint64_t (codec delay in ns)
     kKeyOpusSeekPreRoll   = 'ospr',  // uint64_t (seek preroll in ns)
+    kKeyFlacMetadata      = 'flMd',  // raw data
     kKeyVp9CodecPrivate   = 'vp9p',  // raw data (vp9 csd information)
     kKeyWantsNALFragments = 'NALf',
     kKeyIsSyncFrame       = 'sync',  // int32_t (bool)
diff --git a/media/libstagefright/matroska/Android.bp b/media/libstagefright/matroska/Android.bp
index a5891c3..ec2fb4b 100644
--- a/media/libstagefright/matroska/Android.bp
+++ b/media/libstagefright/matroska/Android.bp
@@ -4,8 +4,10 @@
     srcs: ["MatroskaExtractor.cpp"],
 
     include_dirs: [
+        "external/flac/include",
         "external/libvpx/libwebm",
         "frameworks/native/include/media/openmax",
+        "frameworks/av/media/libstagefright/flac/dec",
         "frameworks/av/media/libstagefright/include",
     ],
 
@@ -26,5 +28,8 @@
         },
     },
 
-    shared_libs: ["libmedia"],
+    shared_libs: [
+        "libmedia",
+        "libstagefright_flacdec"
+    ],
 }
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 81179d1..813a257 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "MatroskaExtractor"
 #include <utils/Log.h>
 
+#include "FLACDecoder.h"
 #include "MatroskaExtractor.h"
 #include "avc_utils.h"
 
@@ -1051,6 +1052,37 @@
     return OK;
 }
 
+static status_t addFlacMetadata(
+        const sp<MetaData> &meta,
+        const void *codecPrivate, size_t codecPrivateSize) {
+    // hexdump(codecPrivate, codecPrivateSize);
+
+    meta->setData(kKeyFlacMetadata, 0, codecPrivate, codecPrivateSize);
+
+    int32_t maxInputSize = 64 << 10;
+    sp<FLACDecoder> flacDecoder = FLACDecoder::Create();
+    if (flacDecoder != NULL
+            && flacDecoder->parseMetadata((const uint8_t*)codecPrivate, codecPrivateSize) == OK) {
+        FLAC__StreamMetadata_StreamInfo streamInfo = flacDecoder->getStreamInfo();
+        maxInputSize = streamInfo.max_framesize;
+        if (maxInputSize == 0) {
+            // In case max framesize is not available, use raw data size as max framesize,
+            // assuming there is no expansion.
+            if (streamInfo.max_blocksize != 0
+                    && streamInfo.channels != 0
+                    && ((streamInfo.bits_per_sample + 7) / 8) >
+                        INT32_MAX / streamInfo.max_blocksize / streamInfo.channels) {
+                return ERROR_MALFORMED;
+            }
+            maxInputSize = ((streamInfo.bits_per_sample + 7) / 8)
+                * streamInfo.max_blocksize * streamInfo.channels;
+        }
+    }
+    meta->setInt32(kKeyMaxInputSize, maxInputSize);
+
+    return OK;
+}
+
 status_t MatroskaExtractor::synthesizeAVCC(TrackInfo *trackInfo, size_t index) {
     BlockIterator iter(this, trackInfo->mTrackNum, index);
     if (iter.eos()) {
@@ -1363,6 +1395,9 @@
                     mSeekPreRollNs = track->GetSeekPreRoll();
                 } else if (!strcmp("A_MPEG/L3", codecID)) {
                     meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+                } else if (!strcmp("A_FLAC", codecID)) {
+                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC);
+                    err = addFlacMetadata(meta, codecPrivate, codecPrivateSize);
                 } else {
                     ALOGW("%s is not supported.", codecID);
                     continue;
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index a773ca2..fccb12b 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -58,6 +58,7 @@
     { "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },
     { "OMX.google.vp9.encoder", "vpxenc", "video_encoder.vp9" },
     { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },
+    { "OMX.google.flac.decoder", "flacdec", "audio_decoder.flac" },
     { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },
     { "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" },
 };
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8596817..df10d23 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -5970,12 +5970,17 @@
     switch (kUseFastCapture) {
     case FastCapture_Never:
         initFastCapture = false;
+        ALOGV("%p kUseFastCapture = Never, initFastCapture = false", this);
         break;
     case FastCapture_Always:
         initFastCapture = true;
+        ALOGV("%p kUseFastCapture = Always, initFastCapture = true", this);
         break;
     case FastCapture_Static:
         initFastCapture = (mFrameCount * 1000) / mSampleRate < kMinNormalCaptureBufferSizeMs;
+        ALOGV("%p kUseFastCapture = Static, (%lld * 1000) / %u vs %u, initFastCapture = %d",
+                this, (long long)mFrameCount, mSampleRate, kMinNormalCaptureBufferSizeMs,
+                initFastCapture);
         break;
     // case FastCapture_Dynamic:
     }
@@ -5986,13 +5991,16 @@
         // quadruple-buffering of 20 ms each; this ensures we can sleep for 20ms in RecordThread
         size_t pipeFramesP2 = roundup(4 * FMS_20 * mSampleRate / 1000);
         size_t pipeSize = pipeFramesP2 * Format_frameSize(format);
-        void *pipeBuffer;
+        void *pipeBuffer = nullptr;
         const sp<MemoryDealer> roHeap(readOnlyHeap());
         sp<IMemory> pipeMemory;
         if ((roHeap == 0) ||
                 (pipeMemory = roHeap->allocate(pipeSize)) == 0 ||
-                (pipeBuffer = pipeMemory->pointer()) == NULL) {
-            ALOGE("not enough memory for pipe buffer size=%zu", pipeSize);
+                (pipeBuffer = pipeMemory->pointer()) == nullptr) {
+            ALOGE("not enough memory for pipe buffer size=%zu; "
+                    "roHeap=%p, pipeMemory=%p, pipeBuffer=%p; roHeapSize: %lld",
+                    pipeSize, roHeap.get(), pipeMemory.get(), pipeBuffer,
+                    (long long)kRecordThreadReadOnlyHeapSize);
             goto failed;
         }
         // pipe will be shared directly with fast clients, so clear to avoid leaking old information
@@ -6634,19 +6642,19 @@
               audio_input_flags_t old = *flags;
               chain->checkInputFlagCompatibility(flags);
               if (old != *flags) {
-                  ALOGV("AUDIO_INPUT_FLAGS denied by effect old=%#x new=%#x",
-                          (int)old, (int)*flags);
+                  ALOGV("%p AUDIO_INPUT_FLAGS denied by effect old=%#x new=%#x",
+                          this, (int)old, (int)*flags);
               }
           }
           ALOGV_IF((*flags & AUDIO_INPUT_FLAG_FAST) != 0,
-                   "AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
-                   frameCount, mFrameCount);
+                   "%p AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
+                   this, frameCount, mFrameCount);
       } else {
-        ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%zu mFrameCount=%zu mPipeFramesP2=%zu "
-                "format=%#x isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
+        ALOGV("%p AUDIO_INPUT_FLAG_FAST denied: frameCount=%zu mFrameCount=%zu mPipeFramesP2=%zu "
+                "format=%#x isLinear=%d mFormat=%#x channelMask=%#x sampleRate=%u mSampleRate=%u "
                 "hasFastCapture=%d tid=%d mFastTrackAvail=%d",
-                frameCount, mFrameCount, mPipeFramesP2,
-                format, audio_is_linear_pcm(format), channelMask, sampleRate, mSampleRate,
+                this, frameCount, mFrameCount, mPipeFramesP2,
+                format, audio_is_linear_pcm(format), mFormat, channelMask, sampleRate, mSampleRate,
                 hasFastCapture(), tid, mFastTrackAvail);
         *flags = (audio_input_flags_t)(*flags & ~AUDIO_INPUT_FLAG_FAST);
       }
@@ -7243,6 +7251,10 @@
     result = mInput->stream->getBufferSize(&mBufferSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving buffer size from HAL: %d", result);
     mFrameCount = mBufferSize / mFrameSize;
+    ALOGV("%p RecordThread params: mChannelCount=%u, mFormat=%#x, mFrameSize=%lld, "
+            "mBufferSize=%lld, mFrameCount=%lld",
+            this, mChannelCount, mFormat, (long long)mFrameSize, (long long)mBufferSize,
+            (long long)mFrameCount);
     // This is the formula for calculating the temporary buffer size.
     // With 7 HAL buffers, we can guarantee ability to down-sample the input by ratio of 6:1 to
     // 1 full output buffer, regardless of the alignment of the available input.
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
index 99b3ba8..8c8b97a 100644
--- a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
@@ -156,6 +156,86 @@
     return OK;
 }
 
+void Camera3BufferManager::notifyBufferRemoved(int streamId, int streamSetId) {
+    Mutex::Autolock l(mLock);
+    StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+    size_t& attachedBufferCount =
+            streamSet.attachedBufferCountMap.editValueFor(streamId);
+    attachedBufferCount--;
+}
+
+status_t Camera3BufferManager::checkAndFreeBufferOnOtherStreamsLocked(
+        int streamId, int streamSetId) {
+    StreamId firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
+    StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+    if (streamSet.streamInfoMap.size() == 1) {
+        ALOGV("StreamSet %d has no other stream available to free", streamSetId);
+        return OK;
+    }
+
+    bool freeBufferIsAttached = false;
+    for (size_t i = 0; i < streamSet.streamInfoMap.size(); i++) {
+        firstOtherStreamId = streamSet.streamInfoMap[i].streamId;
+        if (firstOtherStreamId != streamId) {
+
+            size_t otherBufferCount  =
+                    streamSet.handoutBufferCountMap.valueFor(firstOtherStreamId);
+            size_t otherAttachedBufferCount =
+                    streamSet.attachedBufferCountMap.valueFor(firstOtherStreamId);
+            if (otherAttachedBufferCount > otherBufferCount) {
+                freeBufferIsAttached = true;
+                break;
+            }
+        }
+        firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
+    }
+    if (firstOtherStreamId == CAMERA3_STREAM_ID_INVALID || !freeBufferIsAttached) {
+        ALOGV("StreamSet %d has no buffer available to free", streamSetId);
+        return OK;
+    }
+
+
+    // This will drop the reference to one free buffer, which will effectively free one
+    // buffer (from the free buffer list) for the inactive streams.
+    size_t totalAllocatedBufferCount = 0;
+    for (size_t i = 0; i < streamSet.attachedBufferCountMap.size(); i++) {
+        totalAllocatedBufferCount += streamSet.attachedBufferCountMap[i];
+    }
+    if (totalAllocatedBufferCount > streamSet.allocatedBufferWaterMark) {
+        ALOGV("Stream %d: Freeing buffer: detach", firstOtherStreamId);
+        sp<Camera3OutputStream> stream =
+                mStreamMap.valueFor(firstOtherStreamId).promote();
+        if (stream == nullptr) {
+            ALOGE("%s: unable to promote stream %d to detach buffer", __FUNCTION__,
+                    firstOtherStreamId);
+            return INVALID_OPERATION;
+        }
+
+        // Detach and then drop the buffer.
+        //
+        // Need to unlock because the stream may also be calling
+        // into the buffer manager in parallel to signal buffer
+        // release, or acquire a new buffer.
+        bool bufferFreed = false;
+        {
+            mLock.unlock();
+            sp<GraphicBuffer> buffer;
+            stream->detachBuffer(&buffer, /*fenceFd*/ nullptr);
+            mLock.lock();
+            if (buffer.get() != nullptr) {
+                bufferFreed = true;
+            }
+        }
+        if (bufferFreed) {
+            size_t& otherAttachedBufferCount =
+                    streamSet.attachedBufferCountMap.editValueFor(firstOtherStreamId);
+            otherAttachedBufferCount--;
+        }
+    }
+
+    return OK;
+}
+
 status_t Camera3BufferManager::getBufferForStream(int streamId, int streamSetId,
         sp<GraphicBuffer>* gb, int* fenceFd) {
     ATRACE_CALL();
@@ -228,61 +308,15 @@
         // in returnBufferForStream() if we want to free buffer more quickly.
         // TODO: probably should find out all the inactive stream IDs, and free the firstly found
         // buffers for them.
-        StreamId firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
-        if (streamSet.streamInfoMap.size() > 1) {
-            bool freeBufferIsAttached = false;
-            for (size_t i = 0; i < streamSet.streamInfoMap.size(); i++) {
-                firstOtherStreamId = streamSet.streamInfoMap[i].streamId;
-                if (firstOtherStreamId != streamId) {
-                    size_t otherBufferCount  =
-                            streamSet.handoutBufferCountMap.valueFor(firstOtherStreamId);
-                    size_t otherAttachedBufferCount =
-                            streamSet.attachedBufferCountMap.valueFor(firstOtherStreamId);
-                    if (otherAttachedBufferCount > otherBufferCount) {
-                        freeBufferIsAttached = true;
-                        break;
-                    }
-                }
-                firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
-            }
-            if (firstOtherStreamId == CAMERA3_STREAM_ID_INVALID) {
-                return OK;
-            }
-
-            // This will drop the reference to one free buffer, which will effectively free one
-            // buffer for the inactive streams.
-            size_t totalAllocatedBufferCount = 0;
-            for (size_t i = 0; i < streamSet.attachedBufferCountMap.size(); i++) {
-                totalAllocatedBufferCount += streamSet.attachedBufferCountMap[i];
-            }
-            if (totalAllocatedBufferCount > streamSet.allocatedBufferWaterMark) {
-                ALOGV("%s: free a buffer from stream %d", __FUNCTION__, firstOtherStreamId);
-                if (freeBufferIsAttached) {
-                    ALOGV("Stream %d: Freeing buffer: detach", firstOtherStreamId);
-                    sp<Camera3OutputStream> stream =
-                            mStreamMap.valueFor(firstOtherStreamId).promote();
-                    if (stream == nullptr) {
-                        ALOGE("%s: unable to promote stream %d to detach buffer", __FUNCTION__,
-                                firstOtherStreamId);
-                        return INVALID_OPERATION;
-                    }
-
-                    // Detach and then drop the buffer.
-                    //
-                    // Need to unlock because the stream may also be calling
-                    // into the buffer manager in parallel to signal buffer
-                    // release, or acquire a new buffer.
-                    {
-                        mLock.unlock();
-                        sp<GraphicBuffer> buffer;
-                        stream->detachBuffer(&buffer, /*fenceFd*/ nullptr);
-                        mLock.lock();
-                    }
-                    size_t& otherAttachedBufferCount =
-                            streamSet.attachedBufferCountMap.editValueFor(firstOtherStreamId);
-                    otherAttachedBufferCount--;
-                }
-            }
+        res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetId);
+        if (res != OK) {
+            return res;
+        }
+        // Since we just allocated one new buffer above, try free one more buffer from other streams
+        // to prevent total buffer count from growing
+        res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetId);
+        if (res != OK) {
+            return res;
         }
     } else {
         // TODO: implement this.
@@ -292,11 +326,18 @@
     return OK;
 }
 
-status_t Camera3BufferManager::onBufferReleased(int streamId, int streamSetId) {
+status_t Camera3BufferManager::onBufferReleased(
+        int streamId, int streamSetId, bool* shouldFreeBuffer) {
     ATRACE_CALL();
-    Mutex::Autolock l(mLock);
 
+    if (shouldFreeBuffer == nullptr) {
+        ALOGE("%s: shouldFreeBuffer is null", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock l(mLock);
     ALOGV("Stream %d set %d: Buffer released", streamId, streamSetId);
+    *shouldFreeBuffer = false;
 
     if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
         ALOGV("%s: signaling buffer release for an already unregistered stream "
@@ -311,6 +352,36 @@
         bufferCount--;
         ALOGV("%s: Stream %d set %d: Buffer count now %zu", __FUNCTION__, streamId, streamSetId,
                 bufferCount);
+
+        size_t totalAllocatedBufferCount = 0;
+        size_t totalHandOutBufferCount = 0;
+        for (size_t i = 0; i < streamSet.attachedBufferCountMap.size(); i++) {
+            totalAllocatedBufferCount += streamSet.attachedBufferCountMap[i];
+            totalHandOutBufferCount += streamSet.handoutBufferCountMap[i];
+        }
+
+        size_t newWaterMark = totalHandOutBufferCount + BUFFER_WATERMARK_DEC_THRESHOLD;
+        if (totalAllocatedBufferCount > newWaterMark &&
+                    streamSet.allocatedBufferWaterMark > newWaterMark) {
+            // BufferManager got more than enough buffers, so decrease watermark
+            // to trigger more buffers free operation.
+            streamSet.allocatedBufferWaterMark = newWaterMark;
+            ALOGV("%s: Stream %d set %d: watermark--; now %zu",
+                    __FUNCTION__, streamId, streamSetId, streamSet.allocatedBufferWaterMark);
+        }
+
+        size_t attachedBufferCount = streamSet.attachedBufferCountMap.valueFor(streamId);
+        if (attachedBufferCount <= bufferCount) {
+            ALOGV("%s: stream %d has no buffer available to free.", __FUNCTION__, streamId);
+        }
+
+        bool freeBufferIsAttached = (attachedBufferCount > bufferCount);
+        if (freeBufferIsAttached &&
+                totalAllocatedBufferCount > streamSet.allocatedBufferWaterMark &&
+                attachedBufferCount > bufferCount + BUFFER_FREE_THRESHOLD) {
+            ALOGV("%s: free a buffer from stream %d", __FUNCTION__, streamId);
+            *shouldFreeBuffer = true;
+        }
     } else {
         // TODO: implement gralloc V1 support
         return BAD_VALUE;
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.h b/services/camera/libcameraservice/device3/Camera3BufferManager.h
index 549bd99..025062e 100644
--- a/services/camera/libcameraservice/device3/Camera3BufferManager.h
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.h
@@ -137,14 +137,17 @@
      * buffer has been reused. The manager will call detachBuffer on the stream
      * if it needs the released buffer otherwise.
      *
+     * When shouldFreeBuffer is set to true, caller must detach and free one buffer from the
+     * buffer queue, and then call notifyBufferRemoved to update the manager.
+     *
      * Return values:
      *
      *  OK:        Buffer release was processed succesfully
      *  BAD_VALUE: stream ID or streamSetId are invalid, or stream ID and stream set ID
      *             combination doesn't match what was registered, or this stream wasn't registered
-     *             to this buffer manager before.
+     *             to this buffer manager before, or shouldFreeBuffer is null/
      */
-    status_t onBufferReleased(int streamId, int streamSetId);
+    status_t onBufferReleased(int streamId, int streamSetId, /*out*/bool* shouldFreeBuffer);
 
     /**
      * This method notifies the manager that certain buffers has been removed from the
@@ -165,11 +168,29 @@
     status_t onBuffersRemoved(int streamId, int streamSetId, size_t count);
 
     /**
+     * This method notifiers the manager that a buffer is freed from the buffer queue, usually
+     * because onBufferReleased signals the caller to free a buffer via the shouldFreeBuffer flag.
+     */
+    void notifyBufferRemoved(int streamId, int streamSetId);
+
+    /**
      * Dump the buffer manager statistics.
      */
     void     dump(int fd, const Vector<String16> &args) const;
 
 private:
+    // allocatedBufferWaterMark will be decreased when:
+    //   numAllocatedBuffersThisSet > numHandoutBuffersThisSet + BUFFER_WATERMARK_DEC_THRESHOLD
+    // This allows the watermark go back down after a burst of buffer requests
+    static const int BUFFER_WATERMARK_DEC_THRESHOLD = 3;
+
+    // onBufferReleased will set shouldFreeBuffer to true when:
+    //   numAllocatedBuffersThisSet > allocatedBufferWaterMark AND
+    //   numAllocatedBuffersThisStream > numHandoutBuffersThisStream + BUFFER_FREE_THRESHOLD
+    // So after a burst of buffer requests and back to steady state, the buffer queue should have
+    // (BUFFER_FREE_THRESHOLD + steady state handout buffer count) buffers.
+    static const int BUFFER_FREE_THRESHOLD = 3;
+
     /**
      * Lock to synchronize the access to the methods of this class.
      */
@@ -279,6 +300,11 @@
      */
     bool checkIfStreamRegisteredLocked(int streamId, int streamSetId) const;
 
+    /**
+     * Check if other streams in the stream set has extra buffer available to be freed, and
+     * free one if so.
+     */
+    status_t checkAndFreeBufferOnOtherStreamsLocked(int streamId, int streamSetId);
 };
 
 } // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 56174fa..ec0f508 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -691,13 +691,24 @@
     }
 
     ALOGV("Stream %d: Buffer released", stream->getId());
+    bool shouldFreeBuffer = false;
     status_t res = stream->mBufferManager->onBufferReleased(
-        stream->getId(), stream->getStreamSetId());
+        stream->getId(), stream->getStreamSetId(), &shouldFreeBuffer);
     if (res != OK) {
         ALOGE("%s: signaling buffer release to buffer manager failed: %s (%d).", __FUNCTION__,
                 strerror(-res), res);
         stream->mState = STATE_ERROR;
     }
+
+    if (shouldFreeBuffer) {
+        sp<GraphicBuffer> buffer;
+        // Detach and free a buffer (when buffer goes out of scope)
+        stream->detachBufferLocked(&buffer, /*fenceFd*/ nullptr);
+        if (buffer.get() != nullptr) {
+            stream->mBufferManager->notifyBufferRemoved(
+                    stream->getId(), stream->getStreamSetId());
+        }
+    }
 }
 
 void Camera3OutputStream::onBuffersRemovedLocked(
@@ -712,7 +723,10 @@
 
 status_t Camera3OutputStream::detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) {
     Mutex::Autolock l(mLock);
+    return detachBufferLocked(buffer, fenceFd);
+}
 
+status_t Camera3OutputStream::detachBufferLocked(sp<GraphicBuffer>* buffer, int* fenceFd) {
     ALOGV("Stream %d: detachBuffer", getId());
     if (buffer == nullptr) {
         return BAD_VALUE;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 86676e4..98ffb73 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -263,7 +263,11 @@
 
     virtual status_t getEndpointUsage(uint32_t *usage) const;
 
+    /**
+     * Private methods
+     */
     void onBuffersRemovedLocked(const std::vector<sp<GraphicBuffer>>&);
+    status_t detachBufferLocked(sp<GraphicBuffer>* buffer, int* fenceFd);
 
 }; // class Camera3OutputStream
 
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 6b59e81..494b18e 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -76,7 +76,7 @@
     }
 
     mSampleRate = configurationInput.getSampleRate();
-    if (mSampleRate == AAUDIO_FORMAT_UNSPECIFIED) {
+    if (mSampleRate == AAUDIO_UNSPECIFIED) {
         mSampleRate = mServiceEndpoint->getSampleRate();
     } else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
         ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need %d",
@@ -85,7 +85,7 @@
     }
 
     mSamplesPerFrame = configurationInput.getSamplesPerFrame();
-    if (mSamplesPerFrame == AAUDIO_FORMAT_UNSPECIFIED) {
+    if (mSamplesPerFrame == AAUDIO_UNSPECIFIED) {
         mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
     } else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
         ALOGE("AAudioServiceStreamShared::open(), mSamplesPerFrame = %d, need %d",
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
index f7a73c3..beb7c09 100644
--- a/services/radio/RadioService.cpp
+++ b/services/radio/RadioService.cpp
@@ -34,6 +34,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
+#include <binder/PermissionCache.h>
 #include <hardware/radio.h>
 #include <media/AudioSystem.h>
 #include "RadioService.h"
@@ -43,6 +44,8 @@
 
 static const char kRadioTunerAudioDeviceName[] = "Radio tuner source";
 
+static const String16 RADIO_PERMISSION("android.permission.ACCESS_FM_RADIO");
+
 RadioService::RadioService()
     : BnRadioService(), mNextUniqueId(1)
 {
@@ -84,6 +87,9 @@
 status_t RadioService::listModules(struct radio_properties *properties,
                              uint32_t *numModules)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     ALOGV("listModules");
 
     AutoMutex lock(mServiceLock);
@@ -104,6 +110,9 @@
                         bool withAudio,
                         sp<IRadio>& radio)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     ALOGV("%s %d config %p withAudio %d", __FUNCTION__, handle, config, withAudio);
 
     AutoMutex lock(mServiceLock);
@@ -717,6 +726,9 @@
 
 status_t RadioService::ModuleClient::setConfiguration(const struct radio_band_config *config)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     AutoMutex lock(mLock);
     status_t status = NO_ERROR;
     ALOGV("%s locked", __FUNCTION__);
@@ -738,6 +750,9 @@
 
 status_t RadioService::ModuleClient::getConfiguration(struct radio_band_config *config)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     AutoMutex lock(mLock);
     status_t status = NO_ERROR;
     ALOGV("%s locked", __FUNCTION__);
@@ -756,6 +771,9 @@
 
 status_t RadioService::ModuleClient::setMute(bool mute)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     sp<Module> module;
     {
         Mutex::Autolock _l(mLock);
@@ -774,6 +792,9 @@
 
 status_t RadioService::ModuleClient::getMute(bool *mute)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     sp<Module> module;
     {
         Mutex::Autolock _l(mLock);
@@ -788,6 +809,9 @@
 
 status_t RadioService::ModuleClient::scan(radio_direction_t direction, bool skipSubChannel)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
@@ -801,6 +825,9 @@
 
 status_t RadioService::ModuleClient::step(radio_direction_t direction, bool skipSubChannel)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
@@ -814,6 +841,9 @@
 
 status_t RadioService::ModuleClient::tune(uint32_t channel, uint32_t subChannel)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
@@ -827,6 +857,9 @@
 
 status_t RadioService::ModuleClient::cancel()
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
@@ -840,6 +873,9 @@
 
 status_t RadioService::ModuleClient::getProgramInformation(struct radio_program_info *info)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
@@ -854,6 +890,9 @@
 
 status_t RadioService::ModuleClient::hasControl(bool *hasControl)
 {
+    if (!PermissionCache::checkCallingPermission(RADIO_PERMISSION)) {
+        return PERMISSION_DENIED;
+    }
     Mutex::Autolock lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     *hasControl = mTuner != 0;