Merge "libaudioprocessing: clamp application provided float audio" into pi-dev
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 61b5127..5d0f68e 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -22,7 +22,6 @@
 #include <android/hidl/manager/1.0/IServiceManager.h>
 
 #include <binder/IMemory.h>
-#include <cutils/native_handle.h>
 #include <hidlmemory/FrameworkUtils.h>
 #include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -224,10 +223,14 @@
     Mutex::Autolock autoLock(mLock);
 
     if (mInitCheck != OK) {
-        return mInitCheck;
+        return false;
     }
 
-    return mPlugin->requiresSecureDecoderComponent(hidl_string(mime));
+    Return<bool> hResult = mPlugin->requiresSecureDecoderComponent(hidl_string(mime));
+    if (!hResult.isOk()) {
+        return false;
+    }
+    return hResult;
 }
 
 
@@ -245,17 +248,12 @@
         ALOGE("setHeapBase(): heap is NULL");
         return -1;
     }
-    native_handle_t* nativeHandle = native_handle_create(1, 0);
-    if (!nativeHandle) {
-        ALOGE("setHeapBase(), failed to create native handle");
-        return -1;
-    }
 
     Mutex::Autolock autoLock(mLock);
 
     int32_t seqNum = mHeapSeqNum++;
     sp<HidlMemory> hidlMemory = fromHeap(heap);
-    mHeapBases.add(seqNum, mNextBufferId);
+    mHeapBases.add(seqNum, HeapBase(mNextBufferId, heap->getSize()));
     Return<void> hResult = mPlugin->setSharedBufferBase(*hidlMemory, mNextBufferId++);
     ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed");
     return seqNum;
@@ -280,10 +278,26 @@
         return UNEXPECTED_NULL;
     }
 
-    // memory must be in the declared heap
-    CHECK(mHeapBases.indexOfKey(seqNum) >= 0);
+    // memory must be in one of the heaps that have been set
+    if (mHeapBases.indexOfKey(seqNum) < 0) {
+        return UNKNOWN_ERROR;
+    }
 
-    buffer->bufferId = mHeapBases.valueFor(seqNum);
+    // heap must be the same size as the one that was set in setHeapBase
+    if (mHeapBases.valueFor(seqNum).getSize() != heap->getSize()) {
+        android_errorWriteLog(0x534e4554, "76221123");
+        return UNKNOWN_ERROR;
+     }
+
+    // memory must be within the address space of the heap
+    if (memory->pointer() != static_cast<uint8_t *>(heap->getBase()) + memory->offset()  ||
+            heap->getSize() < memory->offset() + memory->size() ||
+            SIZE_MAX - memory->offset() < memory->size()) {
+        android_errorWriteLog(0x534e4554, "76221123");
+        return UNKNOWN_ERROR;
+    }
+
+    buffer->bufferId = mHeapBases.valueFor(seqNum).getBufferId();
     buffer->offset = offset >= 0 ? offset : 0;
     buffer->size = size;
     return OK;
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 4e8ad52..cf08610 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -68,10 +68,12 @@
 
 template<typename T>
 std::string toBase64StringNoPad(const T* data, size_t size) {
-    if (size == 0) {
+    // Note that the base 64 conversion only works with arrays of single-byte
+    // values. If the source is empty or is not an array of single-byte values,
+    // return empty string.
+    if (size == 0 || sizeof(data[0]) != 1) {
       return "";
     }
-    CHECK(sizeof(data[0] == 1));
 
     android::AString outputString;
     encodeBase64(data, size, &outputString);
@@ -1186,9 +1188,9 @@
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
-    Status status = mPlugin->setCipherAlgorithm(toHidlVec(sessionId),
+    Return<Status> status = mPlugin->setCipherAlgorithm(toHidlVec(sessionId),
             toHidlString(algorithm));
-    return toStatusT(status);
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
 }
 
 status_t DrmHal::setMacAlgorithm(Vector<uint8_t> const &sessionId,
@@ -1198,9 +1200,9 @@
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
-    Status status = mPlugin->setMacAlgorithm(toHidlVec(sessionId),
+    Return<Status> status = mPlugin->setMacAlgorithm(toHidlVec(sessionId),
             toHidlString(algorithm));
-    return toStatusT(status);
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
 }
 
 status_t DrmHal::encrypt(Vector<uint8_t> const &sessionId,
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index fce1717..4fed707 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -29,6 +29,7 @@
 using ::android::String16;
 using ::android::String8;
 using ::android::drm_metrics::DrmFrameworkMetrics;
+using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::KeyStatusType;
@@ -192,6 +193,13 @@
     }
 }
 
+inline String16 MakeIndexString(unsigned int index) {
+  std::string str("[");
+  str.append(std::to_string(index));
+  str.append("]");
+  return String16(str.c_str());
+}
+
 } // namespace
 
 namespace android {
@@ -370,9 +378,11 @@
     }
 
     int groupIndex = 0;
+    std::map<String16, int> indexMap;
     for (const auto &hidlMetricGroup : hidlMetricGroups) {
         PersistableBundle bundleMetricGroup;
         for (const auto &hidlMetric : hidlMetricGroup.metrics) {
+            String16 metricName(hidlMetric.name.c_str());
             PersistableBundle bundleMetric;
             // Add metric component values.
             for (const auto &value : hidlMetric.values) {
@@ -388,14 +398,22 @@
             // Add attributes to the bundle metric.
             bundleMetric.putPersistableBundle(String16("attributes"),
                                               bundleMetricAttributes);
+            // Add one layer of indirection, allowing for repeated metric names.
+            PersistableBundle repeatedMetrics;
+            bundleMetricGroup.getPersistableBundle(metricName,
+                                                   &repeatedMetrics);
+            int index = indexMap[metricName];
+            repeatedMetrics.putPersistableBundle(MakeIndexString(index),
+                                                 bundleMetric);
+            indexMap[metricName] = ++index;
+
             // Add the bundle metric to the group of metrics.
-            bundleMetricGroup.putPersistableBundle(
-                String16(hidlMetric.name.c_str()), bundleMetric);
+            bundleMetricGroup.putPersistableBundle(metricName,
+                                                   repeatedMetrics);
         }
         // Add the bundle metric group to the collection of groups.
-        bundleMetricGroups->putPersistableBundle(
-            String16(std::to_string(groupIndex).c_str()), bundleMetricGroup);
-        groupIndex++;
+        bundleMetricGroups->putPersistableBundle(MakeIndexString(groupIndex++),
+                                                 bundleMetricGroup);
     }
 
     return OK;
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index 1a20342..64aa9d0 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -429,7 +429,8 @@
   DrmMetricGroup hidlMetricGroup =
       { { {
               "open_session_ok",
-              { { "status", DrmMetricGroup::ValueType::INT64_TYPE, (int64_t) Status::OK, 0.0, "" } },
+              { { "status", DrmMetricGroup::ValueType::INT64_TYPE,
+                  (int64_t) Status::OK, 0.0, "" } },
               { { "count", DrmMetricGroup::ValueType::INT64_TYPE, 3, 0.0, "" } }
           },
           {
@@ -444,25 +445,28 @@
                                                      &bundleMetricGroups));
   ASSERT_EQ(1U, bundleMetricGroups.size());
   PersistableBundle bundleMetricGroup;
-  ASSERT_TRUE(bundleMetricGroups.getPersistableBundle(String16("0"), &bundleMetricGroup));
+  ASSERT_TRUE(bundleMetricGroups.getPersistableBundle(String16("[0]"), &bundleMetricGroup));
   ASSERT_EQ(2U, bundleMetricGroup.size());
 
   // Verify each metric.
   PersistableBundle metric;
   ASSERT_TRUE(bundleMetricGroup.getPersistableBundle(String16("open_session_ok"), &metric));
+  PersistableBundle metricInstance;
+  ASSERT_TRUE(metric.getPersistableBundle(String16("[0]"), &metricInstance));
   int64_t value = 0;
-  ASSERT_TRUE(metric.getLong(String16("count"), &value));
+  ASSERT_TRUE(metricInstance.getLong(String16("count"), &value));
   ASSERT_EQ(3, value);
   PersistableBundle attributeBundle;
-  ASSERT_TRUE(metric.getPersistableBundle(String16("attributes"), &attributeBundle));
+  ASSERT_TRUE(metricInstance.getPersistableBundle(String16("attributes"), &attributeBundle));
   ASSERT_TRUE(attributeBundle.getLong(String16("status"), &value));
   ASSERT_EQ((int64_t) Status::OK, value);
 
   ASSERT_TRUE(bundleMetricGroup.getPersistableBundle(String16("close_session_not_opened"),
                                                      &metric));
-  ASSERT_TRUE(metric.getLong(String16("count"), &value));
+  ASSERT_TRUE(metric.getPersistableBundle(String16("[0]"), &metricInstance));
+  ASSERT_TRUE(metricInstance.getLong(String16("count"), &value));
   ASSERT_EQ(7, value);
-  ASSERT_TRUE(metric.getPersistableBundle(String16("attributes"), &attributeBundle));
+  ASSERT_TRUE(metricInstance.getPersistableBundle(String16("attributes"), &attributeBundle));
   value = 0;
   ASSERT_TRUE(attributeBundle.getLong(String16("status"), &value));
   ASSERT_EQ((int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, value);
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index 8b8824f..712f118 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -37,9 +37,11 @@
     // will calculate frame buffer size if |hasData| is set to true.
     VideoFrame(uint32_t width, uint32_t height,
             uint32_t displayWidth, uint32_t displayHeight,
+            uint32_t tileWidth, uint32_t tileHeight,
             uint32_t angle, uint32_t bpp, bool hasData, size_t iccSize):
         mWidth(width), mHeight(height),
         mDisplayWidth(displayWidth), mDisplayHeight(displayHeight),
+        mTileWidth(tileWidth), mTileHeight(tileHeight),
         mRotationAngle(angle), mBytesPerPixel(bpp), mRowBytes(bpp * width),
         mSize(hasData ? (bpp * width * height) : 0),
         mIccSize(iccSize), mReserved(0) {
@@ -74,6 +76,8 @@
     uint32_t mHeight;          // Decoded image height before rotation
     uint32_t mDisplayWidth;    // Display width before rotation
     uint32_t mDisplayHeight;   // Display height before rotation
+    uint32_t mTileWidth;       // Tile width (0 if image doesn't have grid)
+    uint32_t mTileHeight;      // Tile height (0 if image doesn't have grid)
     int32_t  mRotationAngle;   // Rotation angle, clockwise, should be multiple of 90
     uint32_t mBytesPerPixel;   // Number of bytes per pixel
     uint32_t mRowBytes;        // Number of bytes per row before rotation
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index a1f6e9a..b6787af 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -1397,7 +1397,8 @@
         ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
 
         if (image.isGrid()) {
-            if (size > 12) {
+            // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
+            if (size < 8 || size > 12) {
                 return ERROR_MALFORMED;
             }
             uint8_t buf[12];
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index dad36ec..ce5ca63 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -628,7 +628,7 @@
             track->meta.setInt32(kKeyTrackID, imageIndex);
             track->includes_expensive_metadata = false;
             track->skipTrack = false;
-            track->timescale = 0;
+            track->timescale = 1000000;
         }
     }
 
diff --git a/media/img_utils/include/img_utils/DngUtils.h b/media/img_utils/include/img_utils/DngUtils.h
index 1d8df9c..de8f120 100644
--- a/media/img_utils/include/img_utils/DngUtils.h
+++ b/media/img_utils/include/img_utils/DngUtils.h
@@ -39,11 +39,16 @@
  */
 class ANDROID_API OpcodeListBuilder : public LightRefBase<OpcodeListBuilder> {
     public:
+        // Note that the Adobe DNG 1.4 spec for Bayer phase (defined for the
+        // FixBadPixelsConstant and FixBadPixelsList opcodes) is incorrect. It's
+        // inconsistent with the DNG SDK (cf. dng_negative::SetBayerMosaic and
+        // dng_opcode_FixBadPixelsList::IsGreen), and Adobe confirms that the
+        // spec should be updated to match the SDK.
         enum CfaLayout {
-            CFA_RGGB = 0,
-            CFA_GRBG,
-            CFA_GBRG,
+            CFA_GRBG = 0,
+            CFA_RGGB,
             CFA_BGGR,
+            CFA_GBRG,
         };
 
         OpcodeListBuilder();
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
index 2623697..f618d3d 100644
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -495,7 +495,7 @@
     }
 
     void printStatus() override {
-        printf("state = %d, echo gain = %f ", mState, mEchoGain);
+        printf("st = %d, echo gain = %f ", mState, mEchoGain);
     }
 
     static void sendImpulse(float *outputData, int outputChannelCount) {
@@ -670,7 +670,7 @@
         printf(LOOPBACK_RESULT_TAG "phase.offset       = %7.5f\n", mPhaseOffset);
         printf(LOOPBACK_RESULT_TAG "ref.phase          = %7.5f\n", mPhase);
         printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated);
-        printf(LOOPBACK_RESULT_TAG "sine.period        = %6d\n", mPeriod);
+        printf(LOOPBACK_RESULT_TAG "sine.period        = %6d\n", mSinePeriod);
         printf(LOOPBACK_RESULT_TAG "test.state         = %6d\n", mState);
         printf(LOOPBACK_RESULT_TAG "frame.count        = %6d\n", mFrameCounter);
         // Did we ever get a lock?
@@ -684,7 +684,7 @@
     }
 
     void printStatus() override {
-        printf("  state = %d, glitches = %3d,", mState, mGlitchCount);
+        printf("st = %d, #gl = %3d,", mState, mGlitchCount);
     }
 
     double calculateMagnitude(double *phasePtr = NULL) {
@@ -709,6 +709,8 @@
     void process(float *inputData, int inputChannelCount,
                  float *outputData, int outputChannelCount,
                  int numFrames) override {
+        mProcessCount++;
+
         float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
         if (peak > mPeakAmplitude) {
             mPeakAmplitude = peak;
@@ -720,6 +722,7 @@
             float sinOut = sinf(mPhase);
 
             switch (mState) {
+                case STATE_IDLE:
                 case STATE_IMMUNE:
                 case STATE_WAITING_FOR_SIGNAL:
                     break;
@@ -728,7 +731,7 @@
                     mCosAccumulator += sample * cosf(mPhase);
                     mFramesAccumulated++;
                     // Must be a multiple of the period or the calculation will not be accurate.
-                    if (mFramesAccumulated == mPeriod * 4) {
+                    if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) {
                         mPhaseOffset = 0.0;
                         mMagnitude = calculateMagnitude(&mPhaseOffset);
                         if (mMagnitude > mThreshold) {
@@ -754,7 +757,22 @@
                         //       mFrameCounter, mGlitchCount, predicted, sample);
                         mState = STATE_IMMUNE;
                         //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter);
-                        mDownCounter = mPeriod;  // Set duration of IMMUNE state.
+                        mDownCounter = mSinePeriod;  // Set duration of IMMUNE state.
+                    }
+
+                    // Track incoming signal and slowly adjust magnitude to account
+                    // for drift in the DRC or AGC.
+                    mSinAccumulator += sample * sinOut;
+                    mCosAccumulator += sample * cosf(mPhase);
+                    mFramesAccumulated++;
+                    // Must be a multiple of the period or the calculation will not be accurate.
+                    if (mFramesAccumulated == mSinePeriod) {
+                        const double coefficient = 0.1;
+                        double phaseOffset = 0.0;
+                        double magnitude = calculateMagnitude(&phaseOffset);
+                        // One pole averaging filter.
+                        mMagnitude = (mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient);
+                        resetAccumulator();
                     }
                 } break;
             }
@@ -775,6 +793,9 @@
 
         // Do these once per buffer.
         switch (mState) {
+            case STATE_IDLE:
+                mState = STATE_IMMUNE; // so we can tell when
+                break;
             case STATE_IMMUNE:
                 mDownCounter -= numFrames;
                 if (mDownCounter <= 0) {
@@ -805,21 +826,29 @@
     void reset() override {
         mGlitchCount = 0;
         mState = STATE_IMMUNE;
-        mPhaseIncrement = 2.0 * M_PI / mPeriod;
-        printf("phaseInc = %f for period %d\n", mPhaseIncrement, mPeriod);
+        mDownCounter = IMMUNE_FRAME_COUNT;
+        mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
+        printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
         resetAccumulator();
+        mProcessCount = 0;
     }
 
 private:
 
     enum sine_state_t {
+        STATE_IDLE,
         STATE_IMMUNE,
         STATE_WAITING_FOR_SIGNAL,
         STATE_WAITING_FOR_LOCK,
         STATE_LOCKED
     };
 
-    int     mPeriod = 79;
+    enum constants {
+        IMMUNE_FRAME_COUNT = 48 * 500,
+        PERIODS_NEEDED_FOR_LOCK = 8
+    };
+
+    int     mSinePeriod = 79;
     double  mPhaseIncrement = 0.0;
     double  mPhase = 0.0;
     double  mPhaseOffset = 0.0;
@@ -828,18 +857,19 @@
     double  mThreshold = 0.005;
     double  mTolerance = 0.01;
     int32_t mFramesAccumulated = 0;
+    int32_t mProcessCount = 0;
     double  mSinAccumulator = 0.0;
     double  mCosAccumulator = 0.0;
     int32_t mGlitchCount = 0;
     double  mPeakAmplitude = 0.0;
-    int     mDownCounter = 4000;
+    int     mDownCounter = IMMUNE_FRAME_COUNT;
     int32_t mFrameCounter = 0;
     float   mOutputAmplitude = 0.75;
 
     PseudoRandom  mWhiteNoise;
     float   mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
 
-    sine_state_t  mState = STATE_IMMUNE;
+    sine_state_t  mState = STATE_IDLE;
 };
 
 
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 0ebcdbd..26d1e4b 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -38,21 +38,25 @@
 // Tag for machine readable results as property = value pairs
 #define RESULT_TAG              "RESULT: "
 #define NUM_SECONDS             5
+#define PERIOD_MILLIS           1000
 #define NUM_INPUT_CHANNELS      1
 #define FILENAME_ALL            "/data/loopback_all.wav"
 #define FILENAME_ECHOS          "/data/loopback_echos.wav"
-#define APP_VERSION             "0.2.01"
+#define APP_VERSION             "0.2.03"
+
+constexpr int kNumCallbacksToDrain   = 20;
+constexpr int kNumCallbacksToDiscard = 20;
 
 struct LoopbackData {
     AAudioStream      *inputStream = nullptr;
     int32_t            inputFramesMaximum = 0;
     int16_t           *inputShortData = nullptr;
     float             *inputFloatData = nullptr;
-    int16_t            peakShort = 0;
     aaudio_format_t    actualInputFormat = AAUDIO_FORMAT_INVALID;
     int32_t            actualInputChannelCount = 0;
     int32_t            actualOutputChannelCount = 0;
-    int32_t            inputBuffersToDiscard = 10;
+    int32_t            numCallbacksToDrain = kNumCallbacksToDrain;
+    int32_t            numCallbacksToDiscard = kNumCallbacksToDiscard;
     int32_t            minNumFrames = INT32_MAX;
     int32_t            maxNumFrames = 0;
     int32_t            insufficientReadCount = 0;
@@ -131,23 +135,32 @@
         myData->minNumFrames = numFrames;
     }
 
-    if (myData->inputBuffersToDiscard > 0) {
+    // Silence the output.
+    int32_t numBytes = numFrames * myData->actualOutputChannelCount * sizeof(float);
+    memset(audioData, 0 /* value */, numBytes);
+
+    if (myData->numCallbacksToDrain > 0) {
         // Drain the input.
         do {
             framesRead = readFormattedData(myData, numFrames);
-            if (framesRead < 0) {
-                result = AAUDIO_CALLBACK_RESULT_STOP;
-            } else if (framesRead > 0) {
-                myData->inputBuffersToDiscard--;
-            }
+            // Ignore errors because input stream may not be started yet.
         } while (framesRead > 0);
+        myData->numCallbacksToDrain--;
 
-        // Silence the output.
-        int32_t numBytes = numFrames * myData->actualOutputChannelCount * sizeof(float);
-        memset(audioData, 0 /* value */, numBytes);
+    } else if (myData->numCallbacksToDiscard > 0) {
+        // Ignore. Allow the input to fill back up to equilibrium with the output.
+        framesRead = readFormattedData(myData, numFrames);
+        if (framesRead < 0) {
+            result = AAUDIO_CALLBACK_RESULT_STOP;
+        }
+        myData->numCallbacksToDiscard--;
 
     } else {
 
+        int32_t numInputBytes = numFrames * myData->actualInputChannelCount * sizeof(float);
+        memset(myData->inputFloatData, 0 /* value */, numInputBytes);
+
+        // Process data after equilibrium.
         int64_t inputFramesWritten = AAudioStream_getFramesWritten(myData->inputStream);
         int64_t inputFramesRead = AAudioStream_getFramesRead(myData->inputStream);
         int64_t framesAvailable = inputFramesWritten - inputFramesRead;
@@ -155,6 +168,7 @@
         if (framesRead < 0) {
             result = AAUDIO_CALLBACK_RESULT_STOP;
         } else {
+
             if (framesRead < numFrames) {
                 if(framesRead < (int32_t) framesAvailable) {
                     printf("insufficient but numFrames = %d, framesRead = %d, available = %d\n",
@@ -172,13 +186,13 @@
             // Save for later.
             myData->audioRecording.write(myData->inputFloatData,
                                          myData->actualInputChannelCount,
-                                         framesRead);
+                                         numFrames);
             // Analyze the data.
             myData->loopbackProcessor->process(myData->inputFloatData,
                                                myData->actualInputChannelCount,
                                                outputData,
                                                myData->actualOutputChannelCount,
-                                               framesRead);
+                                               numFrames);
             myData->isDone = myData->loopbackProcessor->isDone();
             if (myData->isDone) {
                 result = AAUDIO_CALLBACK_RESULT_STOP;
@@ -366,7 +380,9 @@
     }
 
     int32_t requestedDuration = argParser.getDurationSeconds();
-    int32_t recordingDuration = std::min(60, requestedDuration);
+    int32_t requestedDurationMillis = requestedDuration * MILLIS_PER_SECOND;
+    int32_t timeMillis = 0;
+    int32_t recordingDuration = std::min(60 * 5, requestedDuration);
 
     switch(testMode) {
         case TEST_SINE_MAGNITUDE:
@@ -449,7 +465,6 @@
 
     // Allocate a buffer for the audio data.
     loopbackData.inputFramesMaximum = 32 * AAudioStream_getFramesPerBurst(inputStream);
-    loopbackData.inputBuffersToDiscard = 200;
 
     if (loopbackData.actualInputFormat == AAUDIO_FORMAT_PCM_I16) {
         loopbackData.inputShortData = new int16_t[loopbackData.inputFramesMaximum
@@ -460,13 +475,7 @@
 
     loopbackData.loopbackProcessor->reset();
 
-    result = recorder.start();
-    if (result != AAUDIO_OK) {
-        printf("ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
-               result, AAudio_convertResultToText(result));
-        goto finish;
-    }
-
+    // Start OUTPUT first so INPUT does not overflow.
     result = player.start();
     if (result != AAUDIO_OK) {
         printf("ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
@@ -474,9 +483,15 @@
         goto finish;
     }
 
-    printf("------- sleep while the callback runs --------------\n");
-    fflush(stdout);
-    for (int i = requestedDuration; i > 0 ; i--) {
+    result = recorder.start();
+    if (result != AAUDIO_OK) {
+        printf("ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
+               result, AAudio_convertResultToText(result));
+        goto finish;
+    }
+
+    printf("------- sleep and log while the callback runs --------------\n");
+    while (timeMillis <= requestedDurationMillis) {
         if (loopbackData.inputError != AAUDIO_OK) {
             printf("  ERROR on input stream\n");
             break;
@@ -487,10 +502,9 @@
                 printf("  test says it is done!\n");
                 break;
         } else {
-            sleep(1);
-            printf("%4d: ", i);
+            // Log a line of stream data.
+            printf("%7.3f: ", 0.001 * timeMillis); // display in seconds
             loopbackData.loopbackProcessor->printStatus();
-
             printf(" insf %3d,", (int) loopbackData.insufficientReadCount);
 
             int64_t inputFramesWritten = AAudioStream_getFramesWritten(inputStream);
@@ -498,7 +512,7 @@
             int64_t outputFramesWritten = AAudioStream_getFramesWritten(outputStream);
             int64_t outputFramesRead = AAudioStream_getFramesRead(outputStream);
             static const int textOffset = strlen("AAUDIO_STREAM_STATE_"); // strip this off
-            printf(" INPUT: wr %7lld - rd %7lld = %5lld, state %s, oruns %3d | ",
+            printf(" | INPUT: wr %7lld - rd %7lld = %5lld, st %8s, oruns %3d",
                    (long long) inputFramesWritten,
                    (long long) inputFramesRead,
                    (long long) (inputFramesWritten - inputFramesRead),
@@ -506,7 +520,7 @@
                            AAudioStream_getState(inputStream))[textOffset],
                    AAudioStream_getXRunCount(inputStream));
 
-            printf(" OUTPUT: wr %7lld - rd %7lld = %5lld, state %s, uruns %3d\n",
+            printf(" | OUTPUT: wr %7lld - rd %7lld = %5lld, st %8s, uruns %3d\n",
                    (long long) outputFramesWritten,
                    (long long) outputFramesRead,
                     (long long) (outputFramesWritten - outputFramesRead),
@@ -515,6 +529,9 @@
                    AAudioStream_getXRunCount(outputStream)
             );
         }
+        int32_t periodMillis = (timeMillis < 2000) ? PERIOD_MILLIS / 4 : PERIOD_MILLIS;
+        usleep(periodMillis * 1000);
+        timeMillis += periodMillis;
     }
 
     result = player.stop();
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 1981ba3..505f2ee 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -350,7 +350,7 @@
     mTimestampPosition.set(getFramesRead());
     mAudioRecord->stop();
     mCallbackEnabled.store(false);
-    mFramesRead.reset32();
+    mFramesWritten.reset32(); // service writes frames, service position reset on flush
     mTimestampPosition.reset32();
     // Pass false to prevent errorCallback from being called after disconnect
     // when app has already requested a stop().
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 9653601..505cd77 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -310,7 +310,7 @@
     setState(AAUDIO_STREAM_STATE_FLUSHING);
     incrementFramesRead(getFramesWritten() - getFramesRead());
     mAudioTrack->flush();
-    mFramesWritten.reset32();
+    mFramesRead.reset32(); // service reads frames, service position reset on flush
     mTimestampPosition.reset32();
     return AAUDIO_OK;
 }
@@ -324,7 +324,7 @@
     setState(AAUDIO_STREAM_STATE_STOPPING);
     incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
     mTimestampPosition.set(getFramesWritten());
-    mFramesWritten.reset32();
+    mFramesRead.reset32(); // service reads frames, service position reset on stop
     mTimestampPosition.reset32();
     mAudioTrack->stop();
     mCallbackEnabled.store(false);
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index a8369c2..f9df5b1 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -99,6 +99,11 @@
     static constexpr char kAudioRecordLatency[] = "android.media.audiorecord.latency";
     static constexpr char kAudioRecordSampleRate[] = "android.media.audiorecord.samplerate";
     static constexpr char kAudioRecordChannelCount[] = "android.media.audiorecord.channels";
+    static constexpr char kAudioRecordCreated[] = "android.media.audiorecord.createdMs";
+    static constexpr char kAudioRecordDuration[] = "android.media.audiorecord.durationMs";
+    static constexpr char kAudioRecordCount[] = "android.media.audiorecord.n";
+    static constexpr char kAudioRecordError[] = "android.media.audiorecord.errcode";
+    static constexpr char kAudioRecordErrorFunction[] = "android.media.audiorecord.errfunc";
 
     // constructor guarantees mAnalyticsItem is valid
 
@@ -109,6 +114,24 @@
                                audioFormatTypeString(record->mFormat).c_str());
     mAnalyticsItem->setCString(kAudioRecordSource,
                                audioSourceString(record->mAttributes.source).c_str());
+
+    // log total duration recording, including anything currently running [and count].
+    nsecs_t active = 0;
+    if (mStartedNs != 0) {
+        active = systemTime() - mStartedNs;
+    }
+    mAnalyticsItem->setInt64(kAudioRecordDuration, (mDurationNs + active) / (1000 * 1000));
+    mAnalyticsItem->setInt32(kAudioRecordCount, mCount);
+
+    // XXX I don't know that this adds a lot of value, long term
+    if (mCreatedNs != 0) {
+        mAnalyticsItem->setInt64(kAudioRecordCreated, mCreatedNs / (1000 * 1000));
+    }
+
+    if (mLastError != NO_ERROR) {
+        mAnalyticsItem->setInt32(kAudioRecordError, mLastError);
+        mAnalyticsItem->setCString(kAudioRecordErrorFunction, mLastErrorFunc.c_str());
+    }
 }
 
 // hand the user a snapshot of the metrics.
@@ -354,6 +377,9 @@
 
 exit:
     mStatus = status;
+    if (status != NO_ERROR) {
+        mMediaMetrics.markError(status, __FUNCTION__);
+    }
     return status;
 }
 
@@ -412,8 +438,14 @@
             get_sched_policy(0, &mPreviousSchedulingGroup);
             androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
         }
+
+        // we've successfully started, log that time
+        mMediaMetrics.logStart(systemTime());
     }
 
+    if (status != NO_ERROR) {
+        mMediaMetrics.markError(status, __FUNCTION__);
+    }
     return status;
 }
 
@@ -438,6 +470,9 @@
         setpriority(PRIO_PROCESS, 0, mPreviousPriority);
         set_sched_policy(0, mPreviousSchedulingGroup);
     }
+
+    // we've successfully started, log that time
+    mMediaMetrics.logStop(systemTime());
 }
 
 bool AudioRecord::stopped() const
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 86791c2..ab9efe8 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -806,7 +806,7 @@
         return;
     }
     AutoMutex lock(mLock);
-    if (mState == STATE_ACTIVE || mState == STATE_FLUSHED) {
+    if (mState == STATE_ACTIVE) {
         return;
     }
     flush_l();
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index abb502b..eca6fee 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -876,7 +876,8 @@
         case SET_DEVICE_CONNECTION_STATE:
         case HANDLE_DEVICE_CONFIG_CHANGE:
         case SET_PHONE_STATE:
-        case SET_FORCE_USE:
+//FIXME: Allow SET_FORCE_USE calls from system apps until a better use case routing API is available
+//      case SET_FORCE_USE:
         case INIT_STREAM_VOLUME:
         case SET_STREAM_VOLUME:
         case REGISTER_POLICY_MIXES:
@@ -895,20 +896,7 @@
             break;
     }
 
-    // FIXME: extend timeout for SET_DEVICE_CONNECTION_STATE and HANDLE_DEVICE_CONFIG_CHANGE
-    // while we investigate why BT A2DP device connection/disconnection can sometimes
-    // take more than 5 seconds
-    uint32_t timeoutMs = TimeCheck::kDefaultTimeOutMs;
-    switch (code) {
-        case SET_DEVICE_CONNECTION_STATE:
-        case HANDLE_DEVICE_CONFIG_CHANGE:
-            timeoutMs *= 2;
-            break;
-        default:
-            break;
-    }
-
-    TimeCheck check("IAudioPolicyService", timeoutMs);
+    TimeCheck check("IAudioPolicyService");
 
     switch (code) {
         case SET_DEVICE_CONNECTION_STATE: {
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index c07c397..cf446a5 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -704,7 +704,10 @@
 private:
     class MediaMetrics {
       public:
-        MediaMetrics() : mAnalyticsItem(new MediaAnalyticsItem("audiorecord")) {
+        MediaMetrics() : mAnalyticsItem(new MediaAnalyticsItem("audiorecord")),
+                         mCreatedNs(systemTime(SYSTEM_TIME_REALTIME)),
+                         mStartedNs(0), mDurationNs(0), mCount(0),
+                         mLastError(NO_ERROR) {
         }
         ~MediaMetrics() {
             // mAnalyticsItem alloc failure will be flagged in the constructor
@@ -715,8 +718,20 @@
         }
         void gather(const AudioRecord *record);
         MediaAnalyticsItem *dup() { return mAnalyticsItem->dup(); }
+
+        void logStart(nsecs_t when) { mStartedNs = when; mCount++; }
+        void logStop(nsecs_t when) { mDurationNs += (when-mStartedNs); mStartedNs = 0;}
+        void markError(status_t errcode, const char *func)
+                 { mLastError = errcode; mLastErrorFunc = func;}
       private:
         std::unique_ptr<MediaAnalyticsItem> mAnalyticsItem;
+        nsecs_t mCreatedNs;     // XXX: perhaps not worth it in production
+        nsecs_t mStartedNs;
+        nsecs_t mDurationNs;
+        int32_t mCount;
+
+        status_t mLastError;
+        std::string mLastErrorFunc;
     };
     MediaMetrics mMediaMetrics;
 };
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index 852252d..aa39443 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -204,15 +204,14 @@
 
     // channel masks have changed, does this track need a downmixer?
     // update to try using our desired format (if we aren't already using it)
-    const audio_format_t prevDownmixerFormat = track->mDownmixRequiresFormat;
     const status_t status = track->prepareForDownmix();
     ALOGE_IF(status != OK,
             "prepareForDownmix error %d, track channel mask %#x, mixer channel mask %#x",
             status, track->channelMask, track->mMixerChannelMask);
 
-    if (prevDownmixerFormat != track->mDownmixRequiresFormat) {
-        track->prepareForReformat(); // because of downmixer, track format may change!
-    }
+    // always do reformat since channel mask changed,
+    // do it after downmix since track format may change!
+    track->prepareForReformat();
 
     if (track->mResampler.get() != nullptr && mixerChannelCountChanged) {
         // resampler channels may have changed.
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 8dae251..01f014f 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -271,17 +271,43 @@
 
 /////////////////////////////////////////////////////////////////////////
 
+struct HeifDecoderImpl::DecodeThread : public Thread {
+    explicit DecodeThread(HeifDecoderImpl *decoder) : mDecoder(decoder) {}
+
+private:
+    HeifDecoderImpl* mDecoder;
+
+    bool threadLoop();
+
+    DISALLOW_EVIL_CONSTRUCTORS(DecodeThread);
+};
+
+bool HeifDecoderImpl::DecodeThread::threadLoop() {
+    return mDecoder->decodeAsync();
+}
+
+/////////////////////////////////////////////////////////////////////////
+
 HeifDecoderImpl::HeifDecoderImpl() :
     // output color format should always be set via setOutputColor(), in case
     // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
     mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
     mCurScanline(0),
+    mWidth(0),
+    mHeight(0),
     mFrameDecoded(false),
     mHasImage(false),
-    mHasVideo(false) {
+    mHasVideo(false),
+    mAvailableLines(0),
+    mNumSlices(1),
+    mSliceHeight(0),
+    mAsyncDecodeDone(false) {
 }
 
 HeifDecoderImpl::~HeifDecoderImpl() {
+    if (mThread != nullptr) {
+        mThread->join();
+    }
 }
 
 bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
@@ -310,22 +336,23 @@
 
     mHasImage = hasImage && !strcasecmp(hasImage, "yes");
     mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
+    sp<IMemory> sharedMem;
     if (mHasImage) {
         // image index < 0 to retrieve primary image
-        mFrameMemory = mRetriever->getImageAtIndex(
+        sharedMem = mRetriever->getImageAtIndex(
                 -1, mOutputColor, true /*metaOnly*/);
     } else if (mHasVideo) {
-        mFrameMemory = mRetriever->getFrameAtTime(0,
+        sharedMem = mRetriever->getFrameAtTime(0,
                 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
                 mOutputColor, true /*metaOnly*/);
     }
 
-    if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+    if (sharedMem == nullptr || sharedMem->pointer() == nullptr) {
         ALOGE("getFrameAtTime: videoFrame is a nullptr");
         return false;
     }
 
-    VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+    VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->pointer());
 
     ALOGV("Meta dimension %dx%d, display %dx%d, angle %d, iccSize %d",
             videoFrame->mWidth,
@@ -344,6 +371,14 @@
                 videoFrame->mIccSize,
                 videoFrame->getFlattenedIccData());
     }
+    mWidth = videoFrame->mWidth;
+    mHeight = videoFrame->mHeight;
+    if (mHasImage && videoFrame->mTileHeight >= 512 && mWidth >= 3000 && mHeight >= 2000 ) {
+        // Try decoding in slices only if the image has tiles and is big enough.
+        mSliceHeight = videoFrame->mTileHeight;
+        mNumSlices = (videoFrame->mHeight + mSliceHeight - 1) / mSliceHeight;
+        ALOGV("mSliceHeight %u, mNumSlices %zu", mSliceHeight, mNumSlices);
+    }
     return true;
 }
 
@@ -376,6 +411,36 @@
     return false;
 }
 
+bool HeifDecoderImpl::decodeAsync() {
+    for (size_t i = 1; i < mNumSlices; i++) {
+        ALOGV("decodeAsync(): decoding slice %zu", i);
+        size_t top = i * mSliceHeight;
+        size_t bottom = (i + 1) * mSliceHeight;
+        if (bottom > mHeight) {
+            bottom = mHeight;
+        }
+        sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
+                -1, mOutputColor, 0, top, mWidth, bottom);
+        {
+            Mutex::Autolock autolock(mLock);
+
+            if (frameMemory == nullptr || frameMemory->pointer() == nullptr) {
+                mAsyncDecodeDone = true;
+                mScanlineReady.signal();
+                break;
+            }
+            mFrameMemory = frameMemory;
+            mAvailableLines = bottom;
+            ALOGV("decodeAsync(): available lines %zu", mAvailableLines);
+            mScanlineReady.signal();
+        }
+    }
+    // Aggressive clear to avoid holding on to resources
+    mRetriever.clear();
+    mDataSource.clear();
+    return false;
+}
+
 bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
     // reset scanline pointer
     mCurScanline = 0;
@@ -384,6 +449,47 @@
         return true;
     }
 
+    // See if we want to decode in slices to allow client to start
+    // scanline processing in parallel with decode. If this fails
+    // we fallback to decoding the full frame.
+    if (mHasImage && mNumSlices > 1) {
+        // get first slice and metadata
+        sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
+                -1, mOutputColor, 0, 0, mWidth, mSliceHeight);
+
+        if (frameMemory == nullptr || frameMemory->pointer() == nullptr) {
+            ALOGE("decode: metadata is a nullptr");
+            return false;
+        }
+
+        VideoFrame* videoFrame = static_cast<VideoFrame*>(frameMemory->pointer());
+
+        if (frameInfo != nullptr) {
+            frameInfo->set(
+                    videoFrame->mWidth,
+                    videoFrame->mHeight,
+                    videoFrame->mRotationAngle,
+                    videoFrame->mBytesPerPixel,
+                    videoFrame->mIccSize,
+                    videoFrame->getFlattenedIccData());
+        }
+
+        mFrameMemory = frameMemory;
+        mAvailableLines = mSliceHeight;
+        mThread = new DecodeThread(this);
+        if (mThread->run("HeifDecode", ANDROID_PRIORITY_FOREGROUND) == OK) {
+            mFrameDecoded = true;
+            return true;
+        }
+
+        // Fallback to decode without slicing
+        mThread.clear();
+        mNumSlices = 1;
+        mSliceHeight = 0;
+        mAvailableLines = 0;
+        mFrameMemory.clear();
+    }
+
     if (mHasImage) {
         // image index < 0 to retrieve primary image
         mFrameMemory = mRetriever->getImageAtIndex(-1, mOutputColor);
@@ -393,14 +499,14 @@
     }
 
     if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
-        ALOGE("getFrameAtTime: videoFrame is a nullptr");
+        ALOGE("decode: videoFrame is a nullptr");
         return false;
     }
 
     VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
     if (videoFrame->mSize == 0 ||
             mFrameMemory->size() < videoFrame->getFlattenedSize()) {
-        ALOGE("getFrameAtTime: videoFrame size is invalid");
+        ALOGE("decode: videoFrame size is invalid");
         return false;
     }
 
@@ -424,36 +530,45 @@
     }
     mFrameDecoded = true;
 
-    // Aggressive clear to avoid holding on to resources
+    // Aggressively clear to avoid holding on to resources
     mRetriever.clear();
     mDataSource.clear();
     return true;
 }
 
-bool HeifDecoderImpl::getScanline(uint8_t* dst) {
+bool HeifDecoderImpl::getScanlineInner(uint8_t* dst) {
     if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
         return false;
     }
     VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
-    if (mCurScanline >= videoFrame->mHeight) {
-        ALOGE("no more scanline available");
-        return false;
-    }
     uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
     memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
     return true;
 }
 
-size_t HeifDecoderImpl::skipScanlines(size_t count) {
-    if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
-        return 0;
+bool HeifDecoderImpl::getScanline(uint8_t* dst) {
+    if (mCurScanline >= mHeight) {
+        ALOGE("no more scanline available");
+        return false;
     }
-    VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
 
+    if (mNumSlices > 1) {
+        Mutex::Autolock autolock(mLock);
+
+        while (!mAsyncDecodeDone && mCurScanline >= mAvailableLines) {
+            mScanlineReady.wait(mLock);
+        }
+        return (mCurScanline < mAvailableLines) ? getScanlineInner(dst) : false;
+    }
+
+    return getScanlineInner(dst);
+}
+
+size_t HeifDecoderImpl::skipScanlines(size_t count) {
     uint32_t oldScanline = mCurScanline;
     mCurScanline += count;
-    if (mCurScanline > videoFrame->mHeight) {
-        mCurScanline = videoFrame->mHeight;
+    if (mCurScanline > mHeight) {
+        mCurScanline = mHeight;
     }
     return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
 }
diff --git a/media/libheif/HeifDecoderImpl.h b/media/libheif/HeifDecoderImpl.h
index 406c2c1..528ee3b 100644
--- a/media/libheif/HeifDecoderImpl.h
+++ b/media/libheif/HeifDecoderImpl.h
@@ -19,6 +19,8 @@
 
 #include "include/HeifDecoderAPI.h"
 #include <system/graphics.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
 #include <utils/RefBase.h>
 
 namespace android {
@@ -49,14 +51,30 @@
     size_t skipScanlines(size_t count) override;
 
 private:
+    struct DecodeThread;
+
     sp<IDataSource> mDataSource;
     sp<MediaMetadataRetriever> mRetriever;
     sp<IMemory> mFrameMemory;
     android_pixel_format_t mOutputColor;
     size_t mCurScanline;
+    uint32_t mWidth;
+    uint32_t mHeight;
     bool mFrameDecoded;
     bool mHasImage;
     bool mHasVideo;
+
+    // Slice decoding only
+    Mutex mLock;
+    Condition mScanlineReady;
+    sp<DecodeThread> mThread;
+    size_t mAvailableLines;
+    size_t mNumSlices;
+    uint32_t mSliceHeight;
+    bool mAsyncDecodeDone;
+
+    bool decodeAsync();
+    bool getScanlineInner(uint8_t* dst);
 };
 
 } // namespace android
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 214117b..590ba1a 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -69,6 +69,7 @@
     SET_DATA_SOURCE_CALLBACK,
     GET_FRAME_AT_TIME,
     GET_IMAGE_AT_INDEX,
+    GET_IMAGE_RECT_AT_INDEX,
     GET_FRAME_AT_INDEX,
     EXTRACT_ALBUM_ART,
     EXTRACT_METADATA,
@@ -187,6 +188,30 @@
         return interface_cast<IMemory>(reply.readStrongBinder());
     }
 
+    sp<IMemory> getImageRectAtIndex(
+            int index, int colorFormat, int left, int top, int right, int bottom)
+    {
+        ALOGV("getImageRectAtIndex: index %d, colorFormat(%d) rect {%d, %d, %d, %d}",
+                index, colorFormat, left, top, right, bottom);
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
+        data.writeInt32(index);
+        data.writeInt32(colorFormat);
+        data.writeInt32(left);
+        data.writeInt32(top);
+        data.writeInt32(right);
+        data.writeInt32(bottom);
+#ifndef DISABLE_GROUP_SCHEDULE_HACK
+        sendSchedPolicy(data);
+#endif
+        remote()->transact(GET_IMAGE_RECT_AT_INDEX, data, &reply);
+        status_t ret = reply.readInt32();
+        if (ret != NO_ERROR) {
+            return NULL;
+        }
+        return interface_cast<IMemory>(reply.readStrongBinder());
+    }
+
     status_t getFrameAtIndex(std::vector<sp<IMemory> > *frames,
             int frameIndex, int numFrames, int colorFormat, bool metaOnly)
     {
@@ -375,6 +400,34 @@
 #endif
             return NO_ERROR;
         } break;
+
+        case GET_IMAGE_RECT_AT_INDEX: {
+            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
+            int index = data.readInt32();
+            int colorFormat = data.readInt32();
+            int left = data.readInt32();
+            int top = data.readInt32();
+            int right = data.readInt32();
+            int bottom = data.readInt32();
+            ALOGV("getImageRectAtIndex: index(%d), colorFormat(%d), rect {%d, %d, %d, %d}",
+                    index, colorFormat, left, top, right, bottom);
+#ifndef DISABLE_GROUP_SCHEDULE_HACK
+            setSchedPolicy(data);
+#endif
+            sp<IMemory> bitmap = getImageRectAtIndex(
+                    index, colorFormat, left, top, right, bottom);
+            if (bitmap != 0) {  // Don't send NULL across the binder interface
+                reply->writeInt32(NO_ERROR);
+                reply->writeStrongBinder(IInterface::asBinder(bitmap));
+            } else {
+                reply->writeInt32(UNKNOWN_ERROR);
+            }
+#ifndef DISABLE_GROUP_SCHEDULE_HACK
+            restoreSchedPolicy();
+#endif
+            return NO_ERROR;
+        } break;
+
         case GET_FRAME_AT_INDEX: {
             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
             int frameIndex = data.readInt32();
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 6f56d0c..272bc30 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -31,6 +31,18 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <utils/Errors.h>
 
+// TODO: remove forward declaration when AMediaExtractor_disconnect is offcially added to NDK
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+media_status_t AMediaExtractor_disconnect(AMediaExtractor *);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
 namespace android {
 
 static const size_t kAESBlockSize = 16;  // AES_BLOCK_SIZE
diff --git a/media/libmedia/include/media/CryptoHal.h b/media/libmedia/include/media/CryptoHal.h
index 4414e9d..ff8789d 100644
--- a/media/libmedia/include/media/CryptoHal.h
+++ b/media/libmedia/include/media/CryptoHal.h
@@ -81,7 +81,20 @@
      */
     status_t mInitCheck;
 
-    KeyedVector<int32_t, uint32_t> mHeapBases;
+    struct HeapBase {
+        HeapBase() : mBufferId(0), mSize(0) {}
+        HeapBase(uint32_t bufferId, size_t size) :
+            mBufferId(bufferId), mSize(size) {}
+
+        uint32_t getBufferId() const {return mBufferId;}
+        size_t getSize() const {return mSize;}
+
+    private:
+        uint32_t mBufferId;
+        size_t mSize;
+    };
+
+    KeyedVector<int32_t, HeapBase> mHeapBases;
     uint32_t mNextBufferId;
     int32_t mHeapSeqNum;
 
diff --git a/media/libmedia/include/media/IMediaMetadataRetriever.h b/media/libmedia/include/media/IMediaMetadataRetriever.h
index 1a04552..c6f422d 100644
--- a/media/libmedia/include/media/IMediaMetadataRetriever.h
+++ b/media/libmedia/include/media/IMediaMetadataRetriever.h
@@ -46,6 +46,8 @@
             int64_t timeUs, int option, int colorFormat, bool metaOnly) = 0;
     virtual sp<IMemory>     getImageAtIndex(
             int index, int colorFormat, bool metaOnly, bool thumbnail) = 0;
+    virtual sp<IMemory>     getImageRectAtIndex(
+            int index, int colorFormat, int left, int top, int right, int bottom) = 0;
     virtual status_t        getFrameAtIndex(
             std::vector<sp<IMemory> > *frames,
             int frameIndex, int numFrames, int colorFormat, bool metaOnly) = 0;
diff --git a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
index 992e230..98d300f 100644
--- a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
+++ b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
@@ -47,6 +47,8 @@
             int64_t timeUs, int option, int colorFormat, bool metaOnly) = 0;
     virtual sp<IMemory> getImageAtIndex(
             int index, int colorFormat, bool metaOnly, bool thumbnail) = 0;
+    virtual sp<IMemory> getImageRectAtIndex(
+            int index, int colorFormat, int left, int top, int right, int bottom) = 0;
     virtual status_t getFrameAtIndex(
             std::vector<sp<IMemory> >* frames,
             int frameIndex, int numFrames, int colorFormat, bool metaOnly) = 0;
@@ -54,27 +56,6 @@
     virtual const char* extractMetadata(int keyCode) = 0;
 };
 
-// MediaMetadataRetrieverInterface
-class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase
-{
-public:
-    MediaMetadataRetrieverInterface() {}
-
-    virtual             ~MediaMetadataRetrieverInterface() {}
-    virtual sp<IMemory> getFrameAtTime(
-            int64_t /*timeUs*/, int /*option*/, int /*colorFormat*/, bool /*metaOnly*/)
-    { return NULL; }
-    virtual sp<IMemory> getImageAtIndex(
-            int /*index*/, int /*colorFormat*/, bool /*metaOnly*/, bool /*thumbnail*/)
-    { return NULL; }
-    virtual status_t getFrameAtIndex(
-            std::vector<sp<IMemory> >* /*frames*/,
-            int /*frameIndex*/, int /*numFrames*/, int /*colorFormat*/, bool /*metaOnly*/)
-    { return ERROR_UNSUPPORTED; }
-    virtual MediaAlbumArt* extractAlbumArt() { return NULL; }
-    virtual const char* extractMetadata(int /*keyCode*/) { return NULL; }
-};
-
 }; // namespace android
 
 #endif // ANDROID_MEDIAMETADATARETRIEVERINTERFACE_H
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index 4cdeeb7..cdef637 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -91,6 +91,8 @@
             int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
     sp<IMemory> getImageAtIndex(int index,
             int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false, bool thumbnail = false);
+    sp<IMemory> getImageRectAtIndex(
+            int index, int colorFormat, int left, int top, int right, int bottom);
     status_t getFrameAtIndex(
             std::vector<sp<IMemory> > *frames, int frameIndex, int numFrames = 1,
             int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index c10a907..e61b04d 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -166,6 +166,19 @@
     return mRetriever->getImageAtIndex(index, colorFormat, metaOnly, thumbnail);
 }
 
+sp<IMemory> MediaMetadataRetriever::getImageRectAtIndex(
+        int index, int colorFormat, int left, int top, int right, int bottom) {
+    ALOGV("getImageRectAtIndex: index(%d) colorFormat(%d) rect {%d, %d, %d, %d}",
+            index, colorFormat, left, top, right, bottom);
+    Mutex::Autolock _l(mLock);
+    if (mRetriever == 0) {
+        ALOGE("retriever is not initialized");
+        return NULL;
+    }
+    return mRetriever->getImageRectAtIndex(
+            index, colorFormat, left, top, right, bottom);
+}
+
 status_t MediaMetadataRetriever::getFrameAtIndex(
         std::vector<sp<IMemory> > *frames,
         int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index dc2bec8..135c9b6 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -248,12 +248,17 @@
 }
 
 void MediaAnalyticsItem::Prop::setName(const char *name, size_t len) {
-    mNameLen = len;
+    free((void *)mName);
     mName = (const char *) malloc(len+1);
+    LOG_ALWAYS_FATAL_IF(mName == NULL,
+                        "failed malloc() for property '%s' (len %zu)",
+                        name, len);
     memcpy ((void *)mName, name, len+1);
+    mNameLen = len;
 }
 
-// used only as part of a storing operation
+// consider this "find-or-allocate".
+// caller validates type and uses clearPropValue() accordingly
 MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
     size_t len = strlen(name);
     size_t i = findPropIndex(name, len);
@@ -271,7 +276,6 @@
         i = mPropCount++;
         prop = &mProps[i];
         prop->setName(name, len);
-        prop->mType = kTypeNone;        // make caller set type info
     }
 
     return prop;
@@ -299,6 +303,7 @@
 void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
     Prop *prop = allocateProp(name);
     if (prop != NULL) {
+        clearPropValue(prop);
         prop->mType = kTypeInt32;
         prop->u.int32Value = value;
     }
@@ -307,6 +312,7 @@
 void MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr name, int64_t value) {
     Prop *prop = allocateProp(name);
     if (prop != NULL) {
+        clearPropValue(prop);
         prop->mType = kTypeInt64;
         prop->u.int64Value = value;
     }
@@ -315,6 +321,7 @@
 void MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr name, double value) {
     Prop *prop = allocateProp(name);
     if (prop != NULL) {
+        clearPropValue(prop);
         prop->mType = kTypeDouble;
         prop->u.doubleValue = value;
     }
@@ -325,6 +332,7 @@
     Prop *prop = allocateProp(name);
     // any old value will be gone
     if (prop != NULL) {
+        clearPropValue(prop);
         prop->mType = kTypeCString;
         prop->u.CStringValue = strdup(value);
     }
@@ -333,6 +341,7 @@
 void MediaAnalyticsItem::setRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
     Prop *prop = allocateProp(name);
     if (prop != NULL) {
+        clearPropValue(prop);
         prop->mType = kTypeRate;
         prop->u.rate.count = count;
         prop->u.rate.duration = duration;
@@ -585,6 +594,9 @@
     // fix any pointers that we blindly copied, so we have our own copies
     if (dst->mName) {
         void *p =  malloc(dst->mNameLen + 1);
+        LOG_ALWAYS_FATAL_IF(p == NULL,
+                            "failed malloc() duping property '%s' (len %zu)",
+                            dst->mName, dst->mNameLen);
         memcpy (p, src->mName, dst->mNameLen + 1);
         dst->mName = (const char *) p;
     }
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 672b832..40b17bf 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -48,7 +48,6 @@
 {
     ALOGV("MetadataRetrieverClient constructor pid(%d)", pid);
     mPid = pid;
-    mThumbnail = NULL;
     mAlbumArt = NULL;
     mRetriever = NULL;
 }
@@ -77,7 +76,6 @@
     ALOGV("disconnect from pid %d", mPid);
     Mutex::Autolock lock(mLock);
     mRetriever.clear();
-    mThumbnail.clear();
     mAlbumArt.clear();
     IPCThreadState::self()->flushCommands();
 }
@@ -201,7 +199,6 @@
             (long long)timeUs, option, colorFormat, metaOnly);
     Mutex::Autolock lock(mLock);
     Mutex::Autolock glock(sLock);
-    mThumbnail.clear();
     if (mRetriever == NULL) {
         ALOGE("retriever is not initialized");
         return NULL;
@@ -216,11 +213,10 @@
 
 sp<IMemory> MetadataRetrieverClient::getImageAtIndex(
         int index, int colorFormat, bool metaOnly, bool thumbnail) {
-    ALOGV("getFrameAtTime: index(%d) colorFormat(%d), metaOnly(%d) thumbnail(%d)",
+    ALOGV("getImageAtIndex: index(%d) colorFormat(%d), metaOnly(%d) thumbnail(%d)",
             index, colorFormat, metaOnly, thumbnail);
     Mutex::Autolock lock(mLock);
     Mutex::Autolock glock(sLock);
-    mThumbnail.clear();
     if (mRetriever == NULL) {
         ALOGE("retriever is not initialized");
         return NULL;
@@ -233,6 +229,25 @@
     return frame;
 }
 
+sp<IMemory> MetadataRetrieverClient::getImageRectAtIndex(
+        int index, int colorFormat, int left, int top, int right, int bottom) {
+    ALOGV("getImageRectAtIndex: index(%d) colorFormat(%d), rect {%d, %d, %d, %d}",
+            index, colorFormat, left, top, right, bottom);
+    Mutex::Autolock lock(mLock);
+    Mutex::Autolock glock(sLock);
+    if (mRetriever == NULL) {
+        ALOGE("retriever is not initialized");
+        return NULL;
+    }
+    sp<IMemory> frame = mRetriever->getImageRectAtIndex(
+            index, colorFormat, left, top, right, bottom);
+    if (frame == NULL) {
+        ALOGE("failed to extract image");
+        return NULL;
+    }
+    return frame;
+}
+
 status_t MetadataRetrieverClient::getFrameAtIndex(
             std::vector<sp<IMemory> > *frames,
             int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index e774c8f..272d093 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -54,6 +54,8 @@
             int64_t timeUs, int option, int colorFormat, bool metaOnly);
     virtual sp<IMemory>             getImageAtIndex(
             int index, int colorFormat, bool metaOnly, bool thumbnail);
+    virtual sp<IMemory>             getImageRectAtIndex(
+            int index, int colorFormat, int left, int top, int right, int bottom);
     virtual status_t getFrameAtIndex(
                 std::vector<sp<IMemory> > *frames,
                 int frameIndex, int numFrames, int colorFormat, bool metaOnly);
@@ -73,9 +75,8 @@
     sp<MediaMetadataRetrieverBase>         mRetriever;
     pid_t                                  mPid;
 
-    // Keep the shared memory copy of album art and capture frame (for thumbnail)
+    // Keep the shared memory copy of album art
     sp<IMemory>                            mAlbumArt;
-    sp<IMemory>                            mThumbnail;
 };
 
 }; // namespace android
diff --git a/media/libnbaio/PipeReader.cpp b/media/libnbaio/PipeReader.cpp
index 2486b76..35a43d8 100644
--- a/media/libnbaio/PipeReader.cpp
+++ b/media/libnbaio/PipeReader.cpp
@@ -26,7 +26,7 @@
 
 PipeReader::PipeReader(Pipe& pipe) :
         NBAIO_Source(pipe.mFormat),
-        mPipe(pipe), mFifoReader(mPipe.mFifo, false /*throttlesWriter*/, true /*flush*/),
+        mPipe(pipe), mFifoReader(mPipe.mFifo, false /*throttlesWriter*/, false /*flush*/),
         mFramesOverrun(0),
         mOverruns(0)
 {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ad81f04..2abea9e 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2144,6 +2144,10 @@
                 // value is unknown
                 drc.targetRefLevel = -1;
             }
+            if (!msg->findInt32("aac-drc-effect-type", &drc.effectType)) {
+                // value is unknown
+                drc.effectType = -2; // valid values are -1 and over
+            }
 
             err = setupAACCodec(
                     encoder, numChannels, sampleRate, bitrate, aacProfile,
@@ -2778,7 +2782,7 @@
             ? OMX_AUDIO_AACStreamFormatMP4ADTS
             : OMX_AUDIO_AACStreamFormatMP4FF;
 
-    OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE presentation;
+    OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE presentation;
     InitOMXParams(&presentation);
     presentation.nMaxOutputChannels = maxOutputChannelCount;
     presentation.nDrcCut = drc.drcCut;
@@ -2787,14 +2791,29 @@
     presentation.nTargetReferenceLevel = drc.targetRefLevel;
     presentation.nEncodedTargetLevel = drc.encodedTargetLevel;
     presentation.nPCMLimiterEnable = pcmLimiterEnable;
+    presentation.nDrcEffectType = drc.effectType;
 
     status_t res = mOMXNode->setParameter(
             OMX_IndexParamAudioAac, &profile, sizeof(profile));
     if (res == OK) {
         // optional parameters, will not cause configuration failure
-        mOMXNode->setParameter(
+        if (mOMXNode->setParameter(
+                (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAacDrcPresentation,
+                &presentation, sizeof(presentation)) == ERROR_UNSUPPORTED) {
+            // prior to 9.0 we used a different config structure and index
+            OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE presentation8;
+            InitOMXParams(&presentation8);
+            presentation8.nMaxOutputChannels = presentation.nMaxOutputChannels;
+            presentation8.nDrcCut = presentation.nDrcCut;
+            presentation8.nDrcBoost = presentation.nDrcBoost;
+            presentation8.nHeavyCompression = presentation.nHeavyCompression;
+            presentation8.nTargetReferenceLevel = presentation.nTargetReferenceLevel;
+            presentation8.nEncodedTargetLevel = presentation.nEncodedTargetLevel;
+            presentation8.nPCMLimiterEnable = presentation.nPCMLimiterEnable;
+            (void)mOMXNode->setParameter(
                 (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAacPresentation,
-                &presentation, sizeof(presentation));
+                &presentation8, sizeof(presentation8));
+        }
     } else {
         ALOGW("did not set AudioAndroidAacPresentation due to error %d when setting AudioAac", res);
     }
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 22b1e59..48e351b 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -162,7 +162,6 @@
         "libmedia_helper",
         "libstagefright_codecbase",
         "libstagefright_foundation",
-        "libstagefright_omx",
         "libstagefright_omx_utils",
         "libstagefright_xmlparser",
         "libRScpp",
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index b17fbc9..29a219f 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -39,12 +39,12 @@
 
 namespace android {
 
-static const int64_t kBufferTimeOutUs = 30000ll; // 30 msec
-static const size_t kRetryCount = 20; // must be >0
+static const int64_t kBufferTimeOutUs = 10000ll; // 10 msec
+static const size_t kRetryCount = 50; // must be >0
 
-//static
 sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
-        int32_t width, int32_t height, int32_t dstBpp, bool metaOnly = false) {
+        int32_t width, int32_t height, int32_t tileWidth, int32_t tileHeight,
+        int32_t dstBpp, bool metaOnly = false) {
     int32_t rotationAngle;
     if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
         rotationAngle = 0;  // By default, no rotation
@@ -75,7 +75,7 @@
     }
 
     VideoFrame frame(width, height, displayWidth, displayHeight,
-            rotationAngle, dstBpp, !metaOnly, iccSize);
+            tileWidth, tileHeight, rotationAngle, dstBpp, !metaOnly, iccSize);
 
     size_t size = frame.getFlattenedSize();
     sp<MemoryHeapBase> heap = new MemoryHeapBase(size, 0, "MetadataRetrieverClient");
@@ -94,7 +94,6 @@
     return frameMem;
 }
 
-//static
 bool findThumbnailInfo(
         const sp<MetaData> &trackMeta, int32_t *width, int32_t *height,
         uint32_t *type = NULL, const void **data = NULL, size_t *size = NULL) {
@@ -107,30 +106,15 @@
                 type ?: &dummyType, data ?: &dummyData, size ?: &dummySize);
 }
 
-//static
-sp<IMemory> FrameDecoder::getMetadataOnly(
-        const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail) {
-    OMX_COLOR_FORMATTYPE dstFormat;
-    int32_t dstBpp;
-    if (!getDstColorFormat(
-            (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
-        return NULL;
-    }
-
-    int32_t width, height;
-    if (thumbnail) {
-        if (!findThumbnailInfo(trackMeta, &width, &height)) {
-            return NULL;
-        }
-    } else {
-        CHECK(trackMeta->findInt32(kKeyWidth, &width));
-        CHECK(trackMeta->findInt32(kKeyHeight, &height));
-    }
-    return allocVideoFrame(trackMeta, width, height, dstBpp, true /*metaOnly*/);
+bool findGridInfo(const sp<MetaData> &trackMeta,
+        int32_t *tileWidth, int32_t *tileHeight, int32_t *gridRows, int32_t *gridCols) {
+    return trackMeta->findInt32(kKeyTileWidth, tileWidth) && (*tileWidth > 0)
+        && trackMeta->findInt32(kKeyTileHeight, tileHeight) && (*tileHeight > 0)
+        && trackMeta->findInt32(kKeyGridRows, gridRows) && (*gridRows > 0)
+        && trackMeta->findInt32(kKeyGridCols, gridCols) && (*gridCols > 0);
 }
 
-//static
-bool FrameDecoder::getDstColorFormat(
+bool getDstColorFormat(
         android_pixel_format_t colorFormat,
         OMX_COLOR_FORMATTYPE *dstFormat,
         int32_t *dstBpp) {
@@ -162,46 +146,63 @@
     return false;
 }
 
-sp<IMemory> FrameDecoder::extractFrame(
-        int64_t frameTimeUs, int option, int colorFormat) {
+//static
+sp<IMemory> FrameDecoder::getMetadataOnly(
+        const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail) {
+    OMX_COLOR_FORMATTYPE dstFormat;
+    int32_t dstBpp;
     if (!getDstColorFormat(
-            (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
+            (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
         return NULL;
     }
 
-    status_t err = extractInternal(frameTimeUs, 1, option);
-    if (err != OK) {
-        return NULL;
-    }
+    int32_t width, height, tileWidth = 0, tileHeight = 0;
+    if (thumbnail) {
+        if (!findThumbnailInfo(trackMeta, &width, &height)) {
+            return NULL;
+        }
+    } else {
+        CHECK(trackMeta->findInt32(kKeyWidth, &width));
+        CHECK(trackMeta->findInt32(kKeyHeight, &height));
 
-    return mFrames.size() > 0 ? mFrames[0] : NULL;
+        int32_t gridRows, gridCols;
+        if (!findGridInfo(trackMeta, &tileWidth, &tileHeight, &gridRows, &gridCols)) {
+            tileWidth = tileHeight = 0;
+        }
+    }
+    return allocVideoFrame(trackMeta,
+            width, height, tileWidth, tileHeight, dstBpp, true /*metaOnly*/);
 }
 
-status_t FrameDecoder::extractFrames(
-        int64_t frameTimeUs, size_t numFrames, int option, int colorFormat,
-        std::vector<sp<IMemory> >* frames) {
+FrameDecoder::FrameDecoder(
+        const AString &componentName,
+        const sp<MetaData> &trackMeta,
+        const sp<IMediaSource> &source)
+    : mComponentName(componentName),
+      mTrackMeta(trackMeta),
+      mSource(source),
+      mDstFormat(OMX_COLOR_Format16bitRGB565),
+      mDstBpp(2),
+      mHaveMoreInputs(true),
+      mFirstSample(true) {
+}
+
+FrameDecoder::~FrameDecoder() {
+    if (mDecoder != NULL) {
+        mDecoder->release();
+        mSource->stop();
+    }
+}
+
+status_t FrameDecoder::init(
+        int64_t frameTimeUs, size_t numFrames, int option, int colorFormat) {
     if (!getDstColorFormat(
             (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
         return ERROR_UNSUPPORTED;
     }
 
-    status_t err = extractInternal(frameTimeUs, numFrames, option);
-    if (err != OK) {
-        return err;
-    }
-
-    for (size_t i = 0; i < mFrames.size(); i++) {
-        frames->push_back(mFrames[i]);
-    }
-    return OK;
-}
-
-status_t FrameDecoder::extractInternal(
-        int64_t frameTimeUs, size_t numFrames, int option) {
-
-    MediaSource::ReadOptions options;
     sp<AMessage> videoFormat = onGetFormatAndSeekOptions(
-            frameTimeUs, numFrames, option, &options);
+            frameTimeUs, numFrames, option, &mReadOptions);
     if (videoFormat == NULL) {
         ALOGE("video format or seek mode not supported");
         return ERROR_UNSUPPORTED;
@@ -217,7 +218,8 @@
         return (decoder.get() == NULL) ? NO_MEMORY : err;
     }
 
-    err = decoder->configure(videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
+    err = decoder->configure(
+            videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
     if (err != OK) {
         ALOGW("configure returned error %d (%s)", err, asString(err));
         decoder->release();
@@ -237,57 +239,71 @@
         decoder->release();
         return err;
     }
+    mDecoder = decoder;
 
-    Vector<sp<MediaCodecBuffer> > inputBuffers;
-    err = decoder->getInputBuffers(&inputBuffers);
+    return OK;
+}
+
+sp<IMemory> FrameDecoder::extractFrame(FrameRect *rect) {
+    status_t err = onExtractRect(rect);
+    if (err == OK) {
+        err = extractInternal();
+    }
     if (err != OK) {
-        ALOGW("failed to get input buffers: %d (%s)", err, asString(err));
-        decoder->release();
-        mSource->stop();
+        return NULL;
+    }
+
+    return mFrames.size() > 0 ? mFrames[0] : NULL;
+}
+
+status_t FrameDecoder::extractFrames(std::vector<sp<IMemory> >* frames) {
+    status_t err = extractInternal();
+    if (err != OK) {
         return err;
     }
 
-    Vector<sp<MediaCodecBuffer> > outputBuffers;
-    err = decoder->getOutputBuffers(&outputBuffers);
-    if (err != OK) {
-        ALOGW("failed to get output buffers: %d (%s)", err, asString(err));
-        decoder->release();
-        mSource->stop();
-        return err;
+    for (size_t i = 0; i < mFrames.size(); i++) {
+        frames->push_back(mFrames[i]);
     }
+    return OK;
+}
 
-    sp<AMessage> outputFormat = NULL;
-    bool haveMoreInputs = true;
-    size_t index, offset, size;
-    int64_t timeUs;
-    size_t retriesLeft = kRetryCount;
+status_t FrameDecoder::extractInternal() {
+    status_t err = OK;
     bool done = false;
-    bool firstSample = true;
+    size_t retriesLeft = kRetryCount;
     do {
-        size_t inputIndex = -1;
+        size_t index;
         int64_t ptsUs = 0ll;
         uint32_t flags = 0;
-        sp<MediaCodecBuffer> codecBuffer = NULL;
 
-        while (haveMoreInputs) {
-            err = decoder->dequeueInputBuffer(&inputIndex, kBufferTimeOutUs);
+        // Queue as many inputs as we possibly can, then block on dequeuing
+        // outputs. After getting each output, come back and queue the inputs
+        // again to keep the decoder busy.
+        while (mHaveMoreInputs) {
+            err = mDecoder->dequeueInputBuffer(&index, 0);
             if (err != OK) {
-                ALOGW("Timed out waiting for input");
+                ALOGV("Timed out waiting for input");
                 if (retriesLeft) {
                     err = OK;
                 }
                 break;
             }
-            codecBuffer = inputBuffers[inputIndex];
+            sp<MediaCodecBuffer> codecBuffer;
+            err = mDecoder->getInputBuffer(index, &codecBuffer);
+            if (err != OK) {
+                ALOGE("failed to get input buffer %zu", index);
+                break;
+            }
 
             MediaBufferBase *mediaBuffer = NULL;
 
-            err = mSource->read(&mediaBuffer, &options);
-            options.clearSeekTo();
+            err = mSource->read(&mediaBuffer, &mReadOptions);
+            mReadOptions.clearSeekTo();
             if (err != OK) {
                 ALOGW("Input Error or EOS");
-                haveMoreInputs = false;
-                if (!firstSample && err == ERROR_END_OF_STREAM) {
+                mHaveMoreInputs = false;
+                if (!mFirstSample && err == ERROR_END_OF_STREAM) {
                     err = OK;
                 }
                 break;
@@ -296,7 +312,7 @@
             if (mediaBuffer->range_length() > codecBuffer->capacity()) {
                 ALOGE("buffer size (%zu) too large for codec input size (%zu)",
                         mediaBuffer->range_length(), codecBuffer->capacity());
-                haveMoreInputs = false;
+                mHaveMoreInputs = false;
                 err = BAD_VALUE;
             } else {
                 codecBuffer->setRange(0, mediaBuffer->range_length());
@@ -306,51 +322,46 @@
                         (const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
                         mediaBuffer->range_length());
 
-                onInputReceived(codecBuffer, mediaBuffer->meta_data(), firstSample, &flags);
-                firstSample = false;
+                onInputReceived(codecBuffer, mediaBuffer->meta_data(), mFirstSample, &flags);
+                mFirstSample = false;
             }
 
             mediaBuffer->release();
-            break;
-        }
 
-        if (haveMoreInputs && inputIndex < inputBuffers.size()) {
-            ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x",
-                    codecBuffer->size(), ptsUs, flags);
+            if (mHaveMoreInputs) {
+                ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x",
+                        codecBuffer->size(), ptsUs, flags);
 
-            err = decoder->queueInputBuffer(
-                    inputIndex,
-                    codecBuffer->offset(),
-                    codecBuffer->size(),
-                    ptsUs,
-                    flags);
+                err = mDecoder->queueInputBuffer(
+                        index,
+                        codecBuffer->offset(),
+                        codecBuffer->size(),
+                        ptsUs,
+                        flags);
 
-            if (flags & MediaCodec::BUFFER_FLAG_EOS) {
-                haveMoreInputs = false;
-            }
-
-            // we don't expect an output from codec config buffer
-            if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
-                continue;
+                if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+                    mHaveMoreInputs = false;
+                }
             }
         }
 
         while (err == OK) {
+            size_t offset, size;
             // wait for a decoded buffer
-            err = decoder->dequeueOutputBuffer(
+            err = mDecoder->dequeueOutputBuffer(
                     &index,
                     &offset,
                     &size,
-                    &timeUs,
+                    &ptsUs,
                     &flags,
                     kBufferTimeOutUs);
 
             if (err == INFO_FORMAT_CHANGED) {
                 ALOGV("Received format change");
-                err = decoder->getOutputFormat(&outputFormat);
+                err = mDecoder->getOutputFormat(&mOutputFormat);
             } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
                 ALOGV("Output buffers changed");
-                err = decoder->getOutputBuffers(&outputBuffers);
+                err = OK;
             } else {
                 if (err == -EAGAIN /* INFO_TRY_AGAIN_LATER */ && --retriesLeft > 0) {
                     ALOGV("Timed-out waiting for output.. retries left = %zu", retriesLeft);
@@ -358,12 +369,15 @@
                 } else if (err == OK) {
                     // If we're seeking with CLOSEST option and obtained a valid targetTimeUs
                     // from the extractor, decode to the specified frame. Otherwise we're done.
-                    ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs);
-                    sp<MediaCodecBuffer> videoFrameBuffer = outputBuffers.itemAt(index);
-
-                    err = onOutputReceived(videoFrameBuffer, outputFormat, timeUs, &done);
-
-                    decoder->releaseOutputBuffer(index);
+                    ALOGV("Received an output buffer, timeUs=%lld", (long long)ptsUs);
+                    sp<MediaCodecBuffer> videoFrameBuffer;
+                    err = mDecoder->getOutputBuffer(index, &videoFrameBuffer);
+                    if (err != OK) {
+                        ALOGE("failed to get output buffer %zu", index);
+                        break;
+                    }
+                    err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
+                    mDecoder->releaseOutputBuffer(index);
                 } else {
                     ALOGW("Received error %d (%s) instead of output", err, asString(err));
                     done = true;
@@ -373,9 +387,6 @@
         }
     } while (err == OK && !done);
 
-    mSource->stop();
-    decoder->release();
-
     if (err != OK) {
         ALOGE("failed to get video frame (err %d)", err);
     }
@@ -383,6 +394,20 @@
     return err;
 }
 
+//////////////////////////////////////////////////////////////////////
+
+VideoFrameDecoder::VideoFrameDecoder(
+        const AString &componentName,
+        const sp<MetaData> &trackMeta,
+        const sp<IMediaSource> &source)
+    : FrameDecoder(componentName, trackMeta, source),
+      mIsAvcOrHevc(false),
+      mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
+      mTargetTimeUs(-1ll),
+      mNumFrames(0),
+      mNumFramesDecoded(0) {
+}
+
 sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
         int64_t frameTimeUs, size_t numFrames, int seekMode, MediaSource::ReadOptions *options) {
     mSeekMode = static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
@@ -488,6 +513,8 @@
             trackMeta(),
             (crop_right - crop_left + 1),
             (crop_bottom - crop_top + 1),
+            0,
+            0,
             dstBpp());
     addFrame(frameMem);
     VideoFrame* frame = static_cast<VideoFrame*>(frameMem->pointer());
@@ -514,33 +541,50 @@
     return ERROR_UNSUPPORTED;
 }
 
+////////////////////////////////////////////////////////////////////////
+
+ImageDecoder::ImageDecoder(
+        const AString &componentName,
+        const sp<MetaData> &trackMeta,
+        const sp<IMediaSource> &source)
+    : FrameDecoder(componentName, trackMeta, source),
+      mFrame(NULL),
+      mWidth(0),
+      mHeight(0),
+      mGridRows(1),
+      mGridCols(1),
+      mTileWidth(0),
+      mTileHeight(0),
+      mTilesDecoded(0),
+      mTargetTiles(0) {
+}
+
 sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
         int64_t frameTimeUs, size_t /*numFrames*/,
         int /*seekMode*/, MediaSource::ReadOptions *options) {
     sp<MetaData> overrideMeta;
-    mThumbnail = false;
     if (frameTimeUs < 0) {
         uint32_t type;
         const void *data;
         size_t size;
-        int32_t thumbWidth, thumbHeight;
 
         // if we have a stand-alone thumbnail, set up the override meta,
         // and set seekTo time to -1.
-        if (!findThumbnailInfo(trackMeta(),
-                &thumbWidth, &thumbHeight, &type, &data, &size)) {
+        if (!findThumbnailInfo(trackMeta(), &mWidth, &mHeight, &type, &data, &size)) {
             ALOGE("Thumbnail not available");
             return NULL;
         }
         overrideMeta = new MetaData(*(trackMeta()));
         overrideMeta->remove(kKeyDisplayWidth);
         overrideMeta->remove(kKeyDisplayHeight);
-        overrideMeta->setInt32(kKeyWidth, thumbWidth);
-        overrideMeta->setInt32(kKeyHeight, thumbHeight);
+        overrideMeta->setInt32(kKeyWidth, mWidth);
+        overrideMeta->setInt32(kKeyHeight, mHeight);
         overrideMeta->setData(kKeyHVCC, type, data, size);
         options->setSeekTo(-1);
-        mThumbnail = true;
     } else {
+        CHECK(trackMeta()->findInt32(kKeyWidth, &mWidth));
+        CHECK(trackMeta()->findInt32(kKeyHeight, &mHeight));
+
         options->setSeekTo(frameTimeUs);
     }
 
@@ -548,32 +592,28 @@
     if (overrideMeta == NULL) {
         // check if we're dealing with a tiled heif
         int32_t tileWidth, tileHeight, gridRows, gridCols;
-        if (trackMeta()->findInt32(kKeyTileWidth, &tileWidth) && tileWidth > 0
-         && trackMeta()->findInt32(kKeyTileHeight, &tileHeight) && tileHeight > 0
-         && trackMeta()->findInt32(kKeyGridRows, &gridRows) && gridRows > 0
-         && trackMeta()->findInt32(kKeyGridCols, &gridCols) && gridCols > 0) {
-            int32_t width, height;
-            CHECK(trackMeta()->findInt32(kKeyWidth, &width));
-            CHECK(trackMeta()->findInt32(kKeyHeight, &height));
-
-            if (width <= tileWidth * gridCols && height <= tileHeight * gridRows) {
+        if (findGridInfo(trackMeta(), &tileWidth, &tileHeight, &gridRows, &gridCols)) {
+            if (mWidth <= tileWidth * gridCols && mHeight <= tileHeight * gridRows) {
                 ALOGV("grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
-                        gridCols, gridRows, tileWidth, tileHeight, width, height);
+                        gridCols, gridRows, tileWidth, tileHeight, mWidth, mHeight);
 
                 overrideMeta = new MetaData(*(trackMeta()));
                 overrideMeta->setInt32(kKeyWidth, tileWidth);
                 overrideMeta->setInt32(kKeyHeight, tileHeight);
+                mTileWidth = tileWidth;
+                mTileHeight = tileHeight;
                 mGridCols = gridCols;
                 mGridRows = gridRows;
             } else {
-                ALOGE("bad grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
-                        gridCols, gridRows, tileWidth, tileHeight, width, height);
+                ALOGW("ignore bad grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
+                        gridCols, gridRows, tileWidth, tileHeight, mWidth, mHeight);
             }
         }
         if (overrideMeta == NULL) {
             overrideMeta = trackMeta();
         }
     }
+    mTargetTiles = mGridCols * mGridRows;
 
     sp<AMessage> videoFormat;
     if (convertMetaDataToMessage(overrideMeta, &videoFormat) != OK) {
@@ -592,6 +632,45 @@
     return videoFormat;
 }
 
+status_t ImageDecoder::onExtractRect(FrameRect *rect) {
+    // TODO:
+    // This callback is for verifying whether we can decode the rect,
+    // and if so, set up the internal variables for decoding.
+    // Currently, rect decoding is restricted to sequentially decoding one
+    // row of tiles at a time. We can't decode arbitrary rects, as the image
+    // track doesn't yet support seeking by tiles. So all we do here is to
+    // verify the rect against what we expect.
+    // When seeking by tile is supported, this code should be updated to
+    // set the seek parameters.
+    if (rect == NULL) {
+        if (mTilesDecoded > 0) {
+            return ERROR_UNSUPPORTED;
+        }
+        mTargetTiles = mGridRows * mGridCols;
+        return OK;
+    }
+
+    if (mTileWidth <= 0 || mTileHeight <=0) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    int32_t row = mTilesDecoded / mGridCols;
+    int32_t expectedTop = row * mTileHeight;
+    int32_t expectedBot = (row + 1) * mTileHeight;
+    if (expectedBot > mHeight) {
+        expectedBot = mHeight;
+    }
+    if (rect->left != 0 || rect->top != expectedTop
+            || rect->right != mWidth || rect->bottom != expectedBot) {
+        ALOGE("currently only support sequential decoding of slices");
+        return ERROR_UNSUPPORTED;
+    }
+
+    // advance one row
+    mTargetTiles = mTilesDecoded + mGridCols;
+    return OK;
+}
+
 status_t ImageDecoder::onOutputReceived(
         const sp<MediaCodecBuffer> &videoFrameBuffer,
         const sp<AMessage> &outputFormat, int64_t /*timeUs*/, bool *done) {
@@ -603,17 +682,9 @@
     CHECK(outputFormat->findInt32("width", &width));
     CHECK(outputFormat->findInt32("height", &height));
 
-    int32_t imageWidth, imageHeight;
-    if (mThumbnail) {
-        CHECK(trackMeta()->findInt32(kKeyThumbnailWidth, &imageWidth));
-        CHECK(trackMeta()->findInt32(kKeyThumbnailHeight, &imageHeight));
-    } else {
-        CHECK(trackMeta()->findInt32(kKeyWidth, &imageWidth));
-        CHECK(trackMeta()->findInt32(kKeyHeight, &imageHeight));
-    }
-
     if (mFrame == NULL) {
-        sp<IMemory> frameMem = allocVideoFrame(trackMeta(), imageWidth, imageHeight, dstBpp());
+        sp<IMemory> frameMem = allocVideoFrame(
+                trackMeta(), mWidth, mHeight, mTileWidth, mTileHeight, dstBpp());
         mFrame = static_cast<VideoFrame*>(frameMem->pointer());
 
         addFrame(frameMem);
@@ -625,8 +696,6 @@
     ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat());
 
     int32_t dstLeft, dstTop, dstRight, dstBottom;
-    int32_t numTiles = mGridRows * mGridCols;
-
     dstLeft = mTilesDecoded % mGridCols * width;
     dstTop = mTilesDecoded / mGridCols * height;
     dstRight = dstLeft + width - 1;
@@ -641,16 +710,16 @@
 
     // apply crop on bottom-right
     // TODO: need to move this into the color converter itself.
-    if (dstRight >= imageWidth) {
-        crop_right = imageWidth - dstLeft - 1;
+    if (dstRight >= mWidth) {
+        crop_right = mWidth - dstLeft - 1;
         dstRight = dstLeft + crop_right;
     }
-    if (dstBottom >= imageHeight) {
-        crop_bottom = imageHeight - dstTop - 1;
+    if (dstBottom >= mHeight) {
+        crop_bottom = mHeight - dstTop - 1;
         dstBottom = dstTop + crop_bottom;
     }
 
-    *done = (++mTilesDecoded >= numTiles);
+    *done = (++mTilesDecoded >= mTargetTiles);
 
     if (converter.isValid()) {
         converter.convert(
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index cd091a6..9a33168 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -287,7 +287,9 @@
 //static
 bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
     return componentName.startsWithIgnoreCase("OMX.google.")
-        || !componentName.startsWithIgnoreCase("OMX.");
+            || componentName.startsWithIgnoreCase("c2.android.")
+            || (!componentName.startsWithIgnoreCase("OMX.")
+                    && !componentName.startsWithIgnoreCase("c2."));
 }
 
 static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
@@ -298,7 +300,14 @@
         return isSoftwareCodec2 - isSoftwareCodec1;
     }
 
-    // sort order 2: OMX codecs are first (lower)
+    // sort order 2: Codec 2.0 codecs are first (lower)
+    bool isC2_1 = name1->startsWithIgnoreCase("c2.");
+    bool isC2_2 = name2->startsWithIgnoreCase("c2.");
+    if (isC2_1 != isC2_2) {
+        return isC2_2 - isC2_1;
+    }
+
+    // sort order 3: OMX codecs are first (lower)
     bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
     bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
     return isOMX2 - isOMX1;
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
index 6920e51..cac53f4 100644
--- a/media/libstagefright/MediaCodecListOverrides.cpp
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -222,7 +222,7 @@
     AString supportMultipleSecureCodecs = "true";
     for (const auto& info : infos) {
         AString name = info->getCodecName();
-        if (name.startsWith("OMX.google.") ||
+        if (name.startsWith("OMX.google.") || name.startsWith("c2.android.") ||
                 // TODO: reenable below codecs once fixed
                 name == "OMX.Intel.VideoDecoder.VP9.hybrid") {
             continue;
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
index fe141ab..96b896b 100644
--- a/media/libstagefright/OmxInfoBuilder.cpp
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -108,24 +108,6 @@
     if (!transStatus.isOk()) {
         ALOGE("Fail to obtain codec roles from IOmxStore.");
         return NO_INIT;
-    } else if (roles.size() == 0) {
-        ALOGW("IOmxStore has empty implementation. "
-                "Creating a local default instance...");
-        omxStore = new implementation::OmxStore();
-        if (omxStore == nullptr) {
-            ALOGE("Cannot create a local default instance.");
-            return NO_INIT;
-        }
-        ALOGI("IOmxStore local default instance created.");
-        transStatus = omxStore->listRoles(
-                [&roles] (
-                const hidl_vec<IOmxStore::RoleInfo>& inRoleList) {
-                    roles = inRoleList;
-                });
-        if (!transStatus.isOk()) {
-            ALOGE("Fail to obtain codec roles from local IOmxStore.");
-            return NO_INIT;
-        }
     }
 
     hidl_vec<IOmxStore::ServiceAttribute> serviceAttributes;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 4ff2bfe..e010b3e 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -40,7 +40,8 @@
         ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
         ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
         ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
-        ".avi", ".mpeg", ".mpg", ".awb", ".mpga", ".mov"
+        ".avi", ".mpeg", ".mpg", ".awb", ".mpga", ".mov",
+        ".m4v", ".oga"
     };
     static const size_t kNumValidExtensions =
         sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
@@ -62,6 +63,11 @@
     client.setLocale(locale());
     client.beginFile();
     MediaScanResult result = processFileInternal(path, mimeType, client);
+    ALOGV("result: %d", result);
+    if (mimeType == NULL && result != MEDIA_SCAN_RESULT_OK) {
+        ALOGW("media scan failed for %s", path);
+        client.setMimeType("application/octet-stream");
+    }
     client.endFile();
     return result;
 }
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 5417fef..e80ec3b 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -40,7 +40,8 @@
 
 StagefrightMetadataRetriever::StagefrightMetadataRetriever()
     : mParsedMetaData(false),
-      mAlbumArt(NULL) {
+      mAlbumArt(NULL),
+      mLastImageIndex(-1) {
     ALOGV("StagefrightMetadataRetriever()");
 }
 
@@ -126,10 +127,30 @@
 
 sp<IMemory> StagefrightMetadataRetriever::getImageAtIndex(
         int index, int colorFormat, bool metaOnly, bool thumbnail) {
-
     ALOGV("getImageAtIndex: index(%d) colorFormat(%d) metaOnly(%d) thumbnail(%d)",
             index, colorFormat, metaOnly, thumbnail);
 
+    return getImageInternal(index, colorFormat, metaOnly, thumbnail, NULL);
+}
+
+sp<IMemory> StagefrightMetadataRetriever::getImageRectAtIndex(
+        int index, int colorFormat, int left, int top, int right, int bottom) {
+    ALOGV("getImageRectAtIndex: index(%d) colorFormat(%d) rect {%d, %d, %d, %d}",
+            index, colorFormat, left, top, right, bottom);
+
+    FrameRect rect = {left, top, right, bottom};
+
+    if (mImageDecoder != NULL && index == mLastImageIndex) {
+        return mImageDecoder->extractFrame(&rect);
+    }
+
+    return getImageInternal(
+            index, colorFormat, false /*metaOnly*/, false /*thumbnail*/, &rect);
+}
+
+sp<IMemory> StagefrightMetadataRetriever::getImageInternal(
+        int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect) {
+
     if (mExtractor.get() == NULL) {
         ALOGE("no extractor.");
         return NULL;
@@ -192,12 +213,19 @@
 
     for (size_t i = 0; i < matchingCodecs.size(); ++i) {
         const AString &componentName = matchingCodecs[i];
-        ImageDecoder decoder(componentName, trackMeta, source);
-        sp<IMemory> frame = decoder.extractFrame(
-                thumbnail ? -1 : 0 /*frameTimeUs*/, 0 /*seekMode*/, colorFormat);
+        sp<ImageDecoder> decoder = new ImageDecoder(componentName, trackMeta, source);
+        int64_t frameTimeUs = thumbnail ? -1 : 0;
+        if (decoder->init(frameTimeUs, 1 /*numFrames*/, 0 /*option*/, colorFormat) == OK) {
+            sp<IMemory> frame = decoder->extractFrame(rect);
 
-        if (frame != NULL) {
-            return frame;
+            if (frame != NULL) {
+                if (rect != NULL) {
+                    // keep the decoder if slice decoding
+                    mImageDecoder = decoder;
+                    mLastImageIndex = index;
+                }
+                return frame;
+            }
         }
         ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
     }
@@ -307,16 +335,17 @@
     for (size_t i = 0; i < matchingCodecs.size(); ++i) {
         const AString &componentName = matchingCodecs[i];
         VideoFrameDecoder decoder(componentName, trackMeta, source);
-        if (outFrame != NULL) {
-            *outFrame = decoder.extractFrame(timeUs, option, colorFormat);
-            if (*outFrame != NULL) {
-                return OK;
-            }
-        } else if (outFrames != NULL) {
-            status_t err = decoder.extractFrames(
-                    timeUs, numFrames, option, colorFormat, outFrames);
-            if (err == OK) {
-                return OK;
+        if (decoder.init(timeUs, numFrames, option, colorFormat) == OK) {
+            if (outFrame != NULL) {
+                *outFrame = decoder.extractFrame();
+                if (*outFrame != NULL) {
+                    return OK;
+                }
+            } else if (outFrames != NULL) {
+                status_t err = decoder.extractFrames(outFrames);
+                if (err == OK) {
+                    return OK;
+                }
             }
         }
         ALOGV("%s failed to extract frame, trying next decoder.", componentName.c_str());
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index c61f4b5..cf5e91e 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -154,6 +154,7 @@
         { 23, OMX_AUDIO_AACObjectLD       },
         { 29, OMX_AUDIO_AACObjectHE_PS    },
         { 39, OMX_AUDIO_AACObjectELD      },
+        { 42, OMX_AUDIO_AACObjectXHE      },
     };
 
     OMX_AUDIO_AACPROFILETYPE profile;
@@ -1610,6 +1611,7 @@
     { OMX_AUDIO_AACObjectLD,          AUDIO_FORMAT_AAC_LD},
     { OMX_AUDIO_AACObjectHE_PS,       AUDIO_FORMAT_AAC_HE_V2},
     { OMX_AUDIO_AACObjectELD,         AUDIO_FORMAT_AAC_ELD},
+    { OMX_AUDIO_AACObjectXHE,         AUDIO_FORMAT_AAC_XHE},
     { OMX_AUDIO_AACObjectNull,        AUDIO_FORMAT_AAC},
 };
 
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
index 129ad65..95d3724 100644
--- a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
@@ -24,7 +24,7 @@
 //#define DRC_PRES_MODE_WRAP_DEBUG
 
 #define GPM_ENCODER_TARGET_LEVEL 64
-#define MAX_TARGET_LEVEL 64
+#define MAX_TARGET_LEVEL 40
 
 CDrcPresModeWrapper::CDrcPresModeWrapper()
 {
@@ -164,7 +164,7 @@
     if (mDataUpdate) {
         // sanity check
         if (mDesTarget < MAX_TARGET_LEVEL){
-            mDesTarget = MAX_TARGET_LEVEL;  // limit target level to -16 dB or below
+            mDesTarget = MAX_TARGET_LEVEL;  // limit target level to -10 dB or below
             newTarget = MAX_TARGET_LEVEL;
         }
 
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index e0c0c32..417ca55 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -36,6 +36,7 @@
 #define DRC_DEFAULT_MOBILE_DRC_CUT   127 /* maximum compression of dynamic range for mobile conf */
 #define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
 #define DRC_DEFAULT_MOBILE_DRC_HEAVY 1   /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_EFFECT 3  /* MPEG-D DRC effect type; 3 => Limited playback range */
 #define DRC_DEFAULT_MOBILE_ENC_LEVEL (-1) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
 #define MAX_CHANNEL_COUNT            8  /* maximum number of audio channels that can be decoded */
 // names of properties that can be used to override the default DRC settings
@@ -44,6 +45,7 @@
 #define PROP_DRC_OVERRIDE_BOOST      "aac_drc_boost"
 #define PROP_DRC_OVERRIDE_HEAVY      "aac_drc_heavy"
 #define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level"
+#define PROP_DRC_OVERRIDE_EFFECT     "ro.aac_drc_effect_type"
 
 namespace android {
 
@@ -207,6 +209,15 @@
     } else {
         mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, DRC_DEFAULT_MOBILE_ENC_LEVEL);
     }
+    // AAC_UNIDRC_SET_EFFECT
+    int32_t effectType =
+            property_get_int32(PROP_DRC_OVERRIDE_EFFECT, DRC_DEFAULT_MOBILE_DRC_EFFECT);
+    if (effectType < -1 || effectType > 8) {
+        effectType = DRC_DEFAULT_MOBILE_DRC_EFFECT;
+    }
+    ALOGV("AAC decoder using MPEG-D DRC effect type %d (default=%d)",
+            effectType, DRC_DEFAULT_MOBILE_DRC_EFFECT);
+    aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_SET_EFFECT, effectType);
 
     // By default, the decoder creates a 5.1 channel downmix signal.
     // For seven and eight channel input streams, enable 6.1 and 7.1 channel output
@@ -414,10 +425,10 @@
             return OMX_ErrorNone;
         }
 
-        case OMX_IndexParamAudioAndroidAacPresentation:
+        case OMX_IndexParamAudioAndroidAacDrcPresentation:
         {
-            const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *aacPresParams =
-                    (const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *)params;
+            const OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE *aacPresParams =
+                    (const OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE *)params;
 
             if (!isValidOMXParam(aacPresParams)) {
                 return OMX_ErrorBadParameter;
@@ -443,6 +454,10 @@
                 ALOGV("set nMaxOutputChannels=%d", max);
                 aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, max);
             }
+            if (aacPresParams->nDrcEffectType >= -1) {
+                ALOGV("set nDrcEffectType=%d", aacPresParams->nDrcEffectType);
+                aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_SET_EFFECT, aacPresParams->nDrcEffectType);
+            }
             bool updateDrcWrapper = false;
             if (aacPresParams->nDrcBoost >= 0) {
                 ALOGV("set nDrcBoost=%d", aacPresParams->nDrcBoost);
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 1b38852..05f4104 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -25,7 +25,7 @@
 
 #include "libyuv/convert_from.h"
 #include "libyuv/video_common.h"
-
+#include <functional>
 #include <sys/time.h>
 
 #define USE_LIBYUV
@@ -58,14 +58,16 @@
 
 bool ColorConverter::isValid() const {
     switch (mSrcFormat) {
+        case OMX_COLOR_FormatYUV420Planar16:
+            if (mDstFormat == OMX_COLOR_FormatYUV444Y410) {
+                return true;
+            }
+            // fall-thru
         case OMX_COLOR_FormatYUV420Planar:
             return mDstFormat == OMX_COLOR_Format16bitRGB565
                     || mDstFormat == OMX_COLOR_Format32BitRGBA8888
                     || mDstFormat == OMX_COLOR_Format32bitBGRA8888;
 
-        case OMX_COLOR_FormatYUV420Planar16:
-            return mDstFormat == OMX_COLOR_FormatYUV444Y410;
-
         case OMX_COLOR_FormatCbYCrY:
         case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
         case OMX_COLOR_FormatYUV420SemiPlanar:
@@ -311,84 +313,120 @@
     return OK;
 }
 
-void ColorConverter::writeToDst(
-        void *dst_ptr, uint8_t *kAdjustedClip, bool uncropped,
-        signed r1, signed g1, signed b1,
-        signed r2, signed g2, signed b2) {
-    switch (mDstFormat) {
+std::function<void (void *, void *, void *, size_t,
+                    signed *, signed *, signed *, signed *)>
+getReadFromSrc(OMX_COLOR_FORMATTYPE srcFormat) {
+    switch(srcFormat) {
+    case OMX_COLOR_FormatYUV420Planar:
+        return [](void *src_y, void *src_u, void *src_v, size_t x,
+                  signed *y1, signed *y2, signed *u, signed *v) {
+            *y1 = ((uint8_t*)src_y)[x] - 16;
+            *y2 = ((uint8_t*)src_y)[x + 1] - 16;
+            *u = ((uint8_t*)src_u)[x / 2] - 128;
+            *v = ((uint8_t*)src_v)[x / 2] - 128;
+        };
+    case OMX_COLOR_FormatYUV420Planar16:
+        return [](void *src_y, void *src_u, void *src_v, size_t x,
+                signed *y1, signed *y2, signed *u, signed *v) {
+            *y1 = (signed)(((uint16_t*)src_y)[x] >> 2) - 16;
+            *y2 = (signed)(((uint16_t*)src_y)[x + 1] >> 2) - 16;
+            *u = (signed)(((uint16_t*)src_u)[x / 2] >> 2) - 128;
+            *v = (signed)(((uint16_t*)src_v)[x / 2] >> 2) - 128;
+        };
+    default:
+        TRESPASS();
+    }
+    return nullptr;
+}
+
+std::function<void (void *, bool, signed, signed, signed, signed, signed, signed)>
+getWriteToDst(OMX_COLOR_FORMATTYPE dstFormat, uint8_t *kAdjustedClip) {
+    switch (dstFormat) {
     case OMX_COLOR_Format16bitRGB565:
     {
-        uint32_t rgb1 =
-            ((kAdjustedClip[r1] >> 3) << 11)
-            | ((kAdjustedClip[g1] >> 2) << 5)
-            | (kAdjustedClip[b1] >> 3);
+        return [kAdjustedClip](void *dst_ptr, bool uncropped,
+                               signed r1, signed g1, signed b1,
+                               signed r2, signed g2, signed b2) {
+            uint32_t rgb1 =
+                ((kAdjustedClip[r1] >> 3) << 11)
+                | ((kAdjustedClip[g1] >> 2) << 5)
+                | (kAdjustedClip[b1] >> 3);
 
-        if (uncropped) {
-            uint32_t rgb2 =
-                ((kAdjustedClip[r2] >> 3) << 11)
-                | ((kAdjustedClip[g2] >> 2) << 5)
-                | (kAdjustedClip[b2] >> 3);
+            if (uncropped) {
+                uint32_t rgb2 =
+                    ((kAdjustedClip[r2] >> 3) << 11)
+                    | ((kAdjustedClip[g2] >> 2) << 5)
+                    | (kAdjustedClip[b2] >> 3);
 
-            *(uint32_t *)dst_ptr = (rgb2 << 16) | rgb1;
-        } else {
-            *(uint16_t *)dst_ptr = rgb1;
-        }
-        break;
+                *(uint32_t *)dst_ptr = (rgb2 << 16) | rgb1;
+            } else {
+                *(uint16_t *)dst_ptr = rgb1;
+            }
+        };
     }
     case OMX_COLOR_Format32BitRGBA8888:
     {
-        ((uint32_t *)dst_ptr)[0] =
-                (kAdjustedClip[r1])
-                | (kAdjustedClip[g1] << 8)
-                | (kAdjustedClip[b1] << 16)
-                | (0xFF << 24);
-
-        if (uncropped) {
-            ((uint32_t *)dst_ptr)[1] =
-                    (kAdjustedClip[r2])
-                    | (kAdjustedClip[g2] << 8)
-                    | (kAdjustedClip[b2] << 16)
+        return [kAdjustedClip](void *dst_ptr, bool uncropped,
+                               signed r1, signed g1, signed b1,
+                               signed r2, signed g2, signed b2) {
+            ((uint32_t *)dst_ptr)[0] =
+                    (kAdjustedClip[r1])
+                    | (kAdjustedClip[g1] << 8)
+                    | (kAdjustedClip[b1] << 16)
                     | (0xFF << 24);
-        }
-        break;
+
+            if (uncropped) {
+                ((uint32_t *)dst_ptr)[1] =
+                        (kAdjustedClip[r2])
+                        | (kAdjustedClip[g2] << 8)
+                        | (kAdjustedClip[b2] << 16)
+                        | (0xFF << 24);
+            }
+        };
     }
     case OMX_COLOR_Format32bitBGRA8888:
     {
-        ((uint32_t *)dst_ptr)[0] =
-                (kAdjustedClip[b1])
-                | (kAdjustedClip[g1] << 8)
-                | (kAdjustedClip[r1] << 16)
-                | (0xFF << 24);
-
-        if (uncropped) {
-            ((uint32_t *)dst_ptr)[1] =
-                    (kAdjustedClip[b2])
-                    | (kAdjustedClip[g2] << 8)
-                    | (kAdjustedClip[r2] << 16)
+        return [kAdjustedClip](void *dst_ptr, bool uncropped,
+                               signed r1, signed g1, signed b1,
+                               signed r2, signed g2, signed b2) {
+            ((uint32_t *)dst_ptr)[0] =
+                    (kAdjustedClip[b1])
+                    | (kAdjustedClip[g1] << 8)
+                    | (kAdjustedClip[r1] << 16)
                     | (0xFF << 24);
-        }
-        break;
+
+            if (uncropped) {
+                ((uint32_t *)dst_ptr)[1] =
+                        (kAdjustedClip[b2])
+                        | (kAdjustedClip[g2] << 8)
+                        | (kAdjustedClip[r2] << 16)
+                        | (0xFF << 24);
+            }
+        };
     }
     default:
-        break;
+        TRESPASS();
     }
+    return nullptr;
 }
+
 status_t ColorConverter::convertYUV420Planar(
         const BitmapParams &src, const BitmapParams &dst) {
     uint8_t *kAdjustedClip = initClip();
 
+    auto readFromSrc = getReadFromSrc(mSrcFormat);
+    auto writeToDst = getWriteToDst(mDstFormat, kAdjustedClip);
+
     uint8_t *dst_ptr = (uint8_t *)dst.mBits
-        + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
+            + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
 
-    const uint8_t *src_y =
-        (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
+    uint8_t *src_y = (uint8_t *)src.mBits
+            + src.mCropTop * src.mStride + src.mCropLeft * src.mBpp;
 
-    const uint8_t *src_u =
-        (const uint8_t *)src.mBits + src.mStride * src.mHeight
-        + (src.mCropTop / 2) * (src.mStride / 2) + src.mCropLeft / 2;
+    uint8_t *src_u = (uint8_t *)src.mBits + src.mStride * src.mHeight
+            + (src.mCropTop / 2) * (src.mStride / 2) + src.mCropLeft / 2 * src.mBpp;
 
-    const uint8_t *src_v =
-        src_u + (src.mStride / 2) * (src.mHeight / 2);
+    uint8_t *src_v = src_u + (src.mStride / 2) * (src.mHeight / 2);
 
     for (size_t y = 0; y < src.cropHeight(); ++y) {
         for (size_t x = 0; x < src.cropWidth(); x += 2) {
@@ -410,11 +448,8 @@
 
             // clip range -278 .. 535
 
-            signed y1 = (signed)src_y[x] - 16;
-            signed y2 = (signed)src_y[x + 1] - 16;
-
-            signed u = (signed)src_u[x / 2] - 128;
-            signed v = (signed)src_v[x / 2] - 128;
+            signed y1, y2, u, v;
+            readFromSrc(src_y, src_u, src_v, x, &y1, &y2, &u, &v);
 
             signed u_b = u * 517;
             signed u_g = -u * 100;
@@ -432,8 +467,7 @@
             signed r2 = (tmp2 + v_r) / 256;
 
             bool uncropped = x + 1 < src.cropWidth();
-            (void)writeToDst(dst_ptr + x * dst.mBpp,
-                    kAdjustedClip, uncropped, r1, g1, b1, r2, g2, b2);
+            writeToDst(dst_ptr + x * dst.mBpp, uncropped, r1, g1, b1, r2, g2, b2);
         }
 
         src_y += src.mStride;
@@ -449,6 +483,15 @@
     return OK;
 }
 
+status_t ColorConverter::convertYUV420Planar16(
+        const BitmapParams &src, const BitmapParams &dst) {
+    if (mDstFormat == OMX_COLOR_FormatYUV444Y410) {
+        return convertYUV420Planar16ToY410(src, dst);
+    }
+
+    return convertYUV420Planar(src, dst);
+}
+
 /*
  * Pack 10-bit YUV into RGBA_1010102.
  *
@@ -480,7 +523,7 @@
 
 #if !USE_NEON_Y410
 
-status_t ColorConverter::convertYUV420Planar16(
+status_t ColorConverter::convertYUV420Planar16ToY410(
         const BitmapParams &src, const BitmapParams &dst) {
     uint8_t *dst_ptr = (uint8_t *)dst.mBits
         + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
@@ -554,7 +597,7 @@
 
 #else
 
-status_t ColorConverter::convertYUV420Planar16(
+status_t ColorConverter::convertYUV420Planar16ToY410(
         const BitmapParams &src, const BitmapParams &dst) {
     uint8_t *out = (uint8_t *)dst.mBits
         + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 838bc5f..657a05b 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -79,13 +79,26 @@
         cropBottomNew = heightNew - 1;
     }
 
+    // The native window buffer format for high-bitdepth content could
+    // depend on the dataspace also.
+    android_dataspace dataSpace;
+    bool dataSpaceChangedForPlanar16 = false;
+    if (colorFormatNew == OMX_COLOR_FormatYUV420Planar16
+            && format->findInt32("android._dataspace", (int32_t *)&dataSpace)
+            && dataSpace != mDataSpace) {
+        // Do not modify mDataSpace here, it's only modified at last
+        // when we do native_window_set_buffers_data_space().
+        dataSpaceChangedForPlanar16 = true;
+    }
+
     if (static_cast<int32_t>(mColorFormat) == colorFormatNew &&
         mWidth == widthNew &&
         mHeight == heightNew &&
         mCropLeft == cropLeftNew &&
         mCropTop == cropTopNew &&
         mCropRight == cropRightNew &&
-        mCropBottom == cropBottomNew) {
+        mCropBottom == cropBottomNew &&
+        !dataSpaceChangedForPlanar16) {
         // Nothing changed, no need to reset renderer.
         return;
     }
@@ -135,11 +148,16 @@
             }
             case OMX_COLOR_FormatYUV420Planar16:
             {
-                // Here we would convert OMX_COLOR_FormatYUV420Planar16 into
-                // OMX_COLOR_FormatYUV444Y410, and put it inside a buffer with
-                // format HAL_PIXEL_FORMAT_RGBA_1010102. Surfaceflinger will
-                // use render engine to convert it to RGB if needed.
-                halFormat = HAL_PIXEL_FORMAT_RGBA_1010102;
+                if (((dataSpace & HAL_DATASPACE_STANDARD_MASK) == HAL_DATASPACE_STANDARD_BT2020)
+                 && ((dataSpace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_ST2084)) {
+                    // Here we would convert OMX_COLOR_FormatYUV420Planar16 into
+                    // OMX_COLOR_FormatYUV444Y410, and put it inside a buffer with
+                    // format HAL_PIXEL_FORMAT_RGBA_1010102. Surfaceflinger will
+                    // use render engine to convert it to RGB if needed.
+                    halFormat = HAL_PIXEL_FORMAT_RGBA_1010102;
+                } else {
+                    halFormat = HAL_PIXEL_FORMAT_YV12;
+                }
                 bufWidth = (mCropWidth + 1) & ~1;
                 bufHeight = (mCropHeight + 1) & ~1;
                 break;
@@ -155,7 +173,7 @@
         mConverter = new ColorConverter(
                 mColorFormat, OMX_COLOR_Format16bitRGB565);
         CHECK(mConverter->isValid());
-    } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar16) {
+    } else if (halFormat == HAL_PIXEL_FORMAT_RGBA_1010102) {
         mConverter = new ColorConverter(
                 mColorFormat, OMX_COLOR_FormatYUV444Y410);
         CHECK(mConverter->isValid());
@@ -300,6 +318,46 @@
             dst_u += dst_c_stride;
             dst_v += dst_c_stride;
         }
+    } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar16) {
+        const uint16_t *src_y = (const uint16_t *)data;
+        const uint16_t *src_u = (const uint16_t *)data + mWidth * mHeight;
+        const uint16_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
+
+        src_y += mCropLeft + mCropTop * mWidth;
+        src_u += (mCropLeft + mCropTop * mWidth / 2) / 2;
+        src_v += (mCropLeft + mCropTop * mWidth / 2) / 2;
+
+        uint8_t *dst_y = (uint8_t *)dst;
+        size_t dst_y_size = buf->stride * buf->height;
+        size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
+        size_t dst_c_size = dst_c_stride * buf->height / 2;
+        uint8_t *dst_v = dst_y + dst_y_size;
+        uint8_t *dst_u = dst_v + dst_c_size;
+
+        dst_y += mCropTop * buf->stride + mCropLeft;
+        dst_v += (mCropTop / 2) * dst_c_stride + mCropLeft / 2;
+        dst_u += (mCropTop / 2) * dst_c_stride + mCropLeft / 2;
+
+        for (int y = 0; y < mCropHeight; ++y) {
+            for (int x = 0; x < mCropWidth; ++x) {
+                dst_y[x] = (uint8_t)(src_y[x] >> 2);
+            }
+
+            src_y += mWidth;
+            dst_y += buf->stride;
+        }
+
+        for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
+            for (int x = 0; x < (mCropWidth + 1) / 2; ++x) {
+                dst_u[x] = (uint8_t)(src_u[x] >> 2);
+                dst_v[x] = (uint8_t)(src_v[x] >> 2);
+            }
+
+            src_u += mWidth / 2;
+            src_v += mWidth / 2;
+            dst_u += dst_c_stride;
+            dst_v += dst_c_stride;
+        }
     } else if (mColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
             || mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
         const uint8_t *src_y = (const uint8_t *)data;
diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml
index b86f4ad..0b554a2 100644
--- a/media/libstagefright/data/media_codecs_google_c2_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml
@@ -16,77 +16,77 @@
 
 <Included>
     <Decoders>
-        <MediaCodec name="c2.google.mp3.decoder" type="audio/mpeg">
+        <MediaCodec name="c2.android.mp3.decoder" type="audio/mpeg">
             <Limit name="channel-count" max="2" />
             <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <Limit name="bitrate" range="8000-320000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.amrnb.decoder" type="audio/3gpp">
+        <MediaCodec name="c2.android.amrnb.decoder" type="audio/3gpp">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000" />
             <Limit name="bitrate" range="4750-12200" />
         </MediaCodec>
-        <MediaCodec name="c2.google.amrwb.decoder" type="audio/amr-wb">
+        <MediaCodec name="c2.android.amrwb.decoder" type="audio/amr-wb">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="16000" />
             <Limit name="bitrate" range="6600-23850" />
         </MediaCodec>
-        <MediaCodec name="c2.google.aac.decoder" type="audio/mp4a-latm">
+        <MediaCodec name="c2.android.aac.decoder" type="audio/mp4a-latm">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="7350,8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <Limit name="bitrate" range="8000-960000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.g711.alaw.decoder" type="audio/g711-alaw">
+        <MediaCodec name="c2.android.g711.alaw.decoder" type="audio/g711-alaw">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000-48000" />
             <Limit name="bitrate" range="64000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.g711.mlaw.decoder" type="audio/g711-mlaw">
+        <MediaCodec name="c2.android.g711.mlaw.decoder" type="audio/g711-mlaw">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000-48000" />
             <Limit name="bitrate" range="64000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vorbis.decoder" type="audio/vorbis">
+        <MediaCodec name="c2.android.vorbis.decoder" type="audio/vorbis">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="8000-96000" />
             <Limit name="bitrate" range="32000-500000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.opus.decoder" type="audio/opus">
+        <MediaCodec name="c2.android.opus.decoder" type="audio/opus">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="48000" />
             <Limit name="bitrate" range="6000-510000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.raw.decoder" type="audio/raw">
+        <MediaCodec name="c2.android.raw.decoder" type="audio/raw">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="8000-96000" />
             <Limit name="bitrate" range="1-10000000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.flac.decoder" type="audio/flac">
+        <MediaCodec name="c2.android.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="c2.google.aac.encoder" type="audio/mp4a-latm">
+        <MediaCodec name="c2.android.aac.encoder" type="audio/mp4a-latm">
             <Limit name="channel-count" max="6" />
             <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <!-- also may support 64000, 88200  and 96000 Hz -->
             <Limit name="bitrate" range="8000-960000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.amrnb.encoder" type="audio/3gpp">
+        <MediaCodec name="c2.android.amrnb.encoder" type="audio/3gpp">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000" />
             <Limit name="bitrate" range="4750-12200" />
             <Feature name="bitrate-modes" value="CBR" />
         </MediaCodec>
-        <MediaCodec name="c2.google.amrwb.encoder" type="audio/amr-wb">
+        <MediaCodec name="c2.android.amrwb.encoder" type="audio/amr-wb">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="16000" />
             <Limit name="bitrate" range="6600-23850" />
             <Feature name="bitrate-modes" value="CBR" />
         </MediaCodec>
-        <MediaCodec name="c2.google.flac.encoder" type="audio/flac">
+        <MediaCodec name="c2.android.flac.encoder" type="audio/flac">
             <Limit name="channel-count" max="2" />
             <Limit name="sample-rate" ranges="1-655350" />
             <Limit name="bitrate" range="1-21000000" />
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index 593463b..adb45b3 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -16,7 +16,7 @@
 
 <Included>
     <Decoders>
-        <MediaCodec name="c2.google.mpeg4.decoder" type="video/mp4v-es">
+        <MediaCodec name="c2.android.mpeg4.decoder" type="video/mp4v-es">
             <!-- profiles and levels:  ProfileSimple : Level3 -->
             <Limit name="size" min="2x2" max="352x288" />
             <Limit name="alignment" value="2x2" />
@@ -25,7 +25,7 @@
             <Limit name="bitrate" range="1-384000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.h263.decoder" type="video/3gpp">
+        <MediaCodec name="c2.android.h263.decoder" type="video/3gpp">
             <!-- profiles and levels:  ProfileBaseline : Level30, ProfileBaseline : Level45
                     ProfileISWV2 : Level30, ProfileISWV2 : Level45 -->
             <Limit name="size" min="2x2" max="352x288" />
@@ -33,7 +33,7 @@
             <Limit name="bitrate" range="1-384000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.avc.decoder" type="video/avc">
+        <MediaCodec name="c2.android.avc.decoder" type="video/avc">
             <!-- profiles and levels:  ProfileHigh : Level52 -->
             <Limit name="size" min="2x2" max="4080x4080" />
             <Limit name="alignment" value="2x2" />
@@ -43,7 +43,7 @@
             <Limit name="bitrate" range="1-48000000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.hevc.decoder" type="video/hevc">
+        <MediaCodec name="c2.android.hevc.decoder" type="video/hevc">
             <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
             <Limit name="size" min="2x2" max="4096x4096" />
             <Limit name="alignment" value="2x2" />
@@ -53,7 +53,7 @@
             <Limit name="bitrate" range="1-10000000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vp8.decoder" type="video/x-vnd.on2.vp8">
+        <MediaCodec name="c2.android.vp8.decoder" type="video/x-vnd.on2.vp8">
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
@@ -62,7 +62,7 @@
             <Limit name="bitrate" range="1-40000000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vp9.decoder" type="video/x-vnd.on2.vp9">
+        <MediaCodec name="c2.android.vp9.decoder" type="video/x-vnd.on2.vp9">
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
@@ -74,13 +74,13 @@
     </Decoders>
 
     <Encoders>
-        <MediaCodec name="c2.google.h263.encoder" type="video/3gpp">
+        <MediaCodec name="c2.android.h263.encoder" type="video/3gpp">
             <!-- profiles and levels:  ProfileBaseline : Level45 -->
             <Limit name="size" min="176x144" max="176x144" />
             <Limit name="alignment" value="16x16" />
             <Limit name="bitrate" range="1-128000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.avc.encoder" type="video/avc">
+        <MediaCodec name="c2.android.avc.encoder" type="video/avc">
             <!-- profiles and levels:  ProfileBaseline : Level41 -->
             <Limit name="size" min="16x16" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
@@ -90,7 +90,7 @@
             <Limit name="bitrate" range="1-12000000" />
             <Feature name="intra-refresh" />
         </MediaCodec>
-        <MediaCodec name="c2.google.mpeg4.encoder" type="video/mp4v-es">
+        <MediaCodec name="c2.android.mpeg4.encoder" type="video/mp4v-es">
             <!-- profiles and levels:  ProfileCore : Level2 -->
             <Limit name="size" min="16x16" max="176x144" />
             <Limit name="alignment" value="16x16" />
@@ -98,7 +98,7 @@
             <Limit name="blocks-per-second" range="12-1485" />
             <Limit name="bitrate" range="1-64000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vp8.encoder" type="video/x-vnd.on2.vp8">
+        <MediaCodec name="c2.android.vp8.encoder" type="video/x-vnd.on2.vp8">
             <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
@@ -108,7 +108,7 @@
             <Limit name="bitrate" range="1-40000000" />
             <Feature name="bitrate-modes" value="VBR,CBR" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vp9.encoder" type="video/x-vnd.on2.vp9">
+        <MediaCodec name="c2.android.vp9.encoder" type="video/x-vnd.on2.vp9">
             <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 3b84018..738f066 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -944,6 +944,40 @@
     return mItems[index].mName;
 }
 
+AMessage::ItemData AMessage::getEntryAt(size_t index) const {
+    ItemData it;
+    if (index < mNumItems) {
+        switch (mItems[index].mType) {
+            case kTypeInt32:    it.set(mItems[index].u.int32Value); break;
+            case kTypeInt64:    it.set(mItems[index].u.int64Value); break;
+            case kTypeSize:     it.set(mItems[index].u.sizeValue); break;
+            case kTypeFloat:    it.set(mItems[index].u.floatValue); break;
+            case kTypeDouble:   it.set(mItems[index].u.doubleValue); break;
+            case kTypePointer:  it.set(mItems[index].u.ptrValue); break;
+            case kTypeRect:     it.set(mItems[index].u.rectValue); break;
+            case kTypeString:   it.set(*mItems[index].u.stringValue); break;
+            case kTypeObject: {
+                sp<RefBase> obj = mItems[index].u.refValue;
+                it.set(obj);
+                break;
+            }
+            case kTypeMessage: {
+                sp<AMessage> msg = static_cast<AMessage *>(mItems[index].u.refValue);
+                it.set(msg);
+                break;
+            }
+            case kTypeBuffer: {
+                sp<ABuffer> buf = static_cast<ABuffer *>(mItems[index].u.refValue);
+                it.set(buf);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return it;
+}
+
 status_t AMessage::setEntryNameAt(size_t index, const char *name) {
     if (index >= mNumItems) {
         return BAD_INDEX;
@@ -964,6 +998,60 @@
     return OK;
 }
 
+status_t AMessage::setEntryAt(size_t index, const ItemData &item) {
+    AString stringValue;
+    sp<RefBase> refValue;
+    sp<AMessage> msgValue;
+    sp<ABuffer> bufValue;
+
+    if (index >= mNumItems) {
+        return BAD_INDEX;
+    }
+    if (!item.used()) {
+        return BAD_VALUE;
+    }
+    Item *dst = &mItems[index];
+    freeItemValue(dst);
+
+    // some values can be directly set with the getter. others need items to be allocated
+    if (item.find(&dst->u.int32Value)) {
+        dst->mType = kTypeInt32;
+    } else if (item.find(&dst->u.int64Value)) {
+        dst->mType = kTypeInt64;
+    } else if (item.find(&dst->u.sizeValue)) {
+        dst->mType = kTypeSize;
+    } else if (item.find(&dst->u.floatValue)) {
+        dst->mType = kTypeFloat;
+    } else if (item.find(&dst->u.doubleValue)) {
+        dst->mType = kTypeDouble;
+    } else if (item.find(&dst->u.ptrValue)) {
+        dst->mType = kTypePointer;
+    } else if (item.find(&dst->u.rectValue)) {
+        dst->mType = kTypeRect;
+    } else if (item.find(&stringValue)) {
+        dst->u.stringValue = new AString(stringValue);
+        dst->mType = kTypeString;
+    } else if (item.find(&refValue)) {
+        if (refValue != NULL) { refValue->incStrong(this); }
+        dst->u.refValue = refValue.get();
+        dst->mType = kTypeObject;
+    } else if (item.find(&msgValue)) {
+        if (msgValue != NULL) { msgValue->incStrong(this); }
+        dst->u.refValue = msgValue.get();
+        dst->mType = kTypeMessage;
+    } else if (item.find(&bufValue)) {
+        if (bufValue != NULL) { bufValue->incStrong(this); }
+        dst->u.refValue = bufValue.get();
+        dst->mType = kTypeBuffer;
+    } else {
+        // unsupported item - we should not be here.
+        dst->mType = kTypeInt32;
+        dst->u.int32Value = 0xDEADDEAD;
+        return BAD_TYPE;
+    }
+    return OK;
+}
+
 status_t AMessage::removeEntryAt(size_t index) {
     if (index >= mNumItems) {
         return BAD_INDEX;
@@ -983,6 +1071,34 @@
     return OK;
 }
 
+void AMessage::setItem(const char *name, const ItemData &item) {
+    if (item.used()) {
+        Item *it = allocateItem(name);
+        if (it != nullptr) {
+            setEntryAt(it - mItems, item);
+        }
+    }
+}
+
+AMessage::ItemData AMessage::findItem(const char *name) const {
+    return getEntryAt(findEntryByName(name));
+}
+
+void AMessage::extend(const sp<AMessage> &other) {
+    // ignore null messages
+    if (other == nullptr) {
+        return;
+    }
+
+    for (size_t ix = 0; ix < other->mNumItems; ++ix) {
+        Item *it = allocateItem(other->mItems[ix].mName);
+        if (it != nullptr) {
+            ItemData data = other->getEntryAt(ix);
+            setEntryAt(it - mItems, data);
+        }
+    }
+}
+
 size_t AMessage::findEntryByName(const char *name) const {
     return name == nullptr ? countEntries() : findItemIndex(name, strlen(name));
 }
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AData.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AData.h
index 49aa0dc..5acc6d6 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AData.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AData.h
@@ -25,6 +25,9 @@
 #include <media/stagefright/foundation/TypeTraits.h>
 #include <media/stagefright/foundation/Flagged.h>
 
+#undef HIDE
+#define HIDE __attribute__((visibility("hidden")))
+
 namespace android {
 
 /**
@@ -78,7 +81,7 @@
  * This class is needed as member function specialization is not allowed for a
  * templated class.
  */
-struct _AUnion_impl {
+struct HIDE _AUnion_impl {
     /**
      * Calls placement constuctor for type T with arbitrary arguments for a storage at an address.
      * Storage MUST be large enough to contain T.
@@ -113,13 +116,13 @@
 
 /** Constructor specialization for void type */
 template<>
-inline void _AUnion_impl::emplace<void>(size_t totalSize, void *addr) {
+HIDE inline void _AUnion_impl::emplace<void>(size_t totalSize, void *addr) {
     memset(addr, 0, totalSize);
 }
 
 /** Destructor specialization for void type */
 template<>
-inline void _AUnion_impl::del<void>(void *) {
+HIDE inline void _AUnion_impl::del<void>(void *) {
 }
 
 /// \endcond
@@ -221,7 +224,7 @@
 template<
         typename T,
         bool=std::is_copy_assignable<T>::value>
-struct _AData_copier {
+struct HIDE _AData_copier {
     static_assert(std::is_copy_assignable<T>::value, "T must be copy assignable here");
 
     /**
@@ -294,7 +297,7 @@
  *
  */
 template<typename T>
-struct _AData_copier<T, false> {
+struct HIDE _AData_copier<T, false> {
     static_assert(!std::is_copy_assignable<T>::value, "T must not be copy assignable here");
     static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible here");
 
@@ -318,7 +321,7 @@
 template<
         typename T,
         bool=std::is_move_assignable<T>::value>
-struct _AData_mover {
+struct HIDE _AData_mover {
     static_assert(std::is_move_assignable<T>::value, "T must be move assignable here");
 
     /**
@@ -389,7 +392,7 @@
  *
  */
 template<typename T>
-struct _AData_mover<T, false> {
+struct HIDE _AData_mover<T, false> {
     static_assert(!std::is_move_assignable<T>::value, "T must not be move assignable here");
     static_assert(std::is_move_constructible<T>::value, "T must be move constructible here");
 
@@ -407,13 +410,13 @@
  * \param Ts types to consider for the member
  */
 template<typename Flagger, typename U, typename ...Ts>
-struct _AData_deleter;
+struct HIDE _AData_deleter;
 
 /**
  * Template specialization when there are still types to consider (T and rest)
  */
 template<typename Flagger, typename U, typename T, typename ...Ts>
-struct _AData_deleter<Flagger, U, T, Ts...> {
+struct HIDE _AData_deleter<Flagger, U, T, Ts...> {
     static bool del(typename Flagger::type flags, U &data) {
         if (Flagger::canDeleteAs(flags, Flagger::flagFor((T*)0))) {
             data.template del<T>();
@@ -427,7 +430,7 @@
  * Template specialization when there are no more types to consider.
  */
 template<typename Flagger, typename U>
-struct _AData_deleter<Flagger, U> {
+struct HIDE _AData_deleter<Flagger, U> {
     inline static bool del(typename Flagger::type, U &) {
         return false;
     }
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
index d90a0de..742651e 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
@@ -19,6 +19,7 @@
 #define A_MESSAGE_H_
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AData.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
@@ -155,6 +156,9 @@
     // their refcount incremented.
     sp<AMessage> dup() const;
 
+    // Adds all items from other into this.
+    void extend(const sp<AMessage> &other);
+
     // Performs a shallow or deep comparison of |this| and |other| and returns
     // an AMessage with the differences.
     // Warning: RefBase items, i.e. "objects" are _not_ copied but only have
@@ -180,10 +184,39 @@
         kTypeBuffer,
     };
 
+    struct Rect {
+        int32_t mLeft, mTop, mRight, mBottom;
+    };
+
     size_t countEntries() const;
     const char *getEntryNameAt(size_t index, Type *type) const;
 
     /**
+     * Retrieves the item at a specific index.
+     */
+    typedef AData<
+        int32_t, int64_t, size_t, float, double, Rect, AString,
+        void *, sp<AMessage>, sp<ABuffer>, sp<RefBase>>::Basic ItemData;
+
+    /**
+     * Finds an item by name. This can be used if the type is unknown.
+     *
+     * \param name name of the item
+     * Returns an empty item if no item is present with that name.
+     */
+    ItemData findItem(const char *name) const;
+
+    /**
+     * Sets an item of arbitrary type. Does nothing if the item value is empty.
+     *
+     * \param name name of the item
+     * \param item value of the item
+     */
+    void setItem(const char *name, const ItemData &item);
+
+    ItemData getEntryAt(size_t index) const;
+
+    /**
      * Finds an entry by name and returns its index.
      *
      * \retval countEntries() if the entry is not found.
@@ -204,6 +237,19 @@
     status_t setEntryNameAt(size_t index, const char *name);
 
     /**
+     * Sets the item of an entry based on index.
+     *
+     * \param index index of the entry
+     * \param item new item of the entry
+     *
+     * \retval OK the item was set successfully
+     * \retval BAD_INDEX invalid index
+     * \retval BAD_VALUE item is invalid (null)
+     * \retval BAD_TYPE type is unsupported (should not happen)
+     */
+    status_t setEntryAt(size_t index, const ItemData &item);
+
+    /**
      * Removes an entry based on index.
      *
      * \param index index of the entry
@@ -227,10 +273,6 @@
     wp<AHandler> mHandler;
     wp<ALooper> mLooper;
 
-    struct Rect {
-        int32_t mLeft, mTop, mRight, mBottom;
-    };
-
     struct Item {
         union {
             int32_t int32Value;
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/TypeTraits.h b/media/libstagefright/foundation/include/media/stagefright/foundation/TypeTraits.h
index 1250e9b..2041b22 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/TypeTraits.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/TypeTraits.h
@@ -19,6 +19,9 @@
 
 #include <type_traits>
 
+#undef HIDE
+#define HIDE __attribute__((visibility("hidden")))
+
 namespace android {
 
 /**
@@ -31,7 +34,7 @@
  * Type support utility class to check if a type is an integral type or an enum.
  */
 template<typename T>
-struct is_integral_or_enum
+struct HIDE is_integral_or_enum
     : std::integral_constant<bool, std::is_integral<T>::value || std::is_enum<T>::value> { };
 
 /**
@@ -46,7 +49,7 @@
         typename U=typename std::enable_if<is_integral_or_enum<T>::value>::type,
         bool=std::is_enum<T>::value,
         bool=std::is_integral<T>::value>
-struct underlying_integral_type {
+struct HIDE underlying_integral_type {
     static_assert(!std::is_enum<T>::value, "T should not be enum here");
     static_assert(!std::is_integral<T>::value, "T should not be integral here");
     typedef U type;
@@ -54,7 +57,7 @@
 
 /** Specialization for enums. */
 template<typename T, typename U>
-struct underlying_integral_type<T, U, true, false> {
+struct HIDE underlying_integral_type<T, U, true, false> {
     static_assert(std::is_enum<T>::value, "T should be enum here");
     static_assert(!std::is_integral<T>::value, "T should not be integral here");
     typedef typename std::underlying_type<T>::type type;
@@ -62,7 +65,7 @@
 
 /** Specialization for non-enum std-integral types. */
 template<typename T, typename U>
-struct underlying_integral_type<T, U, false, true> {
+struct HIDE underlying_integral_type<T, U, false, true> {
     static_assert(!std::is_enum<T>::value, "T should not be enum here");
     static_assert(std::is_integral<T>::value, "T should be integral here");
     typedef T type;
@@ -72,7 +75,7 @@
  * Type support utility class to check if the underlying integral type is signed.
  */
 template<typename T>
-struct is_signed_integral
+struct HIDE is_signed_integral
     : std::integral_constant<bool, std::is_signed<
             typename underlying_integral_type<T, unsigned>::type>::value> { };
 
@@ -80,7 +83,7 @@
  * Type support utility class to check if the underlying integral type is unsigned.
  */
 template<typename T>
-struct is_unsigned_integral
+struct HIDE is_unsigned_integral
     : std::integral_constant<bool, std::is_unsigned<
             typename underlying_integral_type<T, signed>::type>::value> {
 };
@@ -92,26 +95,26 @@
  * member constant |value| equal to true. Otherwise value is false.
  */
 template<typename T, typename ...Us>
-struct is_one_of;
+struct HIDE is_one_of;
 
 /// \if 0
 /**
  * Template specialization when first type matches the searched type.
  */
 template<typename T, typename ...Us>
-struct is_one_of<T, T, Us...> : std::true_type {};
+struct HIDE is_one_of<T, T, Us...> : std::true_type {};
 
 /**
  * Template specialization when first type does not match the searched type.
  */
 template<typename T, typename U, typename ...Us>
-struct is_one_of<T, U, Us...> : is_one_of<T, Us...> {};
+struct HIDE is_one_of<T, U, Us...> : is_one_of<T, Us...> {};
 
 /**
  * Template specialization when there are no types to search.
  */
 template<typename T>
-struct is_one_of<T> : std::false_type {};
+struct HIDE is_one_of<T> : std::false_type {};
 /// \endif
 
 /**
@@ -121,44 +124,44 @@
  * Otherwise value is false.
  */
 template<typename ...Us>
-struct are_unique;
+struct HIDE are_unique;
 
 /// \if 0
 /**
  * Template specialization when there are no types.
  */
 template<>
-struct are_unique<> : std::true_type {};
+struct HIDE are_unique<> : std::true_type {};
 
 /**
  * Template specialization when there is at least one type to check.
  */
 template<typename T, typename ...Us>
-struct are_unique<T, Us...>
+struct HIDE are_unique<T, Us...>
     : std::integral_constant<bool, are_unique<Us...>::value && !is_one_of<T, Us...>::value> {};
 /// \endif
 
 /// \if 0
 template<size_t Base, typename T, typename ...Us>
-struct _find_first_impl;
+struct HIDE _find_first_impl;
 
 /**
  * Template specialization when there are no types to search.
  */
 template<size_t Base, typename T>
-struct _find_first_impl<Base, T> : std::integral_constant<size_t, 0> {};
+struct HIDE _find_first_impl<Base, T> : std::integral_constant<size_t, 0> {};
 
 /**
  * Template specialization when T is the first type in Us.
  */
 template<size_t Base, typename T, typename ...Us>
-struct _find_first_impl<Base, T, T, Us...> : std::integral_constant<size_t, Base> {};
+struct HIDE _find_first_impl<Base, T, T, Us...> : std::integral_constant<size_t, Base> {};
 
 /**
  * Template specialization when T is not the first type in Us.
  */
 template<size_t Base, typename T, typename U, typename ...Us>
-struct _find_first_impl<Base, T, U, Us...>
+struct HIDE _find_first_impl<Base, T, U, Us...>
     : std::integral_constant<size_t, _find_first_impl<Base + 1, T, Us...>::value> {};
 
 /// \endif
@@ -169,7 +172,7 @@
  * If T occurs in Us, index is the 1-based left-most index of T in Us. Otherwise, index is 0.
  */
 template<typename T, typename ...Us>
-struct find_first {
+struct HIDE find_first {
     static constexpr size_t index = _find_first_impl<1, T, Us...>::value;
 };
 
@@ -180,13 +183,13 @@
  * Adds a base index.
  */
 template<size_t Base, typename T, typename ...Us>
-struct _find_first_convertible_to_helper;
+struct HIDE _find_first_convertible_to_helper;
 
 /**
  * Template specialization for when there are more types to consider
  */
 template<size_t Base, typename T, typename U, typename ...Us>
-struct _find_first_convertible_to_helper<Base, T, U, Us...> {
+struct HIDE _find_first_convertible_to_helper<Base, T, U, Us...> {
     static constexpr size_t index =
         std::is_convertible<T, U>::value ? Base :
                 _find_first_convertible_to_helper<Base + 1, T, Us...>::index;
@@ -199,7 +202,7 @@
  * Template specialization for when there are no more types to consider
  */
 template<size_t Base, typename T>
-struct _find_first_convertible_to_helper<Base, T> {
+struct HIDE _find_first_convertible_to_helper<Base, T> {
     static constexpr size_t index = 0;
     typedef void type;
 };
@@ -216,7 +219,7 @@
  * \param Us types into which the conversion is considered
  */
 template<typename T, typename ...Us>
-struct find_first_convertible_to : public _find_first_convertible_to_helper<1, T, Us...> { };
+struct HIDE find_first_convertible_to : public _find_first_convertible_to_helper<1, T, Us...> { };
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index f6d4727..dc58c15 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -32,32 +32,30 @@
 class MediaCodecBuffer;
 class IMediaSource;
 class VideoFrame;
+struct MediaCodec;
 
-struct FrameDecoder {
+struct FrameRect {
+    int32_t left, top, right, bottom;
+};
+
+struct FrameDecoder : public RefBase {
     FrameDecoder(
             const AString &componentName,
             const sp<MetaData> &trackMeta,
-            const sp<IMediaSource> &source) :
-                mComponentName(componentName),
-                mTrackMeta(trackMeta),
-                mSource(source),
-                mDstFormat(OMX_COLOR_Format16bitRGB565),
-                mDstBpp(2) {}
+            const sp<IMediaSource> &source);
 
-    sp<IMemory> extractFrame(int64_t frameTimeUs, int option, int colorFormat);
+    status_t init(
+            int64_t frameTimeUs, size_t numFrames, int option, int colorFormat);
 
-    status_t extractFrames(
-            int64_t frameTimeUs,
-            size_t numFrames,
-            int option,
-            int colorFormat,
-            std::vector<sp<IMemory> >* frames);
+    sp<IMemory> extractFrame(FrameRect *rect = NULL);
+
+    status_t extractFrames(std::vector<sp<IMemory> >* frames);
 
     static sp<IMemory> getMetadataOnly(
             const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail = false);
 
 protected:
-    virtual ~FrameDecoder() {}
+    virtual ~FrameDecoder();
 
     virtual sp<AMessage> onGetFormatAndSeekOptions(
             int64_t frameTimeUs,
@@ -65,6 +63,8 @@
             int seekMode,
             MediaSource::ReadOptions *options) = 0;
 
+    virtual status_t onExtractRect(FrameRect *rect) = 0;
+
     virtual status_t onInputReceived(
             const sp<MediaCodecBuffer> &codecBuffer,
             MetaDataBase &sampleMeta,
@@ -92,13 +92,13 @@
     OMX_COLOR_FORMATTYPE mDstFormat;
     int32_t mDstBpp;
     std::vector<sp<IMemory> > mFrames;
+    MediaSource::ReadOptions mReadOptions;
+    sp<MediaCodec> mDecoder;
+    sp<AMessage> mOutputFormat;
+    bool mHaveMoreInputs;
+    bool mFirstSample;
 
-    static bool getDstColorFormat(
-            android_pixel_format_t colorFormat,
-            OMX_COLOR_FORMATTYPE *dstFormat,
-            int32_t *dstBpp);
-
-    status_t extractInternal(int64_t frameTimeUs, size_t numFrames, int option);
+    status_t extractInternal();
 
     DISALLOW_EVIL_CONSTRUCTORS(FrameDecoder);
 };
@@ -107,13 +107,7 @@
     VideoFrameDecoder(
             const AString &componentName,
             const sp<MetaData> &trackMeta,
-            const sp<IMediaSource> &source) :
-                FrameDecoder(componentName, trackMeta, source),
-                mIsAvcOrHevc(false),
-                mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
-                mTargetTimeUs(-1ll),
-                mNumFrames(0),
-                mNumFramesDecoded(0) {}
+            const sp<IMediaSource> &source);
 
 protected:
     virtual sp<AMessage> onGetFormatAndSeekOptions(
@@ -122,6 +116,11 @@
             int seekMode,
             MediaSource::ReadOptions *options) override;
 
+    virtual status_t onExtractRect(FrameRect *rect) override {
+        // Rect extraction for sequences is not supported for now.
+        return (rect == NULL) ? OK : ERROR_UNSUPPORTED;
+    }
+
     virtual status_t onInputReceived(
             const sp<MediaCodecBuffer> &codecBuffer,
             MetaDataBase &sampleMeta,
@@ -146,10 +145,7 @@
     ImageDecoder(
             const AString &componentName,
             const sp<MetaData> &trackMeta,
-            const sp<IMediaSource> &source) :
-                FrameDecoder(componentName, trackMeta, source),
-                mFrame(NULL), mGridRows(1), mGridCols(1),
-                mTilesDecoded(0), mThumbnail(false) {}
+            const sp<IMediaSource> &source);
 
 protected:
     virtual sp<AMessage> onGetFormatAndSeekOptions(
@@ -158,6 +154,8 @@
             int seekMode,
             MediaSource::ReadOptions *options) override;
 
+    virtual status_t onExtractRect(FrameRect *rect) override;
+
     virtual status_t onInputReceived(
             const sp<MediaCodecBuffer> &codecBuffer __unused,
             MetaDataBase &sampleMeta __unused,
@@ -172,10 +170,14 @@
 
 private:
     VideoFrame *mFrame;
+    int32_t mWidth;
+    int32_t mHeight;
     int32_t mGridRows;
     int32_t mGridCols;
+    int32_t mTileWidth;
+    int32_t mTileHeight;
     int32_t mTilesDecoded;
-    bool mThumbnail;
+    int32_t mTargetTiles;
 };
 
 }  // namespace android
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index 209f850..f78e125 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -27,8 +27,10 @@
 
 class DataSource;
 class MediaExtractor;
+struct ImageDecoder;
+struct FrameRect;
 
-struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
+struct StagefrightMetadataRetriever : public MediaMetadataRetrieverBase {
     StagefrightMetadataRetriever();
     virtual ~StagefrightMetadataRetriever();
 
@@ -44,6 +46,8 @@
             int64_t timeUs, int option, int colorFormat, bool metaOnly);
     virtual sp<IMemory> getImageAtIndex(
             int index, int colorFormat, bool metaOnly, bool thumbnail);
+    virtual sp<IMemory> getImageRectAtIndex(
+            int index, int colorFormat, int left, int top, int right, int bottom);
     virtual status_t getFrameAtIndex(
             std::vector<sp<IMemory> >* frames,
             int frameIndex, int numFrames, int colorFormat, bool metaOnly);
@@ -59,6 +63,8 @@
     KeyedVector<int, String8> mMetaData;
     MediaAlbumArt *mAlbumArt;
 
+    sp<ImageDecoder> mImageDecoder;
+    int mLastImageIndex;
     void parseMetaData();
     // Delete album art and clear metadata.
     void clearMetadata();
@@ -66,6 +72,8 @@
     status_t getFrameInternal(
             int64_t timeUs, int numFrames, int option, int colorFormat, bool metaOnly,
             sp<IMemory>* outFrame, std::vector<sp<IMemory> >* outFrames);
+    virtual sp<IMemory> getImageInternal(
+            int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect);
 
     StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
 
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 64caeed..97d15a7 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -446,6 +446,7 @@
         int32_t heavyCompression;
         int32_t targetRefLevel;
         int32_t encodedTargetLevel;
+        int32_t effectType;
     } drcParams_t;
 
     status_t setupAACCodec(
diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h
index a6c8981..5b3543d 100644
--- a/media/libstagefright/include/media/stagefright/ColorConverter.h
+++ b/media/libstagefright/include/media/stagefright/ColorConverter.h
@@ -75,10 +75,16 @@
     status_t convertYUV420Planar(
             const BitmapParams &src, const BitmapParams &dst);
 
+    status_t convertYUV420PlanarUseLibYUV(
+            const BitmapParams &src, const BitmapParams &dst);
+
     status_t convertYUV420Planar16(
             const BitmapParams &src, const BitmapParams &dst);
 
-    status_t convertYUV420PlanarUseLibYUV(
+    status_t convertYUV420Planar16ToY410(
+            const BitmapParams &src, const BitmapParams &dst);
+
+    status_t convertYUV420Planar16ToRGB(
             const BitmapParams &src, const BitmapParams &dst);
 
     status_t convertQCOMYUV420SemiPlanar(
@@ -90,10 +96,6 @@
     status_t convertTIYUV420PackedSemiPlanar(
             const BitmapParams &src, const BitmapParams &dst);
 
-    void writeToDst(void *dst_ptr, uint8_t *kAdjustedClip, bool uncropped,
-            signed r1, signed g1, signed b1,
-            signed r2, signed g2, signed b2);
-
     ColorConverter(const ColorConverter &);
     ColorConverter &operator=(const ColorConverter &);
 };
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
new file mode 100644
index 0000000..3ef4c0e
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef MEDIA_CODEC_CONSTANTS_H_
+#define MEDIA_CODEC_CONSTANTS_H_
+
+namespace {
+
+// from MediaCodecInfo.java
+constexpr int32_t AVCProfileBaseline = 0x01;
+constexpr int32_t AVCProfileMain     = 0x02;
+constexpr int32_t AVCProfileExtended = 0x04;
+constexpr int32_t AVCProfileHigh     = 0x08;
+constexpr int32_t AVCProfileHigh10   = 0x10;
+constexpr int32_t AVCProfileHigh422  = 0x20;
+constexpr int32_t AVCProfileHigh444  = 0x40;
+constexpr int32_t AVCProfileConstrainedBaseline = 0x10000;
+constexpr int32_t AVCProfileConstrainedHigh     = 0x80000;
+
+constexpr int32_t AVCLevel1       = 0x01;
+constexpr int32_t AVCLevel1b      = 0x02;
+constexpr int32_t AVCLevel11      = 0x04;
+constexpr int32_t AVCLevel12      = 0x08;
+constexpr int32_t AVCLevel13      = 0x10;
+constexpr int32_t AVCLevel2       = 0x20;
+constexpr int32_t AVCLevel21      = 0x40;
+constexpr int32_t AVCLevel22      = 0x80;
+constexpr int32_t AVCLevel3       = 0x100;
+constexpr int32_t AVCLevel31      = 0x200;
+constexpr int32_t AVCLevel32      = 0x400;
+constexpr int32_t AVCLevel4       = 0x800;
+constexpr int32_t AVCLevel41      = 0x1000;
+constexpr int32_t AVCLevel42      = 0x2000;
+constexpr int32_t AVCLevel5       = 0x4000;
+constexpr int32_t AVCLevel51      = 0x8000;
+constexpr int32_t AVCLevel52      = 0x10000;
+
+constexpr int32_t H263ProfileBaseline             = 0x01;
+constexpr int32_t H263ProfileH320Coding           = 0x02;
+constexpr int32_t H263ProfileBackwardCompatible   = 0x04;
+constexpr int32_t H263ProfileISWV2                = 0x08;
+constexpr int32_t H263ProfileISWV3                = 0x10;
+constexpr int32_t H263ProfileHighCompression      = 0x20;
+constexpr int32_t H263ProfileInternet             = 0x40;
+constexpr int32_t H263ProfileInterlace            = 0x80;
+constexpr int32_t H263ProfileHighLatency          = 0x100;
+
+constexpr int32_t H263Level10      = 0x01;
+constexpr int32_t H263Level20      = 0x02;
+constexpr int32_t H263Level30      = 0x04;
+constexpr int32_t H263Level40      = 0x08;
+constexpr int32_t H263Level45      = 0x10;
+constexpr int32_t H263Level50      = 0x20;
+constexpr int32_t H263Level60      = 0x40;
+constexpr int32_t H263Level70      = 0x80;
+
+constexpr int32_t MPEG4ProfileSimple              = 0x01;
+constexpr int32_t MPEG4ProfileSimpleScalable      = 0x02;
+constexpr int32_t MPEG4ProfileCore                = 0x04;
+constexpr int32_t MPEG4ProfileMain                = 0x08;
+constexpr int32_t MPEG4ProfileNbit                = 0x10;
+constexpr int32_t MPEG4ProfileScalableTexture     = 0x20;
+constexpr int32_t MPEG4ProfileSimpleFace          = 0x40;
+constexpr int32_t MPEG4ProfileSimpleFBA           = 0x80;
+constexpr int32_t MPEG4ProfileBasicAnimated       = 0x100;
+constexpr int32_t MPEG4ProfileHybrid              = 0x200;
+constexpr int32_t MPEG4ProfileAdvancedRealTime    = 0x400;
+constexpr int32_t MPEG4ProfileCoreScalable        = 0x800;
+constexpr int32_t MPEG4ProfileAdvancedCoding      = 0x1000;
+constexpr int32_t MPEG4ProfileAdvancedCore        = 0x2000;
+constexpr int32_t MPEG4ProfileAdvancedScalable    = 0x4000;
+constexpr int32_t MPEG4ProfileAdvancedSimple      = 0x8000;
+
+constexpr int32_t MPEG4Level0      = 0x01;
+constexpr int32_t MPEG4Level0b     = 0x02;
+constexpr int32_t MPEG4Level1      = 0x04;
+constexpr int32_t MPEG4Level2      = 0x08;
+constexpr int32_t MPEG4Level3      = 0x10;
+constexpr int32_t MPEG4Level3b     = 0x18;
+constexpr int32_t MPEG4Level4      = 0x20;
+constexpr int32_t MPEG4Level4a     = 0x40;
+constexpr int32_t MPEG4Level5      = 0x80;
+constexpr int32_t MPEG4Level6      = 0x100;
+
+constexpr int32_t MPEG2ProfileSimple              = 0x00;
+constexpr int32_t MPEG2ProfileMain                = 0x01;
+constexpr int32_t MPEG2Profile422                 = 0x02;
+constexpr int32_t MPEG2ProfileSNR                 = 0x03;
+constexpr int32_t MPEG2ProfileSpatial             = 0x04;
+constexpr int32_t MPEG2ProfileHigh                = 0x05;
+
+constexpr int32_t MPEG2LevelLL     = 0x00;
+constexpr int32_t MPEG2LevelML     = 0x01;
+constexpr int32_t MPEG2LevelH14    = 0x02;
+constexpr int32_t MPEG2LevelHL     = 0x03;
+constexpr int32_t MPEG2LevelHP     = 0x04;
+
+constexpr int32_t AACObjectMain       = 1;
+constexpr int32_t AACObjectLC         = 2;
+constexpr int32_t AACObjectSSR        = 3;
+constexpr int32_t AACObjectLTP        = 4;
+constexpr int32_t AACObjectHE         = 5;
+constexpr int32_t AACObjectScalable   = 6;
+constexpr int32_t AACObjectERLC       = 17;
+constexpr int32_t AACObjectERScalable = 20;
+constexpr int32_t AACObjectLD         = 23;
+constexpr int32_t AACObjectHE_PS      = 29;
+constexpr int32_t AACObjectELD        = 39;
+constexpr int32_t AACObjectXHE        = 42;
+
+constexpr int32_t VP8Level_Version0 = 0x01;
+constexpr int32_t VP8Level_Version1 = 0x02;
+constexpr int32_t VP8Level_Version2 = 0x04;
+constexpr int32_t VP8Level_Version3 = 0x08;
+
+constexpr int32_t VP8ProfileMain = 0x01;
+
+constexpr int32_t VP9Profile0 = 0x01;
+constexpr int32_t VP9Profile1 = 0x02;
+constexpr int32_t VP9Profile2 = 0x04;
+constexpr int32_t VP9Profile3 = 0x08;
+constexpr int32_t VP9Profile2HDR = 0x1000;
+constexpr int32_t VP9Profile3HDR = 0x2000;
+
+constexpr int32_t VP9Level1  = 0x1;
+constexpr int32_t VP9Level11 = 0x2;
+constexpr int32_t VP9Level2  = 0x4;
+constexpr int32_t VP9Level21 = 0x8;
+constexpr int32_t VP9Level3  = 0x10;
+constexpr int32_t VP9Level31 = 0x20;
+constexpr int32_t VP9Level4  = 0x40;
+constexpr int32_t VP9Level41 = 0x80;
+constexpr int32_t VP9Level5  = 0x100;
+constexpr int32_t VP9Level51 = 0x200;
+constexpr int32_t VP9Level52 = 0x400;
+constexpr int32_t VP9Level6  = 0x800;
+constexpr int32_t VP9Level61 = 0x1000;
+constexpr int32_t VP9Level62 = 0x2000;
+
+constexpr int32_t HEVCProfileMain        = 0x01;
+constexpr int32_t HEVCProfileMain10      = 0x02;
+constexpr int32_t HEVCProfileMainStill   = 0x04;
+constexpr int32_t HEVCProfileMain10HDR10 = 0x1000;
+
+constexpr int32_t HEVCMainTierLevel1  = 0x1;
+constexpr int32_t HEVCHighTierLevel1  = 0x2;
+constexpr int32_t HEVCMainTierLevel2  = 0x4;
+constexpr int32_t HEVCHighTierLevel2  = 0x8;
+constexpr int32_t HEVCMainTierLevel21 = 0x10;
+constexpr int32_t HEVCHighTierLevel21 = 0x20;
+constexpr int32_t HEVCMainTierLevel3  = 0x40;
+constexpr int32_t HEVCHighTierLevel3  = 0x80;
+constexpr int32_t HEVCMainTierLevel31 = 0x100;
+constexpr int32_t HEVCHighTierLevel31 = 0x200;
+constexpr int32_t HEVCMainTierLevel4  = 0x400;
+constexpr int32_t HEVCHighTierLevel4  = 0x800;
+constexpr int32_t HEVCMainTierLevel41 = 0x1000;
+constexpr int32_t HEVCHighTierLevel41 = 0x2000;
+constexpr int32_t HEVCMainTierLevel5  = 0x4000;
+constexpr int32_t HEVCHighTierLevel5  = 0x8000;
+constexpr int32_t HEVCMainTierLevel51 = 0x10000;
+constexpr int32_t HEVCHighTierLevel51 = 0x20000;
+constexpr int32_t HEVCMainTierLevel52 = 0x40000;
+constexpr int32_t HEVCHighTierLevel52 = 0x80000;
+constexpr int32_t HEVCMainTierLevel6  = 0x100000;
+constexpr int32_t HEVCHighTierLevel6  = 0x200000;
+constexpr int32_t HEVCMainTierLevel61 = 0x400000;
+constexpr int32_t HEVCHighTierLevel61 = 0x800000;
+constexpr int32_t HEVCMainTierLevel62 = 0x1000000;
+constexpr int32_t HEVCHighTierLevel62 = 0x2000000;
+
+constexpr int32_t DolbyVisionProfileDvavPer = 0x1;
+constexpr int32_t DolbyVisionProfileDvavPen = 0x2;
+constexpr int32_t DolbyVisionProfileDvheDer = 0x4;
+constexpr int32_t DolbyVisionProfileDvheDen = 0x8;
+constexpr int32_t DolbyVisionProfileDvheDtr = 0x10;
+constexpr int32_t DolbyVisionProfileDvheStn = 0x20;
+constexpr int32_t DolbyVisionProfileDvheDth = 0x40;
+constexpr int32_t DolbyVisionProfileDvheDtb = 0x80;
+constexpr int32_t DolbyVisionProfileDvheSt = 0x100;
+constexpr int32_t DolbyVisionProfileDvavSe = 0x200;
+
+constexpr int32_t DolbyVisionLevelHd24    = 0x1;
+constexpr int32_t DolbyVisionLevelHd30    = 0x2;
+constexpr int32_t DolbyVisionLevelFhd24   = 0x4;
+constexpr int32_t DolbyVisionLevelFhd30   = 0x8;
+constexpr int32_t DolbyVisionLevelFhd60   = 0x10;
+constexpr int32_t DolbyVisionLevelUhd24   = 0x20;
+constexpr int32_t DolbyVisionLevelUhd30   = 0x40;
+constexpr int32_t DolbyVisionLevelUhd48   = 0x80;
+constexpr int32_t DolbyVisionLevelUhd60   = 0x100;
+
+constexpr int32_t BITRATE_MODE_CBR = 2;
+constexpr int32_t BITRATE_MODE_CQ = 0;
+constexpr int32_t BITRATE_MODE_VBR = 1;
+
+constexpr int32_t COLOR_Format12bitRGB444             = 3;
+constexpr int32_t COLOR_Format16bitARGB1555           = 5;
+constexpr int32_t COLOR_Format16bitARGB4444           = 4;
+constexpr int32_t COLOR_Format16bitBGR565             = 7;
+constexpr int32_t COLOR_Format16bitRGB565             = 6;
+constexpr int32_t COLOR_Format18bitARGB1665           = 9;
+constexpr int32_t COLOR_Format18BitBGR666             = 41;
+constexpr int32_t COLOR_Format18bitRGB666             = 8;
+constexpr int32_t COLOR_Format19bitARGB1666           = 10;
+constexpr int32_t COLOR_Format24BitABGR6666           = 43;
+constexpr int32_t COLOR_Format24bitARGB1887           = 13;
+constexpr int32_t COLOR_Format24BitARGB6666           = 42;
+constexpr int32_t COLOR_Format24bitBGR888             = 12;
+constexpr int32_t COLOR_Format24bitRGB888             = 11;
+constexpr int32_t COLOR_Format25bitARGB1888           = 14;
+constexpr int32_t COLOR_Format32bitABGR8888           = 0x7F00A000;
+constexpr int32_t COLOR_Format32bitARGB8888           = 16;
+constexpr int32_t COLOR_Format32bitBGRA8888           = 15;
+constexpr int32_t COLOR_Format8bitRGB332              = 2;
+constexpr int32_t COLOR_FormatCbYCrY                  = 27;
+constexpr int32_t COLOR_FormatCrYCbY                  = 28;
+constexpr int32_t COLOR_FormatL16                     = 36;
+constexpr int32_t COLOR_FormatL2                      = 33;
+constexpr int32_t COLOR_FormatL24                     = 37;
+constexpr int32_t COLOR_FormatL32                     = 38;
+constexpr int32_t COLOR_FormatL4                      = 34;
+constexpr int32_t COLOR_FormatL8                      = 35;
+constexpr int32_t COLOR_FormatMonochrome              = 1;
+constexpr int32_t COLOR_FormatRawBayer10bit           = 31;
+constexpr int32_t COLOR_FormatRawBayer8bit            = 30;
+constexpr int32_t COLOR_FormatRawBayer8bitcompressed  = 32;
+constexpr int32_t COLOR_FormatRGBAFlexible            = 0x7F36A888;
+constexpr int32_t COLOR_FormatRGBFlexible             = 0x7F36B888;
+constexpr int32_t COLOR_FormatSurface                 = 0x7F000789;
+constexpr int32_t COLOR_FormatYCbYCr                  = 25;
+constexpr int32_t COLOR_FormatYCrYCb                  = 26;
+constexpr int32_t COLOR_FormatYUV411PackedPlanar      = 18;
+constexpr int32_t COLOR_FormatYUV411Planar            = 17;
+constexpr int32_t COLOR_FormatYUV420Flexible          = 0x7F420888;
+constexpr int32_t COLOR_FormatYUV420PackedPlanar      = 20;
+constexpr int32_t COLOR_FormatYUV420PackedSemiPlanar  = 39;
+constexpr int32_t COLOR_FormatYUV420Planar            = 19;
+constexpr int32_t COLOR_FormatYUV420SemiPlanar        = 21;
+constexpr int32_t COLOR_FormatYUV422Flexible          = 0x7F422888;
+constexpr int32_t COLOR_FormatYUV422PackedPlanar      = 23;
+constexpr int32_t COLOR_FormatYUV422PackedSemiPlanar  = 40;
+constexpr int32_t COLOR_FormatYUV422Planar            = 22;
+constexpr int32_t COLOR_FormatYUV422SemiPlanar        = 24;
+constexpr int32_t COLOR_FormatYUV444Flexible          = 0x7F444888;
+constexpr int32_t COLOR_FormatYUV444Interleaved       = 29;
+constexpr int32_t COLOR_QCOM_FormatYUV420SemiPlanar   = 0x7fa30c00;
+constexpr int32_t COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;
+
+constexpr char FEATURE_AdaptivePlayback[]       = "adaptive-playback";
+constexpr char FEATURE_IntraRefresh[] = "intra-refresh";
+constexpr char FEATURE_PartialFrame[] = "partial-frame";
+constexpr char FEATURE_SecurePlayback[]         = "secure-playback";
+constexpr char FEATURE_TunneledPlayback[]       = "tunneled-playback";
+
+// from MediaFormat.java
+constexpr char MIMETYPE_VIDEO_VP8[] = "video/x-vnd.on2.vp8";
+constexpr char MIMETYPE_VIDEO_VP9[] = "video/x-vnd.on2.vp9";
+constexpr char MIMETYPE_VIDEO_AVC[] = "video/avc";
+constexpr char MIMETYPE_VIDEO_HEVC[] = "video/hevc";
+constexpr char MIMETYPE_VIDEO_MPEG4[] = "video/mp4v-es";
+constexpr char MIMETYPE_VIDEO_H263[] = "video/3gpp";
+constexpr char MIMETYPE_VIDEO_MPEG2[] = "video/mpeg2";
+constexpr char MIMETYPE_VIDEO_RAW[] = "video/raw";
+constexpr char MIMETYPE_VIDEO_DOLBY_VISION[] = "video/dolby-vision";
+constexpr char MIMETYPE_VIDEO_SCRAMBLED[] = "video/scrambled";
+
+constexpr char MIMETYPE_AUDIO_AMR_NB[] = "audio/3gpp";
+constexpr char MIMETYPE_AUDIO_AMR_WB[] = "audio/amr-wb";
+constexpr char MIMETYPE_AUDIO_MPEG[] = "audio/mpeg";
+constexpr char MIMETYPE_AUDIO_AAC[] = "audio/mp4a-latm";
+constexpr char MIMETYPE_AUDIO_QCELP[] = "audio/qcelp";
+constexpr char MIMETYPE_AUDIO_VORBIS[] = "audio/vorbis";
+constexpr char MIMETYPE_AUDIO_OPUS[] = "audio/opus";
+constexpr char MIMETYPE_AUDIO_G711_ALAW[] = "audio/g711-alaw";
+constexpr char MIMETYPE_AUDIO_G711_MLAW[] = "audio/g711-mlaw";
+constexpr char MIMETYPE_AUDIO_RAW[] = "audio/raw";
+constexpr char MIMETYPE_AUDIO_FLAC[] = "audio/flac";
+constexpr char MIMETYPE_AUDIO_MSGSM[] = "audio/gsm";
+constexpr char MIMETYPE_AUDIO_AC3[] = "audio/ac3";
+constexpr char MIMETYPE_AUDIO_EAC3[] = "audio/eac3";
+constexpr char MIMETYPE_AUDIO_SCRAMBLED[] = "audio/scrambled";
+
+constexpr char MIMETYPE_IMAGE_ANDROID_HEIC[] = "image/vnd.android.heic";
+
+constexpr char MIMETYPE_TEXT_CEA_608[] = "text/cea-608";
+constexpr char MIMETYPE_TEXT_CEA_708[] = "text/cea-708";
+constexpr char MIMETYPE_TEXT_SUBRIP[] = "application/x-subrip";
+constexpr char MIMETYPE_TEXT_VTT[] = "text/vtt";
+
+constexpr int32_t COLOR_RANGE_FULL = 1;
+constexpr int32_t COLOR_RANGE_LIMITED = 2;
+constexpr int32_t COLOR_STANDARD_BT2020 = 6;
+constexpr int32_t COLOR_STANDARD_BT601_NTSC = 4;
+constexpr int32_t COLOR_STANDARD_BT601_PAL = 2;
+constexpr int32_t COLOR_STANDARD_BT709 = 1;
+constexpr int32_t COLOR_TRANSFER_HLG = 7;
+constexpr int32_t COLOR_TRANSFER_LINEAR = 1;
+constexpr int32_t COLOR_TRANSFER_SDR_VIDEO = 3;
+constexpr int32_t COLOR_TRANSFER_ST2084 = 6;
+
+constexpr char KEY_AAC_DRC_ATTENUATION_FACTOR[] = "aac-drc-cut-level";
+constexpr char KEY_AAC_DRC_BOOST_FACTOR[] = "aac-drc-boost-level";
+constexpr char KEY_AAC_DRC_EFFECT_TYPE[] = "aac-drc-effect-type";
+constexpr char KEY_AAC_DRC_HEAVY_COMPRESSION[] = "aac-drc-heavy-compression";
+constexpr char KEY_AAC_DRC_TARGET_REFERENCE_LEVEL[] = "aac-target-ref-level";
+constexpr char KEY_AAC_ENCODED_TARGET_LEVEL[] = "aac-encoded-target-level";
+constexpr char KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT[] = "aac-max-output-channel_count";
+constexpr char KEY_AAC_PROFILE[] = "aac-profile";
+constexpr char KEY_AAC_SBR_MODE[] = "aac-sbr-mode";
+constexpr char KEY_AUDIO_SESSION_ID[] = "audio-session-id";
+constexpr char KEY_BIT_RATE[] = "bitrate";
+constexpr char KEY_BITRATE_MODE[] = "bitrate-mode";
+constexpr char KEY_CA_SESSION_ID[] = "ca-session-id";
+constexpr char KEY_CA_SYSTEM_ID[] = "ca-system-id";
+constexpr char KEY_CAPTURE_RATE[] = "capture-rate";
+constexpr char KEY_CHANNEL_COUNT[] = "channel-count";
+constexpr char KEY_CHANNEL_MASK[] = "channel-mask";
+constexpr char KEY_COLOR_FORMAT[] = "color-format";
+constexpr char KEY_COLOR_RANGE[] = "color-range";
+constexpr char KEY_COLOR_STANDARD[] = "color-standard";
+constexpr char KEY_COLOR_TRANSFER[] = "color-transfer";
+constexpr char KEY_COMPLEXITY[] = "complexity";
+constexpr char KEY_DURATION[] = "durationUs";
+constexpr char KEY_FEATURE_[] = "feature-";
+constexpr char KEY_FLAC_COMPRESSION_LEVEL[] = "flac-compression-level";
+constexpr char KEY_FRAME_RATE[] = "frame-rate";
+constexpr char KEY_GRID_COLUMNS[] = "grid-cols";
+constexpr char KEY_GRID_ROWS[] = "grid-rows";
+constexpr char KEY_HDR_STATIC_INFO[] = "hdr-static-info";
+constexpr char KEY_HEIGHT[] = "height";
+constexpr char KEY_I_FRAME_INTERVAL[] = "i-frame-interval";
+constexpr char KEY_INTRA_REFRESH_PERIOD[] = "intra-refresh-period";
+constexpr char KEY_IS_ADTS[] = "is-adts";
+constexpr char KEY_IS_AUTOSELECT[] = "is-autoselect";
+constexpr char KEY_IS_DEFAULT[] = "is-default";
+constexpr char KEY_IS_FORCED_SUBTITLE[] = "is-forced-subtitle";
+constexpr char KEY_IS_TIMED_TEXT[] = "is-timed-text";
+constexpr char KEY_LANGUAGE[] = "language";
+constexpr char KEY_LATENCY[] = "latency";
+constexpr char KEY_LEVEL[] = "level";
+constexpr char KEY_MAX_BIT_RATE[] = "max-bitrate";
+constexpr char KEY_MAX_HEIGHT[] = "max-height";
+constexpr char KEY_MAX_INPUT_SIZE[] = "max-input-size";
+constexpr char KEY_MAX_WIDTH[] = "max-width";
+constexpr char KEY_MIME[] = "mime";
+constexpr char KEY_OPERATING_RATE[] = "operating-rate";
+constexpr char KEY_OUTPUT_REORDER_DEPTH[] = "output-reorder-depth";
+constexpr char KEY_PCM_ENCODING[] = "pcm-encoding";
+constexpr char KEY_PRIORITY[] = "priority";
+constexpr char KEY_PROFILE[] = "profile";
+constexpr char KEY_PUSH_BLANK_BUFFERS_ON_STOP[] = "push-blank-buffers-on-shutdown";
+constexpr char KEY_QUALITY[] = "quality";
+constexpr char KEY_REPEAT_PREVIOUS_FRAME_AFTER[] = "repeat-previous-frame-after";
+constexpr char KEY_ROTATION[] = "rotation-degrees";
+constexpr char KEY_SAMPLE_RATE[] = "sample-rate";
+constexpr char KEY_SLICE_HEIGHT[] = "slice-height";
+constexpr char KEY_STRIDE[] = "stride";
+constexpr char KEY_TEMPORAL_LAYERING[] = "ts-schema";
+constexpr char KEY_TILE_HEIGHT[] = "tile-height";
+constexpr char KEY_TILE_WIDTH[] = "tile-width";
+constexpr char KEY_TRACK_ID[] = "track-id";
+constexpr char KEY_WIDTH[] = "width";
+
+// from MediaCodec.java
+constexpr int32_t ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4;
+constexpr int32_t ERROR_INSUFFICIENT_RESOURCE = 1100;
+constexpr int32_t ERROR_KEY_EXPIRED = 2;
+constexpr int32_t ERROR_NO_KEY = 1;
+constexpr int32_t ERROR_RECLAIMED = 1101;
+constexpr int32_t ERROR_RESOURCE_BUSY = 3;
+constexpr int32_t ERROR_SESSION_NOT_OPENED = 5;
+constexpr int32_t ERROR_UNSUPPORTED_OPERATION = 6;
+constexpr char CODEC[] = "android.media.mediacodec.codec";
+constexpr char ENCODER[] = "android.media.mediacodec.encoder";
+constexpr char HEIGHT[] = "android.media.mediacodec.height";
+constexpr char MIME_TYPE[] = "android.media.mediacodec.mime";
+constexpr char MODE[] = "android.media.mediacodec.mode";
+constexpr char MODE_AUDIO[] = "audio";
+constexpr char MODE_VIDEO[] = "video";
+constexpr char ROTATION[] = "android.media.mediacodec.rotation";
+constexpr char SECURE[] = "android.media.mediacodec.secure";
+constexpr char WIDTH[] = "android.media.mediacodec.width";
+
+constexpr int32_t BUFFER_FLAG_CODEC_CONFIG = 2;
+constexpr int32_t BUFFER_FLAG_END_OF_STREAM = 4;
+constexpr int32_t BUFFER_FLAG_KEY_FRAME = 1;
+constexpr int32_t BUFFER_FLAG_PARTIAL_FRAME = 8;
+constexpr int32_t BUFFER_FLAG_SYNC_FRAME = 1;
+constexpr int32_t CONFIGURE_FLAG_ENCODE = 1;
+constexpr int32_t CRYPTO_MODE_AES_CBC     = 2;
+constexpr int32_t CRYPTO_MODE_AES_CTR     = 1;
+constexpr int32_t CRYPTO_MODE_UNENCRYPTED = 0;
+constexpr int32_t INFO_OUTPUT_BUFFERS_CHANGED = -3;
+constexpr int32_t INFO_OUTPUT_FORMAT_CHANGED  = -2;
+constexpr int32_t INFO_TRY_AGAIN_LATER        = -1;
+constexpr int32_t VIDEO_SCALING_MODE_SCALE_TO_FIT               = 1;
+constexpr int32_t VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
+constexpr char PARAMETER_KEY_REQUEST_SYNC_FRAME[] = "request-sync";
+constexpr char PARAMETER_KEY_SUSPEND[] = "drop-input-frames";
+constexpr char PARAMETER_KEY_VIDEO_BITRATE[] = "video-bitrate";
+
+}
+
+#endif  // MEDIA_CODEC_CONSTANTS_H_
+
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 3eb98f3..3e6942b 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -62,6 +62,7 @@
         "libmedia_omx",
         "libstagefright_foundation",
         "libstagefright_xmlparser",
+        "libutils",
     ],
 
     cflags: [
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
index baa7b81..5a46b26 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
@@ -22,6 +22,8 @@
 
 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
 #include <android/hardware/media/omx/1.0/IOmx.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
 
 namespace android {
 
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index fe08ab9..6d10f1c 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -224,7 +224,7 @@
 
 static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
     for (iter = mObj->mIds.begin(); iter != mObj->mIds.end(); ++iter) {
-        if (iter->array() == id.ptr && iter->size() == id.length) {
+        if (id.length == iter->size() && memcmp(iter->array(), id.ptr, iter->size()) == 0) {
             return true;
         }
     }
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index 1d295e4..f7b9cfd 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -216,12 +216,6 @@
 
 #endif /* __ANDROID_API__ >= 28 */
 
-#if __ANDROID_API__ >= 29
-
-media_status_t AMediaExtractor_disconnect(AMediaExtractor *ex);
-
-#endif /* __ANDROID_API__ >= 29 */
-
 #endif /* __ANDROID_API__ >= 21 */
 
 __END_DECLS
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index ef466a2..79bb9fe 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -216,11 +216,6 @@
             mWarmupNsMax = LONG_MAX;
         }
         mMixerBufferState = UNDEFINED;
-#if !LOG_NDEBUG
-        for (unsigned i = 0; i < FastMixerState::sMaxFastTracks; ++i) {
-            mFastTrackNames[i] = -1;
-        }
-#endif
         // we need to reconfigure all active tracks
         previousTrackMask = 0;
         mFastTracksGen = current->mFastTracksGen - 1;
@@ -245,9 +240,6 @@
             if (mMixer != NULL) {
                 mMixer->destroy(i);
             }
-#if !LOG_NDEBUG
-            mFastTrackNames[i] = -1;
-#endif
             // don't reset track dump state, since other side is ignoring it
             mGenerations[i] = fastTrack->mGeneration;
         }
@@ -259,7 +251,6 @@
             addedTracks &= ~(1 << i);
             const FastTrack* fastTrack = &current->mFastTracks[i];
             AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
-            ALOG_ASSERT(bufferProvider != NULL && mFastTrackNames[i] == -1);
             if (mMixer != NULL) {
                 const int name = i; // for clarity, choose name as fast track index.
                 status_t status = mMixer->create(
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 2b993ee..fc2dbbb 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -64,7 +64,7 @@
 
     virtual bool        isFastTrack() const { return (mFlags & AUDIO_INPUT_FLAG_FAST) != 0; }
 
-            void        setSilenced(bool silenced) { mSilenced = silenced; }
+            void        setSilenced(bool silenced) { if (!isPatchTrack()) mSilenced = silenced; }
             bool        isSilenced() const { return mSilenced; }
 
             status_t    getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 20de97c..dcad866 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4169,16 +4169,31 @@
     // buffer size, then write 0s to the output
     if (mSleepTimeUs == 0) {
         if (mMixerStatus == MIXER_TRACKS_ENABLED) {
-            mSleepTimeUs = mActiveSleepTimeUs >> sleepTimeShift;
-            if (mSleepTimeUs < kMinThreadSleepTimeUs) {
-                mSleepTimeUs = kMinThreadSleepTimeUs;
-            }
-            // reduce sleep time in case of consecutive application underruns to avoid
-            // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
-            // duration we would end up writing less data than needed by the audio HAL if
-            // the condition persists.
-            if (sleepTimeShift < kMaxThreadSleepTimeShift) {
-                sleepTimeShift++;
+            if (mPipeSink.get() != nullptr && mPipeSink == mNormalSink) {
+                // Using the Monopipe availableToWrite, we estimate the
+                // sleep time to retry for more data (before we underrun).
+                MonoPipe *monoPipe = static_cast<MonoPipe *>(mPipeSink.get());
+                const ssize_t availableToWrite = mPipeSink->availableToWrite();
+                const size_t pipeFrames = monoPipe->maxFrames();
+                const size_t framesLeft = pipeFrames - max(availableToWrite, 0);
+                // HAL_framecount <= framesDelay ~ framesLeft / 2 <= Normal_Mixer_framecount
+                const size_t framesDelay = std::min(
+                        mNormalFrameCount, max(framesLeft / 2, mFrameCount));
+                ALOGV("pipeFrames:%zu framesLeft:%zu framesDelay:%zu",
+                        pipeFrames, framesLeft, framesDelay);
+                mSleepTimeUs = framesDelay * MICROS_PER_SECOND / mSampleRate;
+            } else {
+                mSleepTimeUs = mActiveSleepTimeUs >> sleepTimeShift;
+                if (mSleepTimeUs < kMinThreadSleepTimeUs) {
+                    mSleepTimeUs = kMinThreadSleepTimeUs;
+                }
+                // reduce sleep time in case of consecutive application underruns to avoid
+                // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
+                // duration we would end up writing less data than needed by the audio HAL if
+                // the condition persists.
+                if (sleepTimeShift < kMaxThreadSleepTimeShift) {
+                    sleepTimeShift++;
+                }
             }
         } else {
             mSleepTimeUs = mIdleSleepTimeUs;
@@ -6603,13 +6618,27 @@
         if (mPipeSource != 0) {
             size_t framesToRead = mBufferSize / mFrameSize;
             framesToRead = min(mRsmpInFramesOA - rear, mRsmpInFramesP2 / 2);
-            framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
-                    framesToRead);
-            // since pipe is non-blocking, simulate blocking input by waiting for 1/2 of
-            // buffer size or at least for 20ms.
-            size_t sleepFrames = max(
-                    min(mPipeFramesP2, mRsmpInFramesP2) / 2, FMS_20 * mSampleRate / 1000);
-            if (framesRead <= (ssize_t) sleepFrames) {
+
+            // The audio fifo read() returns OVERRUN on overflow, and advances the read pointer
+            // to the full buffer point (clearing the overflow condition).  Upon OVERRUN error,
+            // we immediately retry the read() to get data and prevent another overflow.
+            for (int retries = 0; retries <= 2; ++retries) {
+                ALOGW_IF(retries > 0, "overrun on read from pipe, retry #%d", retries);
+                framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
+                        framesToRead);
+                if (framesRead != OVERRUN) break;
+            }
+
+            const ssize_t availableToRead = mPipeSource->availableToRead();
+            if (availableToRead >= 0) {
+                // PipeSource is the master clock.  It is up to the AudioRecord client to keep up.
+                LOG_ALWAYS_FATAL_IF((size_t)availableToRead > mPipeFramesP2,
+                        "more frames to read than fifo size, %zd > %zu",
+                        availableToRead, mPipeFramesP2);
+                const size_t pipeFramesFree = mPipeFramesP2 - availableToRead;
+                const size_t sleepFrames = min(pipeFramesFree, mRsmpInFramesP2) / 2;
+                ALOGVV("mPipeFramesP2:%zu mRsmpInFramesP2:%zu sleepFrames:%zu availableToRead:%zd",
+                        mPipeFramesP2, mRsmpInFramesP2, sleepFrames, availableToRead);
                 sleepUs = (sleepFrames * 1000000LL) / mSampleRate;
             }
             if (framesRead < 0) {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index aff1239..a7c4253 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1571,9 +1571,11 @@
     status_t status = NO_ERROR;
     static const int32_t kMaxTries = 5;
     int32_t tryCounter = kMaxTries;
+    const size_t originalFrameCount = buffer->mFrameCount;
     do {
         if (status == NOT_ENOUGH_DATA) {
             restartIfDisabled();
+            buffer->mFrameCount = originalFrameCount; // cleared on error, must be restored.
         }
         status = mProxy->obtainBuffer(buffer, timeOut);
     } while ((status == NOT_ENOUGH_DATA) && (tryCounter-- > 0));
@@ -1663,7 +1665,8 @@
         mFramesToDrop(0),
         mResamplerBufferProvider(NULL), // initialize in case of early constructor exit
         mRecordBufferConverter(NULL),
-        mFlags(flags)
+        mFlags(flags),
+        mSilenced(false)
 {
     if (mCblk == NULL) {
         return;
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index 65571f9..d29cae1 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -25,6 +25,7 @@
     libserviceutility \
     libaudiopolicymanager \
     libmedia_helper \
+    libmediametrics \
     libeffectsconfig
 
 LOCAL_STATIC_LIBRARIES := \
@@ -60,6 +61,7 @@
     audio_policy_criteria.conf \
 
 LOCAL_C_INCLUDES += frameworks/av/services/audiopolicy/engineconfigurable/include
+LOCAL_C_INCLUDES += frameworks/av/include
 
 LOCAL_SHARED_LIBRARIES += libaudiopolicyengineconfigurable
 
@@ -78,6 +80,7 @@
     libaudiopolicycomponents
 
 LOCAL_SHARED_LIBRARIES += libmedia_helper
+LOCAL_SHARED_LIBRARIES += libmediametrics
 
 ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1)
 LOCAL_SHARED_LIBRARIES += libicuuc libxml2
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 7f09e9b..923c091 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -69,8 +69,12 @@
         API_INPUT_CONCURRENCY_NONE = 0,
         API_INPUT_CONCURRENCY_CALL = (1 << 0),      // Concurrency with a call
         API_INPUT_CONCURRENCY_CAPTURE = (1 << 1),   // Concurrency with another capture
+        API_INPUT_CONCURRENCY_HOTWORD = (1 << 2),   // Concurrency with a hotword
+        API_INPUT_CONCURRENCY_PREEMPT = (1 << 3),   // pre-empted someone
+                // NB: preempt is marked on a successful return, others are on failing calls
+        API_INPUT_CONCURRENCY_LAST = (1 << 4),
 
-        API_INPUT_CONCURRENCY_ALL = (API_INPUT_CONCURRENCY_CALL | API_INPUT_CONCURRENCY_CAPTURE),
+        API_INPUT_CONCURRENCY_ALL = (API_INPUT_CONCURRENCY_LAST - 1),
     };
 
     typedef uint32_t concurrency_type__mask_t;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 22bc426..d1d7530 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -596,7 +596,7 @@
         sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
         audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/);
         if (state != AUDIO_MODE_IN_CALL || desc != mPrimaryOutput) {
-            setOutputDevice(desc, newDevice, (newDevice != AUDIO_DEVICE_NONE), delayMs);
+            setOutputDevice(desc, newDevice, (newDevice != AUDIO_DEVICE_NONE), 0 /*delayMs*/);
         }
     }
 
@@ -822,7 +822,7 @@
           "flags %#x",
           device, config->sample_rate, config->format, config->channel_mask, *flags);
 
-    *output = getOutputForDevice(device, session, *stream, *output, config, flags);
+    *output = getOutputForDevice(device, session, *stream, config, flags);
     if (*output == AUDIO_IO_HANDLE_NONE) {
         mOutputRoutes.removeRoute(session);
         return INVALID_OPERATION;
@@ -841,11 +841,10 @@
         audio_devices_t device,
         audio_session_t session,
         audio_stream_type_t stream,
-        audio_io_handle_t originalOutput,
         const audio_config_t *config,
         audio_output_flags_t *flags)
 {
-    audio_io_handle_t output = originalOutput;
+    audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
     status_t status;
 
     // open a direct output if required by specified parameters
@@ -909,22 +908,20 @@
     }
 
     if (profile != 0) {
-        // exclude MMAP streams
-        if ((*flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) == 0 || output != AUDIO_IO_HANDLE_NONE) {
-            for (size_t i = 0; i < mOutputs.size(); i++) {
-                sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
-                if (!desc->isDuplicated() && (profile == desc->mProfile)) {
-                    // reuse direct output if currently open by the same client
-                    // and configured with same parameters
-                    if ((config->sample_rate == desc->mSamplingRate) &&
-                        audio_formats_match(config->format, desc->mFormat) &&
-                        (config->channel_mask == desc->mChannelMask) &&
-                        (session == desc->mDirectClientSession)) {
-                        desc->mDirectOpenCount++;
-                        ALOGI("getOutputForDevice() reusing direct output %d for session %d",
-                              mOutputs.keyAt(i), session);
-                        return mOutputs.keyAt(i);
-                    }
+        // exclusive outputs for MMAP and Offload are enforced by different session ids.
+        for (size_t i = 0; i < mOutputs.size(); i++) {
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            if (!desc->isDuplicated() && (profile == desc->mProfile)) {
+                // reuse direct output if currently open by the same client
+                // and configured with same parameters
+                if ((config->sample_rate == desc->mSamplingRate) &&
+                    (config->format == desc->mFormat) &&
+                    (config->channel_mask == desc->mChannelMask) &&
+                    (session == desc->mDirectClientSession)) {
+                    desc->mDirectOpenCount++;
+                    ALOGI("getOutputForDevice() reusing direct output %d for session %d",
+                        mOutputs.keyAt(i), session);
+                    return mOutputs.keyAt(i);
                 }
             }
         }
@@ -945,8 +942,7 @@
         // only accept an output with the requested parameters
         if (status != NO_ERROR ||
             (config->sample_rate != 0 && config->sample_rate != outputDesc->mSamplingRate) ||
-            (config->format != AUDIO_FORMAT_DEFAULT &&
-                    !audio_formats_match(config->format, outputDesc->mFormat)) ||
+            (config->format != AUDIO_FORMAT_DEFAULT && config->format != outputDesc->mFormat) ||
             (config->channel_mask != 0 && config->channel_mask != outputDesc->mChannelMask)) {
             ALOGV("getOutputForDevice() failed opening direct output: output %d sample rate %d %d,"
                     "format %d %d, channel mask %04x %04x", output, config->sample_rate,
@@ -1035,7 +1031,7 @@
             // if a valid format is specified, skip output if not compatible
             if (format != AUDIO_FORMAT_INVALID) {
                 if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
-                    if (!audio_formats_match(format, outputDesc->mFormat)) {
+                    if (format != outputDesc->mFormat) {
                         continue;
                     }
                 } else if (!audio_is_linear_pcm(format)) {
@@ -1882,6 +1878,7 @@
         if (mCallTxPatch != 0 &&
             inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) {
             ALOGW("startInput(%d) failed: call in progress", input);
+            *concurrency |= API_INPUT_CONCURRENCY_CALL;
             return INVALID_OPERATION;
         }
 
@@ -1924,17 +1921,20 @@
                         ALOGW("startInput(%d) failed for HOTWORD: "
                                 "other input %d already started for HOTWORD",
                               input, activeDesc->mIoHandle);
+                        *concurrency |= API_INPUT_CONCURRENCY_HOTWORD;
                         return INVALID_OPERATION;
                     }
                 } else {
                     ALOGV("startInput(%d) failed for HOTWORD: other input %d already started",
                           input, activeDesc->mIoHandle);
+                    *concurrency |= API_INPUT_CONCURRENCY_CAPTURE;
                     return INVALID_OPERATION;
                 }
             } else {
                 if (activeSource != AUDIO_SOURCE_HOTWORD) {
                     ALOGW("startInput(%d) failed: other input %d already started",
                           input, activeDesc->mIoHandle);
+                    *concurrency |= API_INPUT_CONCURRENCY_CAPTURE;
                     return INVALID_OPERATION;
                 }
             }
@@ -1959,6 +1959,7 @@
                 audio_session_t activeSession = activeSessions.keyAt(0);
                 audio_io_handle_t activeHandle = activeDesc->mIoHandle;
                 SortedVector<audio_session_t> sessions = activeDesc->getPreemptedSessions();
+                *concurrency |= API_INPUT_CONCURRENCY_PREEMPT;
                 sessions.add(activeSession);
                 inputDesc->setPreemptedSessions(sessions);
                 stopInput(activeHandle, activeSession);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index d05ba1f..2b68882 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -628,7 +628,6 @@
                 audio_devices_t device,
                 audio_session_t session,
                 audio_stream_type_t stream,
-                audio_io_handle_t originalOutput,
                 const audio_config_t *config,
                 audio_output_flags_t *flags);
         // internal method to return the input handle for the given device and format
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index cf24c13..f6d407f 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -18,8 +18,11 @@
 //#define LOG_NDEBUG 0
 
 #include <utils/Log.h>
+#include <media/MediaAnalyticsItem.h>
+
 #include "AudioPolicyService.h"
 #include "ServiceUtilities.h"
+#include "TypeConverter.h"
 
 namespace android {
 
@@ -118,9 +121,11 @@
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
-    if (!settingsAllowed()) {
+
+    if (!modifyAudioRoutingAllowed()) {
         return PERMISSION_DENIED;
     }
+
     if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
         return BAD_VALUE;
     }
@@ -337,6 +342,13 @@
         return PERMISSION_DENIED;
     }
 
+    if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK ||
+        attr->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
+        attr->source == AUDIO_SOURCE_VOICE_CALL) &&
+        !captureAudioOutputAllowed(pid, uid)) {
+        return PERMISSION_DENIED;
+    }
+
     if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) {
         return BAD_VALUE;
     }
@@ -409,6 +421,35 @@
     return NO_ERROR;
 }
 
+// this is replicated from frameworks/av/media/libaudioclient/AudioRecord.cpp
+// XXX -- figure out how to put it into a common, shared location
+
+static std::string audioSourceString(audio_source_t value) {
+    std::string source;
+    if (SourceTypeConverter::toString(value, source)) {
+        return source;
+    }
+    char rawbuffer[16];  // room for "%d"
+    snprintf(rawbuffer, sizeof(rawbuffer), "%d", value);
+    return rawbuffer;
+}
+
+static std::string audioConcurrencyString(AudioPolicyInterface::concurrency_type__mask_t concurrency)
+{
+    char buffer[64]; // oversized
+    if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL) {
+        snprintf(buffer, sizeof(buffer), "%s%s%s%s",
+            (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CALL)? ",call":"",
+            (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE)? ",capture":"",
+            (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_HOTWORD)? ",hotword":"",
+            (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_PREEMPT)? ",preempt":"");
+    } else {
+        snprintf(buffer, sizeof(buffer), ",none");
+    }
+
+    return &buffer[1];
+}
+
 status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenced)
 {
     if (mAudioPolicyManager == NULL) {
@@ -444,6 +485,57 @@
         AutoCallerClear acc;
         status = mAudioPolicyManager->startInput(
                     client->input, client->session, *silenced, &concurrency);
+
+    }
+
+    // XXX log them all for a while, during some dogfooding.
+    if (1 || status != NO_ERROR) {
+
+        static constexpr char kAudioPolicy[] = "audiopolicy";
+
+        static constexpr char kAudioPolicyReason[] = "android.media.audiopolicy.reason";
+        static constexpr char kAudioPolicyStatus[] = "android.media.audiopolicy.status";
+        static constexpr char kAudioPolicyRqstSrc[] = "android.media.audiopolicy.rqst.src";
+        static constexpr char kAudioPolicyRqstPkg[] = "android.media.audiopolicy.rqst.pkg";
+        static constexpr char kAudioPolicyRqstSession[] = "android.media.audiopolicy.rqst.session";
+        static constexpr char kAudioPolicyActiveSrc[] = "android.media.audiopolicy.active.src";
+        static constexpr char kAudioPolicyActivePkg[] = "android.media.audiopolicy.active.pkg";
+        static constexpr char kAudioPolicyActiveSession[] = "android.media.audiopolicy.active.session";
+
+        MediaAnalyticsItem *item = new MediaAnalyticsItem(kAudioPolicy);
+        if (item != NULL) {
+
+            item->setCString(kAudioPolicyReason, audioConcurrencyString(concurrency).c_str());
+            item->setInt32(kAudioPolicyStatus, status);
+
+            item->setCString(kAudioPolicyRqstSrc, audioSourceString(client->attributes.source).c_str());
+            item->setCString(kAudioPolicyRqstPkg, std::string(String8(client->opPackageName).string()).c_str());
+            item->setInt32(kAudioPolicyRqstSession, client->session);
+
+            // figure out who is active
+            // NB: might the other party have given up the microphone since then? how sure.
+            // perhaps could have given up on it.
+            // we hold mLock, so perhaps we're safe for this looping
+            if (concurrency != AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE) {
+                int count = mAudioRecordClients.size();
+                for (int i = 0; i<count ; i++) {
+                    if (portId == mAudioRecordClients.keyAt(i)) {
+                        continue;
+                    }
+                    sp<AudioRecordClient> other = mAudioRecordClients.valueAt(i);
+                    if (other->active) {
+                        // keeps the last of the clients marked active
+                        item->setCString(kAudioPolicyActiveSrc,
+                                         audioSourceString(other->attributes.source).c_str());
+                        item->setCString(kAudioPolicyActivePkg, std::string(String8(other->opPackageName).string()).c_str());
+                        item->setInt32(kAudioPolicyActiveSession, other->session);
+                    }
+                }
+            }
+            item->selfrecord();
+            delete item;
+            item = NULL;
+        }
     }
 
     if (status == NO_ERROR) {
@@ -457,6 +549,8 @@
         if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE) {
             //TODO: check concurrent capture permission
         }
+
+        client->active = true;
     } else {
         finishRecording(client->opPackageName, client->uid);
     }
@@ -477,6 +571,8 @@
     }
     sp<AudioRecordClient> client = mAudioRecordClients.valueAt(index);
 
+    client->active = false;
+
     // finish the recording app op
     finishRecording(client->opPackageName, client->uid);
     AutoCallerClear acc;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 8d8bcab..4a58620 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -1215,6 +1215,35 @@
 
     fastInfo.maxJpegSize = getMaxSize(getAvailableJpegSizes());
 
+    isZslReprocessPresent = false;
+    camera_metadata_ro_entry_t availableCapabilities =
+        staticInfo(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+    if (0 < availableCapabilities.count) {
+        const uint8_t *caps = availableCapabilities.data.u8;
+        for (size_t i = 0; i < availableCapabilities.count; i++) {
+            if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING ==
+                caps[i]) {
+                isZslReprocessPresent = true;
+                break;
+            }
+        }
+    }
+    if (isZslReprocessPresent) {
+        Vector<StreamConfiguration> scs = getStreamConfigurations();
+        Size maxPrivInputSize = {0, 0};
+        for (const auto& sc : scs) {
+            if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT &&
+                    sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+                if (sc.width * sc.height > maxPrivInputSize.width * maxPrivInputSize.height) {
+                    maxPrivInputSize = {sc.width, sc.height};
+                }
+            }
+        }
+        fastInfo.maxZslSize = maxPrivInputSize;
+    } else {
+        fastInfo.maxZslSize = {0, 0};
+    }
+
     return OK;
 }
 
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index fe725fd..e35b7a4 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -242,6 +242,7 @@
         float minFocalLength;
         bool useFlexibleYuv;
         Size maxJpegSize;
+        Size maxZslSize;
     } fastInfo;
 
     // Quirks information; these are short-lived flags to enable workarounds for
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 372a2c5..37e1495 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -231,63 +231,9 @@
         return INVALID_OPERATION;
     }
 
-    if ((mZslStreamId != NO_STREAM) || (mInputStreamId != NO_STREAM)) {
-        // Check if stream parameters have to change
-        CameraDeviceBase::StreamInfo streamInfo;
-        res = device->getStreamInfo(mZslStreamId, &streamInfo);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Error querying capture output stream info: "
-                    "%s (%d)", __FUNCTION__,
-                    client->getCameraId(), strerror(-res), res);
-            return res;
-        }
-        if (streamInfo.width != (uint32_t)params.fastInfo.arrayWidth ||
-                streamInfo.height != (uint32_t)params.fastInfo.arrayHeight) {
-            if (mZslStreamId != NO_STREAM) {
-                ALOGV("%s: Camera %d: Deleting stream %d since the buffer "
-                      "dimensions changed",
-                    __FUNCTION__, client->getCameraId(), mZslStreamId);
-                res = device->deleteStream(mZslStreamId);
-                if (res == -EBUSY) {
-                    ALOGV("%s: Camera %d: Device is busy, call updateStream again "
-                          " after it becomes idle", __FUNCTION__, mId);
-                    return res;
-                } else if(res != OK) {
-                    ALOGE("%s: Camera %d: Unable to delete old output stream "
-                            "for ZSL: %s (%d)", __FUNCTION__,
-                            client->getCameraId(), strerror(-res), res);
-                    return res;
-                }
-                mZslStreamId = NO_STREAM;
-            }
-
-            if (mInputStreamId != NO_STREAM) {
-                ALOGV("%s: Camera %d: Deleting stream %d since the buffer "
-                      "dimensions changed",
-                    __FUNCTION__, client->getCameraId(), mInputStreamId);
-                res = device->deleteStream(mInputStreamId);
-                if (res == -EBUSY) {
-                    ALOGV("%s: Camera %d: Device is busy, call updateStream again "
-                          " after it becomes idle", __FUNCTION__, mId);
-                    return res;
-                } else if(res != OK) {
-                    ALOGE("%s: Camera %d: Unable to delete old output stream "
-                            "for ZSL: %s (%d)", __FUNCTION__,
-                            client->getCameraId(), strerror(-res), res);
-                    return res;
-                }
-                mInputStreamId = NO_STREAM;
-            }
-            if (nullptr != mInputProducer.get()) {
-                mInputProducer->disconnect(NATIVE_WINDOW_API_CPU);
-                mInputProducer.clear();
-            }
-        }
-    }
-
     if (mInputStreamId == NO_STREAM) {
-        res = device->createInputStream(params.fastInfo.arrayWidth,
-            params.fastInfo.arrayHeight, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+        res = device->createInputStream(params.fastInfo.maxZslSize.width,
+            params.fastInfo.maxZslSize.height, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
             &mInputStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create input stream: "
@@ -309,8 +255,8 @@
         mProducer->setName(String8("Camera2-ZslRingBufferConsumer"));
         sp<Surface> outSurface = new Surface(producer);
 
-        res = device->createStream(outSurface, params.fastInfo.arrayWidth,
-            params.fastInfo.arrayHeight, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+        res = device->createStream(outSurface, params.fastInfo.maxZslSize.width,
+            params.fastInfo.maxZslSize.height, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
             HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mZslStreamId,
             String8());
         if (res != OK) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 67b5e06..6b958a8 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -4325,9 +4325,9 @@
     uint32_t numRequestProcessed = 0;
     for (size_t i = 0; i < batchSize; i++) {
         requests[i] = &mNextRequests.editItemAt(i).halRequest;
+        ATRACE_ASYNC_BEGIN("frame capture", mNextRequests[i].halRequest.frame_number);
     }
 
-    ATRACE_ASYNC_BEGIN("batch frame capture", mNextRequests[0].halRequest.frame_number);
     res = mInterface->processBatchCaptureRequests(requests, &numRequestProcessed);
 
     bool triggerRemoveFailed = false;
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 6d84a42..4b05395 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -481,6 +481,7 @@
 
 static std::string allowedKeys[] =
 {
+    "audiopolicy",
     "audiorecord",
     "audiotrack",
     "codec",
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 6015b28..253f290 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -49,9 +49,9 @@
 
     virtual aaudio_result_t close() = 0;
 
-    virtual aaudio_result_t registerStream(android::sp<AAudioServiceStreamBase> stream);
+    aaudio_result_t registerStream(android::sp<AAudioServiceStreamBase> stream);
 
-    virtual aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamBase> stream);
+    aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamBase> stream);
 
     virtual aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
                                         audio_port_handle_t *clientHandle) = 0;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index c943008..48d8002 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -105,6 +105,9 @@
             goto error;
         }
 
+        // This is not protected by a lock because the stream cannot be
+        // referenced until the service returns a handle to the client.
+        // So only one thread can open a stream.
         mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService,
                                                          request,
                                                          sharingMode);
@@ -113,6 +116,9 @@
             result = AAUDIO_ERROR_UNAVAILABLE;
             goto error;
         }
+        // Save a weak pointer that we will use to access the endpoint.
+        mServiceEndpointWeak = mServiceEndpoint;
+
         mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
         copyFrom(*mServiceEndpoint);
     }
@@ -131,13 +137,16 @@
 
     stop();
 
-    if (mServiceEndpoint == nullptr) {
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
         result = AAUDIO_ERROR_INVALID_STATE;
     } else {
-        mServiceEndpoint->unregisterStream(this);
-        AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
-        mEndpointManager.closeEndpoint(mServiceEndpoint);
-        mServiceEndpoint.clear();
+        endpoint->unregisterStream(this);
+        AAudioEndpointManager &endpointManager = AAudioEndpointManager::getInstance();
+        endpointManager.closeEndpoint(endpoint);
+
+        // AAudioService::closeStream() prevents two threads from closing at the same time.
+        mServiceEndpoint.clear(); // endpoint will hold the pointer until this method returns.
     }
 
     {
@@ -153,7 +162,12 @@
 
 aaudio_result_t AAudioServiceStreamBase::startDevice() {
     mClientHandle = AUDIO_PORT_HANDLE_NONE;
-    return mServiceEndpoint->startStream(this, &mClientHandle);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    return endpoint->startStream(this, &mClientHandle);
 }
 
 /**
@@ -163,16 +177,11 @@
  */
 aaudio_result_t AAudioServiceStreamBase::start() {
     aaudio_result_t result = AAUDIO_OK;
+
     if (isRunning()) {
         return AAUDIO_OK;
     }
 
-    if (mServiceEndpoint == nullptr) {
-        ALOGE("%s() missing endpoint", __func__);
-        result = AAUDIO_ERROR_INVALID_STATE;
-        goto error;
-    }
-
     setFlowing(false);
 
     // Start with fresh presentation timestamps.
@@ -201,10 +210,6 @@
     if (!isRunning()) {
         return result;
     }
-    if (mServiceEndpoint == nullptr) {
-        ALOGE("%s() missing endpoint", __func__);
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
 
     // Send it now because the timestamp gets rounded up when stopStream() is called below.
     // Also we don't need the timestamps while we are shutting down.
@@ -216,7 +221,12 @@
         return result;
     }
 
-    result = mServiceEndpoint->stopStream(this, mClientHandle);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    result = endpoint->stopStream(this, mClientHandle);
     if (result != AAUDIO_OK) {
         ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText());
         disconnect(); // TODO should we return or pause Base first?
@@ -233,11 +243,6 @@
         return result;
     }
 
-    if (mServiceEndpoint == nullptr) {
-        ALOGE("%s() missing endpoint", __func__);
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-
     setState(AAUDIO_STREAM_STATE_STOPPING);
 
     // Send it now because the timestamp gets rounded up when stopStream() is called below.
@@ -249,10 +254,15 @@
         return result;
     }
 
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
     // TODO wait for data to be played out
-    result = mServiceEndpoint->stopStream(this, mClientHandle);
+    result = endpoint->stopStream(this, mClientHandle);
     if (result != AAUDIO_OK) {
-        ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText());
+        ALOGE("%s() stopStream returned %d, %s", __func__, result, getTypeText());
         disconnect();
         // TODO what to do with result here?
     }
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index d8102be..0ad015e 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -279,7 +279,12 @@
     SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
 
     android::AAudioService &mAudioService;
+
+    // The mServiceEndpoint variable can be accessed by multiple threads.
+    // So we access it by locally promoting a weak pointer to a smart pointer,
+    // which is thread-safe.
     android::sp<AAudioServiceEndpoint> mServiceEndpoint;
+    android::wp<AAudioServiceEndpoint> mServiceEndpointWeak;
 
 private:
     aaudio_handle_t         mHandle = -1;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 34ddb4b..c845309 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -70,14 +70,19 @@
         return result;
     }
 
-    result = mServiceEndpoint->registerStream(keep);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
+    result = endpoint->registerStream(keep);
     if (result != AAUDIO_OK) {
-        goto error;
+        return result;
     }
 
     setState(AAUDIO_STREAM_STATE_OPEN);
 
-error:
     return AAUDIO_OK;
 }
 
@@ -118,21 +123,37 @@
 
 aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
                                                        audio_port_handle_t *clientHandle) {
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
     // Start the client on behalf of the application. Generate a new porthandle.
-    aaudio_result_t result = mServiceEndpoint->startClient(client, clientHandle);
+    aaudio_result_t result = endpoint->startClient(client, clientHandle);
     return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
-    aaudio_result_t result = mServiceEndpoint->stopClient(clientHandle);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    aaudio_result_t result = endpoint->stopClient(clientHandle);
     return result;
 }
 
 // Get free-running DSP or DMA hardware position from the HAL.
 aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
                                                                   int64_t *timeNanos) {
-    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
-            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
+            static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
+
     aaudio_result_t result = serviceEndpointMMAP->getFreeRunningPosition(positionFrames, timeNanos);
     if (result == AAUDIO_OK) {
         Timestamp timestamp(*positionFrames, *timeNanos);
@@ -148,8 +169,15 @@
 // Get timestamp that was written by getFreeRunningPosition()
 aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
-    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
-            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
+            static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
+
     // TODO Get presentation timestamp from the HAL
     if (mAtomicTimestamp.isValid()) {
         Timestamp timestamp = mAtomicTimestamp.read();
@@ -165,7 +193,12 @@
 aaudio_result_t AAudioServiceStreamMMAP::getAudioDataDescription(
         AudioEndpointParcelable &parcelable)
 {
-    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
-            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
+            static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
     return serviceEndpointMMAP->getDownDataDescription(parcelable);
 }
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 864a008..05c5735 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -128,6 +128,12 @@
 
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
 
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        result = AAUDIO_ERROR_INVALID_STATE;
+        goto error;
+    }
+
     // Is the request compatible with the shared endpoint?
     setFormat(configurationInput.getFormat());
     if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
@@ -140,20 +146,20 @@
 
     setSampleRate(configurationInput.getSampleRate());
     if (getSampleRate() == AAUDIO_UNSPECIFIED) {
-        setSampleRate(mServiceEndpoint->getSampleRate());
-    } else if (getSampleRate() != mServiceEndpoint->getSampleRate()) {
+        setSampleRate(endpoint->getSampleRate());
+    } else if (getSampleRate() != endpoint->getSampleRate()) {
         ALOGD("%s() mSampleRate = %d, need %d",
-              __func__, getSampleRate(), mServiceEndpoint->getSampleRate());
+              __func__, getSampleRate(), endpoint->getSampleRate());
         result = AAUDIO_ERROR_INVALID_RATE;
         goto error;
     }
 
     setSamplesPerFrame(configurationInput.getSamplesPerFrame());
     if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
-        setSamplesPerFrame(mServiceEndpoint->getSamplesPerFrame());
-    } else if (getSamplesPerFrame() != mServiceEndpoint->getSamplesPerFrame()) {
+        setSamplesPerFrame(endpoint->getSamplesPerFrame());
+    } else if (getSamplesPerFrame() != endpoint->getSamplesPerFrame()) {
         ALOGD("%s() mSamplesPerFrame = %d, need %d",
-              __func__, getSamplesPerFrame(), mServiceEndpoint->getSamplesPerFrame());
+              __func__, getSamplesPerFrame(), endpoint->getSamplesPerFrame());
         result = AAUDIO_ERROR_OUT_OF_RANGE;
         goto error;
     }
@@ -179,7 +185,10 @@
         }
     }
 
-    result = mServiceEndpoint->registerStream(keep);
+    ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d",
+          getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId());
+
+    result = endpoint->registerStream(keep);
     if (result != AAUDIO_OK) {
         goto error;
     }
@@ -246,7 +255,13 @@
                                                                 int64_t *timeNanos) {
 
     int64_t position = 0;
-    aaudio_result_t result = mServiceEndpoint->getTimestamp(&position, timeNanos);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
+    aaudio_result_t result = endpoint->getTimestamp(&position, timeNanos);
     if (result == AAUDIO_OK) {
         int64_t offset = mTimestampPositionOffset.load();
         // TODO, do not go below starting value