Merge "Enable support for audio HAL V7 in the framework"
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 07176cf..6c1cf33 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -45,8 +45,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= 24
-
 /**
  * ACameraCaptureSession is an opaque type that manages frame captures of a camera device.
  *
@@ -593,10 +591,6 @@
 camera_status_t ACameraCaptureSession_abortCaptures(ACameraCaptureSession* session)
         __INTRODUCED_IN(24);
 
-#endif /* __ANDROID_API__ >= 24 */
-
-#if __ANDROID_API__ >= 28
-
 typedef struct ACaptureSessionOutput ACaptureSessionOutput;
 
 /**
@@ -641,9 +635,7 @@
  */
 camera_status_t ACameraCaptureSession_updateSharedOutput(ACameraCaptureSession* session,
         ACaptureSessionOutput* output) __INTRODUCED_IN(28);
-#endif /* __ANDROID_API__ >= 28 */
 
-#if __ANDROID_API__ >= 29
 /**
  * The definition of final capture result callback with logical multi-camera support.
  *
@@ -788,8 +780,6 @@
         int numRequests, ACaptureRequest** requests,
         /*optional*/int* captureSequenceId) __INTRODUCED_IN(29);
 
-#endif /* __ANDROID_API__ >= 29 */
-
 __END_DECLS
 
 #endif /* _NDK_CAMERA_CAPTURE_SESSION_H */
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index 1537bde..f72fe8d 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -44,8 +44,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= 24
-
 /**
  * ACameraDevice is opaque type that provides access to a camera device.
  *
@@ -687,10 +685,6 @@
         const ACameraCaptureSession_stateCallbacks* callbacks,
         /*out*/ACameraCaptureSession** session) __INTRODUCED_IN(24);
 
-#endif /* __ANDROID_API__ >= 24 */
-
-#if __ANDROID_API__ >= 28
-
 /**
  * Create a shared ACaptureSessionOutput object.
  *
@@ -782,10 +776,6 @@
         const ACameraCaptureSession_stateCallbacks* callbacks,
         /*out*/ACameraCaptureSession** session) __INTRODUCED_IN(28);
 
-#endif /* __ANDROID_API__ >= 28 */
-
-#if __ANDROID_API__ >= 29
-
 /**
  * Create a ACaptureSessionOutput object used for streaming from a physical
  * camera as part of a logical camera device.
@@ -890,8 +880,6 @@
         const ACameraDevice* device,
         const ACaptureSessionOutputContainer* sessionOutputContainer) __INTRODUCED_IN(29);
 
-#endif /* __ANDROID_API__ >= 29 */
-
 __END_DECLS
 
 #endif /* _NDK_CAMERA_DEVICE_H */
diff --git a/camera/ndk/include/camera/NdkCameraError.h b/camera/ndk/include/camera/NdkCameraError.h
index fc618ee..9d77eb4 100644
--- a/camera/ndk/include/camera/NdkCameraError.h
+++ b/camera/ndk/include/camera/NdkCameraError.h
@@ -40,8 +40,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= 24
-
 typedef enum {
     ACAMERA_OK = 0,
 
@@ -138,8 +136,6 @@
     ACAMERA_ERROR_UNSUPPORTED_OPERATION = ACAMERA_ERROR_BASE - 14,
 } camera_status_t;
 
-#endif /* __ANDROID_API__ >= 24 */
-
 __END_DECLS
 
 #endif /* _NDK_CAMERA_ERROR_H */
diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h
index 0a2ee57..be32b11 100644
--- a/camera/ndk/include/camera/NdkCameraManager.h
+++ b/camera/ndk/include/camera/NdkCameraManager.h
@@ -44,8 +44,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= 24
-
 /**
  * ACameraManager is opaque type that provides access to camera service.
  *
@@ -293,10 +291,6 @@
         ACameraDevice_StateCallbacks* callback,
         /*out*/ACameraDevice** device) __INTRODUCED_IN(24);
 
-#endif /* __ANDROID_API__ >= 24 */
-
-#if __ANDROID_API__ >= 29
-
 /**
  * Definition of camera access permission change callback.
  *
@@ -419,8 +413,6 @@
         __INTRODUCED_IN(29);
 #endif
 
-#endif /* __ANDROID_API__ >= 29 */
-
 __END_DECLS
 
 #endif /* _NDK_CAMERA_MANAGER_H */
diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h
index a840bd1..0d5e6c4 100644
--- a/camera/ndk/include/camera/NdkCameraMetadata.h
+++ b/camera/ndk/include/camera/NdkCameraMetadata.h
@@ -41,9 +41,7 @@
 #include <sys/cdefs.h>
 
 #ifndef __ANDROID_VNDK__
-#if __ANDROID_API__ >= 30
 #include "jni.h"
-#endif  /* __ANDROID_API__ >= 30 */
 #endif  /* __ANDROID_VNDK__ */
 
 #include "NdkCameraError.h"
@@ -51,8 +49,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= 24
-
 /**
  * ACameraMetadata is opaque type that provides access to read-only camera metadata like camera
  * characteristics (via {@link ACameraManager_getCameraCharacteristics}) or capture results (via
@@ -238,10 +234,6 @@
  */
 void ACameraMetadata_free(ACameraMetadata* metadata) __INTRODUCED_IN(24);
 
-#endif /* __ANDROID_API__ >= 24 */
-
-#if __ANDROID_API__ >= 29
-
 /**
  * Helper function to check if a camera is logical multi-camera.
  *
@@ -260,10 +252,7 @@
         /*out*/size_t* numPhysicalCameras, /*out*/const char* const** physicalCameraIds)
         __INTRODUCED_IN(29);
 
-#endif /* __ANDROID_API__ >= 29 */
-
 #ifndef __ANDROID_VNDK__
-#if __ANDROID_API__ >= 30
 
 /**
  * Return a {@link ACameraMetadata} that references the same data as
@@ -290,7 +279,6 @@
 ACameraMetadata* ACameraMetadata_fromCameraMetadata(JNIEnv* env, jobject cameraMetadata)
         __INTRODUCED_IN(30);
 
-#endif /* __ANDROID_API__ >= 30 */
 #endif  /* __ANDROID_VNDK__ */
 
 __END_DECLS
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 1354fce..af12354 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -40,8 +40,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= 24
-
 typedef enum acamera_metadata_section {
     ACAMERA_COLOR_CORRECTION,
     ACAMERA_CONTROL,
@@ -8993,8 +8991,6 @@
 
 
 
-#endif /* __ANDROID_API__ >= 24 */
-
 __END_DECLS
 
 #endif /* _NDK_CAMERA_METADATA_TAGS_H */
diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h
index d3f8826..a4dc374 100644
--- a/camera/ndk/include/camera/NdkCaptureRequest.h
+++ b/camera/ndk/include/camera/NdkCaptureRequest.h
@@ -44,8 +44,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= 24
-
 // Container for output targets
 typedef struct ACameraOutputTargets ACameraOutputTargets;
 
@@ -304,10 +302,6 @@
  */
 void ACaptureRequest_free(ACaptureRequest* request) __INTRODUCED_IN(24);
 
-#endif /* __ANDROID_API__ >= 24 */
-
-#if __ANDROID_API__ >= 28
-
 /**
  * Associate an arbitrary user context pointer to the {@link ACaptureRequest}
  *
@@ -356,10 +350,6 @@
  */
 ACaptureRequest* ACaptureRequest_copy(const ACaptureRequest* src) __INTRODUCED_IN(28);
 
-#endif /* __ANDROID_API__ >= 28 */
-
-#if __ANDROID_API__ >= 29
-
 /**
  * Get a metadata entry from input {@link ACaptureRequest} for
  * a physical camera backing a logical multi-camera device.
@@ -569,8 +559,6 @@
         ACaptureRequest* request, const char* physicalId, uint32_t tag,
         uint32_t count, const ACameraMetadata_rational* data) __INTRODUCED_IN(29);
 
-#endif /* __ANDROID_API__ >= 29 */
-
 __END_DECLS
 
 #endif /* _NDK_CAPTURE_REQUEST_H */
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index 6111fea..1d2562e 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -39,6 +39,8 @@
 
     static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
     static constexpr size_t kMinBufferCountForEviction = 25;
+    static constexpr size_t kMaxUnusedBufferCount = 64;
+    static constexpr size_t kUnusedBufferCountTarget = kMaxUnusedBufferCount - 16;
 
     static constexpr nsecs_t kEvictGranularityNs = 1000000000; // 1 sec
     static constexpr nsecs_t kEvictDurationNs = 5000000000; // 5 secs
@@ -724,9 +726,11 @@
 }
 
 void Accessor::Impl::BufferPool::cleanUp(bool clearCache) {
-    if (clearCache || mTimestampUs > mLastCleanUpUs + kCleanUpDurationUs) {
+    if (clearCache || mTimestampUs > mLastCleanUpUs + kCleanUpDurationUs ||
+            mStats.buffersNotInUse() > kMaxUnusedBufferCount) {
         mLastCleanUpUs = mTimestampUs;
-        if (mTimestampUs > mLastLogUs + kLogDurationUs) {
+        if (mTimestampUs > mLastLogUs + kLogDurationUs ||
+                mStats.buffersNotInUse() > kMaxUnusedBufferCount) {
             mLastLogUs = mTimestampUs;
             ALOGD("bufferpool2 %p : %zu(%zu size) total buffers - "
                   "%zu(%zu size) used buffers - %zu/%zu (recycle/alloc) - "
@@ -737,8 +741,9 @@
                   mStats.mTotalFetches, mStats.mTotalTransfers);
         }
         for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
-            if (!clearCache && (mStats.mSizeCached < kMinAllocBytesForEviction
-                    || mBuffers.size() < kMinBufferCountForEviction)) {
+            if (!clearCache && mStats.buffersNotInUse() <= kUnusedBufferCountTarget &&
+                    (mStats.mSizeCached < kMinAllocBytesForEviction ||
+                     mBuffers.size() < kMinBufferCountForEviction)) {
                 break;
             }
             auto it = mBuffers.find(*freeIt);
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
index cd1b4d0..3d39941 100644
--- a/media/bufferpool/2.0/AccessorImpl.h
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -193,6 +193,12 @@
                 : mSizeCached(0), mBuffersCached(0), mSizeInUse(0), mBuffersInUse(0),
                   mTotalAllocations(0), mTotalRecycles(0), mTotalTransfers(0), mTotalFetches(0) {}
 
+            /// # of currently unused buffers
+            size_t buffersNotInUse() const {
+                ALOG_ASSERT(mBuffersCached >= mBuffersInUse);
+                return mBuffersCached - mBuffersInUse;
+            }
+
             /// A new buffer is allocated on an allocation request.
             void onBufferAllocated(size_t allocSize) {
                 mSizeCached += allocSize;
diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp
index 342fef6..9308b81 100644
--- a/media/bufferpool/2.0/BufferPoolClient.cpp
+++ b/media/bufferpool/2.0/BufferPoolClient.cpp
@@ -32,6 +32,8 @@
 static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms
 static constexpr int kPostMaxRetry = 3;
 static constexpr int kCacheTtlUs = 1000000; // TODO: tune
+static constexpr size_t kMaxCachedBufferCount = 64;
+static constexpr size_t kCachedBufferCountTarget = kMaxCachedBufferCount - 16;
 
 class BufferPoolClient::Impl
         : public std::enable_shared_from_this<BufferPoolClient::Impl> {
@@ -136,6 +138,10 @@
             --mActive;
             mLastChangeUs = getTimestampNow();
         }
+
+        int cachedBufferCount() const {
+            return mBuffers.size() - mActive;
+        }
     } mCache;
 
     // FMQ - release notifier
@@ -668,10 +674,12 @@
 // should have mCache.mLock
 void BufferPoolClient::Impl::evictCaches(bool clearCache) {
     int64_t now = getTimestampNow();
-    if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) {
+    if (now >= mLastEvictCacheUs + kCacheTtlUs ||
+            clearCache || mCache.cachedBufferCount() > kMaxCachedBufferCount) {
         size_t evicted = 0;
         for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
-            if (!it->second->hasCache() && (it->second->expire() || clearCache)) {
+            if (!it->second->hasCache() && (it->second->expire() ||
+                        clearCache || mCache.cachedBufferCount() > kCachedBufferCountTarget)) {
                 it = mCache.mBuffers.erase(it);
                 ++evicted;
             } else {
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index 82c061a..b1cf388 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -30,6 +30,7 @@
 
 namespace android {
 constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+constexpr size_t kMaxDimension = 1920;
 constexpr char COMPONENT_NAME[] = "c2.android.mpeg2.decoder";
 
 class C2SoftMpeg2Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -64,8 +65,8 @@
                 DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
                 .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
                 .withFields({
-                    C2F(mSize, width).inRange(16, 1920, 4),
-                    C2F(mSize, height).inRange(16, 1088, 4),
+                    C2F(mSize, width).inRange(16, kMaxDimension, 2),
+                    C2F(mSize, height).inRange(16, kMaxDimension, 2),
                 })
                 .withSetter(SizeSetter)
                 .build());
@@ -91,8 +92,8 @@
                 DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
                 .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
                 .withFields({
-                    C2F(mSize, width).inRange(2, 1920, 2),
-                    C2F(mSize, height).inRange(2, 1088, 2),
+                    C2F(mSize, width).inRange(2, kMaxDimension, 2),
+                    C2F(mSize, height).inRange(2, kMaxDimension, 2),
                 })
                 .withSetter(MaxPictureSizeSetter, mSize)
                 .build());
@@ -204,8 +205,8 @@
                                     const C2P<C2StreamPictureSizeInfo::output> &size) {
         (void)mayBlock;
         // TODO: get max width/height from the size's field helpers vs. hardcoding
-        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 1920u);
-        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 1088u);
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), kMaxDimension);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), kMaxDimension);
         return C2R::Ok();
     }
 
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
index a7cc037..ddd312f 100644
--- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
@@ -35,8 +35,10 @@
 namespace android {
 constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
 #ifdef MPEG4
+constexpr size_t kMaxDimension = 1920;
 constexpr char COMPONENT_NAME[] = "c2.android.mpeg4.decoder";
 #else
+constexpr size_t kMaxDimension = 352;
 constexpr char COMPONENT_NAME[] = "c2.android.h263.decoder";
 #endif
 
@@ -75,13 +77,8 @@
                 DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
                 .withDefault(new C2StreamPictureSizeInfo::output(0u, 176, 144))
                 .withFields({
-#ifdef MPEG4
-                    C2F(mSize, width).inRange(2, 1920, 2),
-                    C2F(mSize, height).inRange(2, 1088, 2),
-#else
-                    C2F(mSize, width).inRange(2, 352, 2),
-                    C2F(mSize, height).inRange(2, 288, 2),
-#endif
+                    C2F(mSize, width).inRange(2, kMaxDimension, 2),
+                    C2F(mSize, height).inRange(2, kMaxDimension, 2),
                 })
                 .withSetter(SizeSetter)
                 .build());
@@ -130,19 +127,10 @@
 
         addParameter(
                 DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
-#ifdef MPEG4
-                .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 1920, 1088))
-#else
                 .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 352, 288))
-#endif
                 .withFields({
-#ifdef MPEG4
-                    C2F(mSize, width).inRange(2, 1920, 2),
-                    C2F(mSize, height).inRange(2, 1088, 2),
-#else
-                    C2F(mSize, width).inRange(2, 352, 2),
-                    C2F(mSize, height).inRange(2, 288, 2),
-#endif
+                    C2F(mSize, width).inRange(2, kMaxDimension, 2),
+                    C2F(mSize, height).inRange(2, kMaxDimension, 2),
                 })
                 .withSetter(MaxPictureSizeSetter, mSize)
                 .build());
@@ -200,13 +188,8 @@
                                     const C2P<C2StreamPictureSizeInfo::output> &size) {
         (void)mayBlock;
         // TODO: get max width/height from the size's field helpers vs. hardcoding
-#ifdef MPEG4
-        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 1920u);
-        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 1088u);
-#else
-        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 352u);
-        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 288u);
-#endif
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), kMaxDimension);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), kMaxDimension);
         return C2R::Ok();
     }
 
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 38f7389..752140a 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -151,6 +151,7 @@
 
     /* protected content */
     kParamIndexSecureMode,
+    kParamIndexEncryptedBuffer, // info-buffer, used with SM_READ_PROTECTED_WITH_ENCRYPTED
 
     // deprecated
     kParamIndexDelayRequest = kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG,
@@ -221,6 +222,7 @@
     kParamIndexDrcEffectType, // drc, enum
     kParamIndexDrcOutputLoudness, // drc, float (dBFS)
     kParamIndexDrcAlbumMode, // drc, enum
+    kParamIndexAudioFrameSize, // int
 
     /* ============================== platform-defined parameters ============================== */
 
@@ -1144,6 +1146,8 @@
 C2ENUM(C2Config::secure_mode_t, uint32_t,
     SM_UNPROTECTED,    ///< no content protection
     SM_READ_PROTECTED, ///< input and output buffers shall be protected from reading
+    /// both read protected and readable encrypted buffers are used
+    SM_READ_PROTECTED_WITH_ENCRYPTED,
 )
 
 typedef C2GlobalParam<C2Tuning, C2SimpleValueStruct<C2Config::secure_mode_t>, kParamIndexSecureMode>
@@ -1969,9 +1973,20 @@
 /**
  * DRC output loudness in dBFS. Retrieved during decoding
  */
- typedef C2StreamParam<C2Info, C2FloatValue, kParamIndexDrcOutputLoudness>
+typedef C2StreamParam<C2Info, C2FloatValue, kParamIndexDrcOutputLoudness>
         C2StreamDrcOutputLoudnessTuning;
- constexpr char C2_PARAMKEY_DRC_OUTPUT_LOUDNESS[] = "output.drc.output-loudness";
+constexpr char C2_PARAMKEY_DRC_OUTPUT_LOUDNESS[] = "output.drc.output-loudness";
+
+/**
+ * Audio frame size in samples.
+ *
+ * Audio encoders can expose this parameter to signal the desired audio frame
+ * size that corresponds to a single coded access unit.
+ * Default value is 0, meaning that the encoder accepts input buffers of any size.
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexAudioFrameSize>
+        C2StreamAudioFrameSizeInfo;
+constexpr char C2_PARAMKEY_AUDIO_FRAME_SIZE[] = "raw.audio-frame-size";
 
 /* --------------------------------------- AAC components --------------------------------------- */
 
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index 1f0c856..72f7c43 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -895,13 +895,12 @@
         BufferPoolSender* bufferPoolSender,
         std::list<BaseBlock>* baseBlocks,
         std::map<const void*, uint32_t>* baseBlockIndices) {
-    // TODO: C2InfoBuffer is not implemented.
-    (void)d;
-    (void)s;
-    (void)bufferPoolSender;
-    (void)baseBlocks;
-    (void)baseBlockIndices;
-    LOG(INFO) << "InfoBuffer not implemented.";
+    d->index = static_cast<ParamIndex>(s.index());
+    Buffer& dBuffer = d->buffer;
+    if (!objcpy(&dBuffer, s.data(), bufferPoolSender, baseBlocks, baseBlockIndices)) {
+        LOG(ERROR) << "Invalid C2InfoBuffer::data";
+        return false;
+    }
     return true;
 }
 
@@ -1336,6 +1335,68 @@
     return true;
 }
 
+// InfoBuffer -> C2InfoBuffer
+bool objcpy(std::vector<C2InfoBuffer> *d, const InfoBuffer& s,
+        const std::vector<C2BaseBlock>& baseBlocks) {
+
+    // Currently, a non-null C2InfoBufer must contain exactly 1 block.
+    if (s.buffer.blocks.size() == 0) {
+        return true;
+    } else if (s.buffer.blocks.size() != 1) {
+        LOG(ERROR) << "Invalid InfoBuffer::Buffer "
+                      "Currently, a C2InfoBuffer must contain exactly 1 block.";
+        return false;
+    }
+
+    const Block &sBlock = s.buffer.blocks[0];
+    if (sBlock.index >= baseBlocks.size()) {
+        LOG(ERROR) << "Invalid InfoBuffer::Buffer::blocks[0].index: "
+                      "Array index out of range.";
+        return false;
+    }
+    const C2BaseBlock &baseBlock = baseBlocks[sBlock.index];
+
+    // Parse meta.
+    std::vector<C2Param*> sBlockMeta;
+    if (!parseParamsBlob(&sBlockMeta, sBlock.meta)) {
+        LOG(ERROR) << "Invalid InfoBuffer::Buffer::blocks[0].meta.";
+        return false;
+    }
+
+    // Copy fence.
+    C2Fence dFence;
+    if (!objcpy(&dFence, sBlock.fence)) {
+        LOG(ERROR) << "Invalid InfoBuffer::Buffer::blocks[0].fence.";
+        return false;
+    }
+
+    // Construct a block.
+    switch (baseBlock.type) {
+    case C2BaseBlock::LINEAR:
+        if (sBlockMeta.size() == 1 && sBlockMeta[0] != nullptr &&
+            sBlockMeta[0]->size() == sizeof(C2Hidl_RangeInfo)) {
+            C2Hidl_RangeInfo *rangeInfo =
+                    reinterpret_cast<C2Hidl_RangeInfo*>(sBlockMeta[0]);
+            d->emplace_back(C2InfoBuffer::CreateLinearBuffer(
+                    s.index,
+                    baseBlock.linear->share(
+                            rangeInfo->offset, rangeInfo->length, dFence)));
+            return true;
+        }
+        LOG(ERROR) << "Invalid Meta for C2BaseBlock::Linear InfoBuffer.";
+        break;
+    case C2BaseBlock::GRAPHIC:
+        // It's not used now
+        LOG(ERROR) << "Non-Used C2BaseBlock::type for InfoBuffer.";
+        break;
+    default:
+        LOG(ERROR) << "Invalid C2BaseBlock::type for InfoBuffer.";
+        break;
+    }
+
+    return false;
+}
+
 // FrameData -> C2FrameData
 bool objcpy(C2FrameData* d, const FrameData& s,
         const std::vector<C2BaseBlock>& baseBlocks) {
@@ -1370,8 +1431,18 @@
         }
     }
 
-    // TODO: Implement this once C2InfoBuffer has constructors.
     d->infoBuffers.clear();
+    if (s.infoBuffers.size() == 0) {
+        // InfoBuffer is optional
+        return true;
+    }
+    d->infoBuffers.reserve(s.infoBuffers.size());
+    for (const InfoBuffer &sInfoBuffer: s.infoBuffers) {
+        if (!objcpy(&(d->infoBuffers), sInfoBuffer, baseBlocks)) {
+            LOG(ERROR) << "Invalid Framedata::infoBuffers.";
+            return false;
+        }
+    }
     return true;
 }
 
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 94034b5..c3cfcce 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -11,6 +11,7 @@
         "CCodecConfig.cpp",
         "Codec2Buffer.cpp",
         "Codec2InfoBuilder.cpp",
+        "FrameReassembler.cpp",
         "PipelineWatcher.cpp",
         "ReflectedParamUpdater.cpp",
     ],
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 05c1182..ba1d178 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -143,7 +143,8 @@
       mFrameIndex(0u),
       mFirstValidFrameIndex(0u),
       mMetaMode(MODE_NONE),
-      mInputMetEos(false) {
+      mInputMetEos(false),
+      mSendEncryptedInfoBuffer(false) {
     mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + kRenderingDepth;
     {
         Mutexed<Input>::Locked input(mInput);
@@ -188,7 +189,10 @@
     return mInputSurface->signalEndOfInputStream();
 }
 
-status_t CCodecBufferChannel::queueInputBufferInternal(sp<MediaCodecBuffer> buffer) {
+status_t CCodecBufferChannel::queueInputBufferInternal(
+        sp<MediaCodecBuffer> buffer,
+        std::shared_ptr<C2LinearBlock> encryptedBlock,
+        size_t blockSize) {
     int64_t timeUs;
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
@@ -209,6 +213,7 @@
         flags |= C2FrameData::FLAG_CODEC_CONFIG;
     }
     ALOGV("[%s] queueInputBuffer: buffer->size() = %zu", mName, buffer->size());
+    std::list<std::unique_ptr<C2Work>> items;
     std::unique_ptr<C2Work> work(new C2Work);
     work->input.ordinal.timestamp = timeUs;
     work->input.ordinal.frameIndex = mFrameIndex++;
@@ -218,9 +223,8 @@
     work->input.ordinal.customOrdinal = timeUs;
     work->input.buffers.clear();
 
-    uint64_t queuedFrameIndex = work->input.ordinal.frameIndex.peeku();
-    std::vector<std::shared_ptr<C2Buffer>> queuedBuffers;
     sp<Codec2Buffer> copy;
+    bool usesFrameReassembler = false;
 
     if (buffer->size() > 0u) {
         Mutexed<Input>::Locked input(mInput);
@@ -245,30 +249,38 @@
                       "buffer starvation on component.", mName);
             }
         }
-        work->input.buffers.push_back(c2buffer);
-        queuedBuffers.push_back(c2buffer);
+        if (input->frameReassembler) {
+            usesFrameReassembler = true;
+            input->frameReassembler.process(buffer, &items);
+        } else {
+            work->input.buffers.push_back(c2buffer);
+            if (encryptedBlock) {
+                work->input.infoBuffers.emplace_back(C2InfoBuffer::CreateLinearBuffer(
+                        kParamIndexEncryptedBuffer,
+                        encryptedBlock->share(0, blockSize, C2Fence())));
+            }
+        }
     } else if (eos) {
         flags |= C2FrameData::FLAG_END_OF_STREAM;
     }
-    work->input.flags = (C2FrameData::flags_t)flags;
-    // TODO: fill info's
+    if (usesFrameReassembler) {
+        if (!items.empty()) {
+            items.front()->input.configUpdate = std::move(mParamsToBeSet);
+            mFrameIndex = (items.back()->input.ordinal.frameIndex + 1).peek();
+        }
+    } else {
+        work->input.flags = (C2FrameData::flags_t)flags;
+        // TODO: fill info's
 
-    work->input.configUpdate = std::move(mParamsToBeSet);
-    work->worklets.clear();
-    work->worklets.emplace_back(new C2Worklet);
+        work->input.configUpdate = std::move(mParamsToBeSet);
+        work->worklets.clear();
+        work->worklets.emplace_back(new C2Worklet);
 
-    std::list<std::unique_ptr<C2Work>> items;
-    items.push_back(std::move(work));
-    mPipelineWatcher.lock()->onWorkQueued(
-            queuedFrameIndex,
-            std::move(queuedBuffers),
-            PipelineWatcher::Clock::now());
-    c2_status_t err = mComponent->queue(&items);
-    if (err != C2_OK) {
-        mPipelineWatcher.lock()->onWorkDone(queuedFrameIndex);
+        items.push_back(std::move(work));
+
+        eos = eos && buffer->size() > 0u;
     }
-
-    if (err == C2_OK && eos && buffer->size() > 0u) {
+    if (eos) {
         work.reset(new C2Work);
         work->input.ordinal.timestamp = timeUs;
         work->input.ordinal.frameIndex = mFrameIndex++;
@@ -277,23 +289,28 @@
         work->input.buffers.clear();
         work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
         work->worklets.emplace_back(new C2Worklet);
-
-        queuedFrameIndex = work->input.ordinal.frameIndex.peeku();
-        queuedBuffers.clear();
-
-        items.clear();
         items.push_back(std::move(work));
-
-        mPipelineWatcher.lock()->onWorkQueued(
-                queuedFrameIndex,
-                std::move(queuedBuffers),
-                PipelineWatcher::Clock::now());
-        err = mComponent->queue(&items);
-        if (err != C2_OK) {
-            mPipelineWatcher.lock()->onWorkDone(queuedFrameIndex);
-        }
     }
-    if (err == C2_OK) {
+    c2_status_t err = C2_OK;
+    if (!items.empty()) {
+        {
+            Mutexed<PipelineWatcher>::Locked watcher(mPipelineWatcher);
+            PipelineWatcher::Clock::time_point now = PipelineWatcher::Clock::now();
+            for (const std::unique_ptr<C2Work> &work : items) {
+                watcher->onWorkQueued(
+                        work->input.ordinal.frameIndex.peeku(),
+                        std::vector(work->input.buffers),
+                        now);
+            }
+        }
+        err = mComponent->queue(&items);
+    }
+    if (err != C2_OK) {
+        Mutexed<PipelineWatcher>::Locked watcher(mPipelineWatcher);
+        for (const std::unique_ptr<C2Work> &work : items) {
+            watcher->onWorkDone(work->input.ordinal.frameIndex.peeku());
+        }
+    } else {
         Mutexed<Input>::Locked input(mInput);
         bool released = false;
         if (buffer) {
@@ -514,6 +531,40 @@
     }
     sp<EncryptedLinearBlockBuffer> encryptedBuffer((EncryptedLinearBlockBuffer *)buffer.get());
 
+    std::shared_ptr<C2LinearBlock> block;
+    size_t allocSize = buffer->size();
+    size_t bufferSize = 0;
+    c2_status_t blockRes = C2_OK;
+    bool copied = false;
+    if (mSendEncryptedInfoBuffer) {
+        static const C2MemoryUsage kDefaultReadWriteUsage{
+            C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+        constexpr int kAllocGranule0 = 1024 * 64;
+        constexpr int kAllocGranule1 = 1024 * 1024;
+        std::shared_ptr<C2BlockPool> pool = mBlockPools.lock()->inputPool;
+        // round up encrypted sizes to limit fragmentation and encourage buffer reuse
+        if (allocSize <= kAllocGranule1) {
+            bufferSize = align(allocSize, kAllocGranule0);
+        } else {
+            bufferSize = align(allocSize, kAllocGranule1);
+        }
+        blockRes = pool->fetchLinearBlock(
+                bufferSize, kDefaultReadWriteUsage, &block);
+
+        if (blockRes == C2_OK) {
+            C2WriteView view = block->map().get();
+            if (view.error() == C2_OK && view.size() == bufferSize) {
+                copied = true;
+                // TODO: only copy clear sections
+                memcpy(view.data(), buffer->data(), allocSize);
+            }
+        }
+    }
+
+    if (!copied) {
+        block.reset();
+    }
+
     ssize_t result = -1;
     ssize_t codecDataOffset = 0;
     if (numSubSamples == 1
@@ -605,7 +656,8 @@
     }
 
     buffer->setRange(codecDataOffset, result - codecDataOffset);
-    return queueInputBufferInternal(buffer);
+
+    return queueInputBufferInternal(buffer, block, bufferSize);
 }
 
 void CCodecBufferChannel::feedInputBufferIfAvailable() {
@@ -882,27 +934,31 @@
         bool buffersBoundToCodec) {
     C2StreamBufferTypeSetting::input iStreamFormat(0u);
     C2StreamBufferTypeSetting::output oStreamFormat(0u);
+    C2ComponentKindSetting kind;
     C2PortReorderBufferDepthTuning::output reorderDepth;
     C2PortReorderKeySetting::output reorderKey;
     C2PortActualDelayTuning::input inputDelay(0);
     C2PortActualDelayTuning::output outputDelay(0);
     C2ActualPipelineDelayTuning pipelineDelay(0);
+    C2SecureModeTuning secureMode(C2Config::SM_UNPROTECTED);
 
     c2_status_t err = mComponent->query(
             {
                 &iStreamFormat,
                 &oStreamFormat,
+                &kind,
                 &reorderDepth,
                 &reorderKey,
                 &inputDelay,
                 &pipelineDelay,
                 &outputDelay,
+                &secureMode,
             },
             {},
             C2_DONT_BLOCK,
             nullptr);
     if (err == C2_BAD_INDEX) {
-        if (!iStreamFormat || !oStreamFormat) {
+        if (!iStreamFormat || !oStreamFormat || !kind) {
             return UNKNOWN_ERROR;
         }
     } else if (err != C2_OK) {
@@ -919,18 +975,26 @@
     // TODO: get this from input format
     bool secure = mComponent->getName().find(".secure") != std::string::npos;
 
+    // secure mode is a static parameter (shall not change in the executing state)
+    mSendEncryptedInfoBuffer = secureMode.value == C2Config::SM_READ_PROTECTED_WITH_ENCRYPTED;
+
     std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
     int poolMask = GetCodec2PoolMask();
     C2PlatformAllocatorStore::id_t preferredLinearId = GetPreferredLinearAllocatorId(poolMask);
 
     if (inputFormat != nullptr) {
         bool graphic = (iStreamFormat.value == C2BufferData::GRAPHIC);
+        bool audioEncoder = !graphic && (kind.value == C2Component::KIND_ENCODER);
         C2Config::api_feature_t apiFeatures = C2Config::api_feature_t(
                 API_REFLECTION |
                 API_VALUES |
                 API_CURRENT_VALUES |
                 API_DEPENDENCY |
                 API_SAME_INPUT_BUFFER);
+        C2StreamAudioFrameSizeInfo::input encoderFrameSize(0u);
+        C2StreamSampleRateInfo::input sampleRate(0u);
+        C2StreamChannelCountInfo::input channelCount(0u);
+        C2StreamPcmEncodingInfo::input pcmEncoding(0u);
         std::shared_ptr<C2BlockPool> pool;
         {
             Mutexed<BlockPools>::Locked pools(mBlockPools);
@@ -943,7 +1007,19 @@
             // from component, create the input block pool with given ID. Otherwise, use default IDs.
             std::vector<std::unique_ptr<C2Param>> params;
             C2ApiFeaturesSetting featuresSetting{apiFeatures};
-            err = mComponent->query({ &featuresSetting },
+            std::vector<C2Param *> stackParams({&featuresSetting});
+            if (audioEncoder) {
+                stackParams.push_back(&encoderFrameSize);
+                stackParams.push_back(&sampleRate);
+                stackParams.push_back(&channelCount);
+                stackParams.push_back(&pcmEncoding);
+            } else {
+                encoderFrameSize.invalidate();
+                sampleRate.invalidate();
+                channelCount.invalidate();
+                pcmEncoding.invalidate();
+            }
+            err = mComponent->query(stackParams,
                                     { C2PortAllocatorsTuning::input::PARAM_TYPE },
                                     C2_DONT_BLOCK,
                                     &params);
@@ -1001,10 +1077,21 @@
         input->numSlots = numInputSlots;
         input->extraBuffers.flush();
         input->numExtraSlots = 0u;
+        if (audioEncoder && encoderFrameSize && sampleRate && channelCount) {
+            input->frameReassembler.init(
+                    pool,
+                    {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
+                    encoderFrameSize.value,
+                    sampleRate.value,
+                    channelCount.value,
+                    pcmEncoding ? pcmEncoding.value : C2Config::PCM_16);
+        }
         bool conforming = (apiFeatures & API_SAME_INPUT_BUFFER);
         // For encrypted content, framework decrypts source buffer (ashmem) into
         // C2Buffers. Thus non-conforming codecs can process these.
-        if (!buffersBoundToCodec && (hasCryptoOrDescrambler() || conforming)) {
+        if (!buffersBoundToCodec
+                && !input->frameReassembler
+                && (hasCryptoOrDescrambler() || conforming)) {
             input->buffers.reset(new SlotInputBuffers(mName));
         } else if (graphic) {
             if (mInputSurface) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index e2c9aaa..b9e8d39 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -31,6 +31,7 @@
 #include <media/stagefright/CodecBase.h>
 
 #include "CCodecBuffers.h"
+#include "FrameReassembler.h"
 #include "InputSurfaceWrapper.h"
 #include "PipelineWatcher.h"
 
@@ -238,7 +239,9 @@
 
     void feedInputBufferIfAvailable();
     void feedInputBufferIfAvailableInternal();
-    status_t queueInputBufferInternal(sp<MediaCodecBuffer> buffer);
+    status_t queueInputBufferInternal(sp<MediaCodecBuffer> buffer,
+                                      std::shared_ptr<C2LinearBlock> encryptedBlock = nullptr,
+                                      size_t blockSize = 0);
     bool handleWork(
             std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
             const C2StreamInitDataInfo::output *initData);
@@ -269,6 +272,8 @@
         size_t numExtraSlots;
         uint32_t inputDelay;
         uint32_t pipelineDelay;
+
+        FrameReassembler frameReassembler;
     };
     Mutexed<Input> mInput;
     struct Output {
@@ -315,6 +320,7 @@
     inline bool hasCryptoOrDescrambler() {
         return mCrypto != nullptr || mDescrambler != nullptr;
     }
+    std::atomic_bool mSendEncryptedInfoBuffer;
 };
 
 // Conversion of a c2_status_t value to a status_t value may depend on the
diff --git a/media/codec2/sfplugin/FrameReassembler.cpp b/media/codec2/sfplugin/FrameReassembler.cpp
new file mode 100644
index 0000000..f8e6937
--- /dev/null
+++ b/media/codec2/sfplugin/FrameReassembler.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "FrameReassembler"
+
+#include <log/log.h>
+
+#include <media/stagefright/foundation/AMessage.h>
+
+#include "FrameReassembler.h"
+
+namespace android {
+
+static constexpr uint64_t kToleranceUs = 1000;  // 1ms
+
+FrameReassembler::FrameReassembler()
+    : mUsage{0, 0},
+      mSampleRate(0u),
+      mChannelCount(0u),
+      mEncoding(C2Config::PCM_16),
+      mCurrentOrdinal({0, 0, 0}) {
+}
+
+void FrameReassembler::init(
+        const std::shared_ptr<C2BlockPool> &pool,
+        C2MemoryUsage usage,
+        uint32_t frameSize,
+        uint32_t sampleRate,
+        uint32_t channelCount,
+        C2Config::pcm_encoding_t encoding) {
+    mBlockPool = pool;
+    mUsage = usage;
+    mFrameSize = frameSize;
+    mSampleRate = sampleRate;
+    mChannelCount = channelCount;
+    mEncoding = encoding;
+}
+
+void FrameReassembler::updateFrameSize(uint32_t frameSize) {
+    finishCurrentBlock(&mPendingWork);
+    mFrameSize = frameSize;
+}
+
+void FrameReassembler::updateSampleRate(uint32_t sampleRate) {
+    finishCurrentBlock(&mPendingWork);
+    mSampleRate = sampleRate;
+}
+
+void FrameReassembler::updateChannelCount(uint32_t channelCount) {
+    finishCurrentBlock(&mPendingWork);
+    mChannelCount = channelCount;
+}
+
+void FrameReassembler::updatePcmEncoding(C2Config::pcm_encoding_t encoding) {
+    finishCurrentBlock(&mPendingWork);
+    mEncoding = encoding;
+}
+
+void FrameReassembler::reset() {
+    flush();
+    mCurrentOrdinal = {0, 0, 0};
+    mBlockPool.reset();
+    mFrameSize.reset();
+    mSampleRate = 0u;
+    mChannelCount = 0u;
+    mEncoding = C2Config::PCM_16;
+}
+
+FrameReassembler::operator bool() const {
+    return mFrameSize.has_value();
+}
+
+c2_status_t FrameReassembler::process(
+        const sp<MediaCodecBuffer> &buffer,
+        std::list<std::unique_ptr<C2Work>> *items) {
+    int64_t timeUs;
+    if (buffer->size() == 0u
+            || !buffer->meta()->findInt64("timeUs", &timeUs)) {
+        return C2_BAD_VALUE;
+    }
+
+    items->splice(items->end(), mPendingWork);
+
+    // Fill mCurrentBlock
+    if (mCurrentBlock) {
+        // First check the timestamp
+        c2_cntr64_t endTimestampUs = mCurrentOrdinal.timestamp;
+        endTimestampUs += bytesToSamples(mWriteView->size()) * 1000000 / mSampleRate;
+        if (timeUs < endTimestampUs.peek()) {
+            uint64_t diffUs = (endTimestampUs - timeUs).peeku();
+            if (diffUs > kToleranceUs) {
+                // The timestamp is going back in time in large amount.
+                // TODO: b/145702136
+                ALOGW("timestamp going back in time! from %lld to %lld",
+                        endTimestampUs.peekll(), (long long)timeUs);
+            }
+        } else {  // timeUs >= endTimestampUs.peek()
+            uint64_t diffUs = (timeUs - endTimestampUs).peeku();
+            if (diffUs > kToleranceUs) {
+                // The timestamp is going forward; add silence as necessary.
+                size_t gapSamples = usToSamples(diffUs);
+                size_t remainingSamples =
+                    (mWriteView->capacity() - mWriteView->size())
+                    / mChannelCount / bytesPerSample();
+                if (gapSamples < remainingSamples) {
+                    size_t gapBytes = gapSamples * mChannelCount * bytesPerSample();
+                    memset(mWriteView->base() + mWriteView->size(), 0u, gapBytes);
+                    mWriteView->setSize(mWriteView->size() + gapBytes);
+                } else {
+                    finishCurrentBlock(items);
+                }
+            }
+        }
+    }
+
+    if (mCurrentBlock) {
+        // Append the data at the end of the current block
+        size_t copySize = std::min(
+                buffer->size(),
+                size_t(mWriteView->capacity() - mWriteView->size()));
+        memcpy(mWriteView->base() + mWriteView->size(), buffer->data(), copySize);
+        buffer->setRange(buffer->offset() + copySize, buffer->size() - copySize);
+        mWriteView->setSize(mWriteView->size() + copySize);
+        if (mWriteView->size() == mWriteView->capacity()) {
+            finishCurrentBlock(items);
+        }
+        timeUs += bytesToSamples(copySize) * 1000000 / mSampleRate;
+    }
+
+    if (buffer->size() > 0) {
+        mCurrentOrdinal.timestamp = timeUs;
+    }
+
+    size_t frameSizeBytes = mFrameSize.value() * mChannelCount * bytesPerSample();
+    while (buffer->size() > 0) {
+        LOG_ALWAYS_FATAL_IF(
+                mCurrentBlock,
+                "There's remaining data but the pending block is not filled & finished");
+        std::unique_ptr<C2Work> work(new C2Work);
+        c2_status_t err = mBlockPool->fetchLinearBlock(frameSizeBytes, mUsage, &mCurrentBlock);
+        if (err != C2_OK) {
+            return err;
+        }
+        size_t copySize = std::min(buffer->size(), frameSizeBytes);
+        mWriteView = mCurrentBlock->map().get();
+        if (mWriteView->error() != C2_OK) {
+            return mWriteView->error();
+        }
+        ALOGV("buffer={offset=%zu size=%zu) copySize=%zu",
+                buffer->offset(), buffer->size(), copySize);
+        memcpy(mWriteView->base(), buffer->data(), copySize);
+        mWriteView->setOffset(0u);
+        mWriteView->setSize(copySize);
+        buffer->setRange(buffer->offset() + copySize, buffer->size() - copySize);
+        if (copySize == frameSizeBytes) {
+            finishCurrentBlock(items);
+        }
+    }
+
+    int32_t eos = 0;
+    if (buffer->meta()->findInt32("eos", &eos) && eos) {
+        finishCurrentBlock(items);
+    }
+
+    return C2_OK;
+}
+
+void FrameReassembler::flush() {
+    mPendingWork.clear();
+    mWriteView.reset();
+    mCurrentBlock.reset();
+}
+
+uint64_t FrameReassembler::bytesToSamples(size_t numBytes) const {
+    return numBytes / mChannelCount / bytesPerSample();
+}
+
+size_t FrameReassembler::usToSamples(uint64_t us) const {
+    return (us * mChannelCount * mSampleRate / 1000000);
+}
+
+uint32_t FrameReassembler::bytesPerSample() const {
+    return (mEncoding == C2Config::PCM_8) ? 1
+         : (mEncoding == C2Config::PCM_16) ? 2
+         : (mEncoding == C2Config::PCM_FLOAT) ? 4 : 0;
+}
+
+void FrameReassembler::finishCurrentBlock(std::list<std::unique_ptr<C2Work>> *items) {
+    if (!mCurrentBlock) {
+        // No-op
+        return;
+    }
+    if (mWriteView->size() < mWriteView->capacity()) {
+        memset(mWriteView->base() + mWriteView->size(), 0u,
+                mWriteView->capacity() - mWriteView->size());
+        mWriteView->setSize(mWriteView->capacity());
+    }
+    std::unique_ptr<C2Work> work{std::make_unique<C2Work>()};
+    work->input.ordinal = mCurrentOrdinal;
+    work->input.buffers.push_back(C2Buffer::CreateLinearBuffer(
+            mCurrentBlock->share(0, mCurrentBlock->capacity(), C2Fence())));
+    work->worklets.clear();
+    work->worklets.emplace_back(new C2Worklet);
+    items->push_back(std::move(work));
+
+    ++mCurrentOrdinal.frameIndex;
+    mCurrentOrdinal.timestamp += mFrameSize.value() * 1000000 / mSampleRate;
+    mCurrentBlock.reset();
+    mWriteView.reset();
+}
+
+}  // namespace android
diff --git a/media/codec2/sfplugin/FrameReassembler.h b/media/codec2/sfplugin/FrameReassembler.h
new file mode 100644
index 0000000..17ac06d
--- /dev/null
+++ b/media/codec2/sfplugin/FrameReassembler.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019 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 FRAME_REASSEMBLER_H_
+#define FRAME_REASSEMBLER_H_
+
+#include <set>
+#include <memory>
+
+#include <media/MediaCodecBuffer.h>
+
+#include <C2Config.h>
+#include <C2Work.h>
+
+namespace android {
+
+class FrameReassembler {
+public:
+    FrameReassembler();
+
+    void init(
+            const std::shared_ptr<C2BlockPool> &pool,
+            C2MemoryUsage usage,
+            uint32_t frameSize,
+            uint32_t sampleRate,
+            uint32_t channelCount,
+            C2Config::pcm_encoding_t encoding);
+    void updateFrameSize(uint32_t frameSize);
+    void updateSampleRate(uint32_t sampleRate);
+    void updateChannelCount(uint32_t channelCount);
+    void updatePcmEncoding(C2Config::pcm_encoding_t encoding);
+    void reset();
+    void flush();
+
+    explicit operator bool() const;
+
+    c2_status_t process(
+            const sp<MediaCodecBuffer> &buffer,
+            std::list<std::unique_ptr<C2Work>> *items);
+
+private:
+    std::shared_ptr<C2BlockPool> mBlockPool;
+    C2MemoryUsage mUsage;
+    std::optional<uint32_t> mFrameSize;
+    uint32_t mSampleRate;
+    uint32_t mChannelCount;
+    C2Config::pcm_encoding_t mEncoding;
+    std::list<std::unique_ptr<C2Work>> mPendingWork;
+    C2WorkOrdinalStruct mCurrentOrdinal;
+    std::shared_ptr<C2LinearBlock> mCurrentBlock;
+    std::optional<C2WriteView> mWriteView;
+
+    uint64_t bytesToSamples(size_t numBytes) const;
+    size_t usToSamples(uint64_t us) const;
+    uint32_t bytesPerSample() const;
+
+    void finishCurrentBlock(std::list<std::unique_ptr<C2Work>> *items);
+};
+
+}  // namespace android
+
+#endif  // FRAME_REASSEMBLER_H_
diff --git a/media/codec2/sfplugin/tests/Android.bp b/media/codec2/sfplugin/tests/Android.bp
index 8d1a9c3..51b99a4 100644
--- a/media/codec2/sfplugin/tests/Android.bp
+++ b/media/codec2/sfplugin/tests/Android.bp
@@ -4,6 +4,7 @@
     srcs: [
         "CCodecBuffers_test.cpp",
         "CCodecConfig_test.cpp",
+        "FrameReassembler_test.cpp",
         "ReflectedParamUpdater_test.cpp",
     ],
 
diff --git a/media/codec2/sfplugin/tests/FrameReassembler_test.cpp b/media/codec2/sfplugin/tests/FrameReassembler_test.cpp
new file mode 100644
index 0000000..6738ee7
--- /dev/null
+++ b/media/codec2/sfplugin/tests/FrameReassembler_test.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrameReassembler.h"
+
+#include <gtest/gtest.h>
+
+#include <C2PlatformSupport.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+static size_t BytesPerSample(C2Config::pcm_encoding_t encoding) {
+    return encoding == PCM_8 ? 1
+         : encoding == PCM_16 ? 2
+         : encoding == PCM_FLOAT ? 4 : 0;
+}
+
+static uint64_t Diff(c2_cntr64_t a, c2_cntr64_t b) {
+    return std::abs((a - b).peek());
+}
+
+class FrameReassemblerTest : public ::testing::Test {
+public:
+    static const C2MemoryUsage kUsage;
+    static constexpr uint64_t kTimestampToleranceUs = 100;
+
+    FrameReassemblerTest() {
+        mInitStatus = GetCodec2BlockPool(C2BlockPool::BASIC_LINEAR, nullptr, &mPool);
+    }
+
+    status_t initStatus() const { return mInitStatus; }
+
+    void testPushSameSize(
+            size_t encoderFrameSize,
+            size_t sampleRate,
+            size_t channelCount,
+            C2Config::pcm_encoding_t encoding,
+            size_t inputFrameSizeInBytes,
+            size_t count,
+            size_t expectedOutputSize) {
+        FrameReassembler frameReassembler;
+        frameReassembler.init(
+                mPool,
+                kUsage,
+                encoderFrameSize,
+                sampleRate,
+                channelCount,
+                encoding);
+
+        ASSERT_TRUE(frameReassembler) << "FrameReassembler init failed";
+
+        size_t inputIndex = 0, outputIndex = 0;
+        size_t expectCount = 0;
+        for (size_t i = 0; i < count; ++i) {
+            sp<MediaCodecBuffer> buffer = new MediaCodecBuffer(
+                    new AMessage, new ABuffer(inputFrameSizeInBytes));
+            buffer->setRange(0, inputFrameSizeInBytes);
+            buffer->meta()->setInt64(
+                    "timeUs",
+                    inputIndex * 1000000 / sampleRate / channelCount / BytesPerSample(encoding));
+            if (i == count - 1) {
+                buffer->meta()->setInt32("eos", 1);
+            }
+            for (size_t j = 0; j < inputFrameSizeInBytes; ++j, ++inputIndex) {
+                buffer->base()[j] = (inputIndex & 0xFF);
+            }
+            std::list<std::unique_ptr<C2Work>> items;
+            ASSERT_EQ(C2_OK, frameReassembler.process(buffer, &items));
+            while (!items.empty()) {
+                std::unique_ptr<C2Work> work = std::move(*items.begin());
+                items.erase(items.begin());
+                // Verify timestamp
+                uint64_t expectedTimeUs =
+                    outputIndex * 1000000 / sampleRate / channelCount / BytesPerSample(encoding);
+                EXPECT_GE(
+                        kTimestampToleranceUs,
+                        Diff(expectedTimeUs, work->input.ordinal.timestamp))
+                    << "expected timestamp: " << expectedTimeUs
+                    << " actual timestamp: " << work->input.ordinal.timestamp.peeku()
+                    << " output index: " << outputIndex;
+
+                // Verify buffer
+                ASSERT_EQ(1u, work->input.buffers.size());
+                std::shared_ptr<C2Buffer> buffer = work->input.buffers.front();
+                ASSERT_EQ(C2BufferData::LINEAR, buffer->data().type());
+                ASSERT_EQ(1u, buffer->data().linearBlocks().size());
+                C2ReadView view = buffer->data().linearBlocks().front().map().get();
+                ASSERT_EQ(C2_OK, view.error());
+                ASSERT_EQ(encoderFrameSize * BytesPerSample(encoding), view.capacity());
+                for (size_t j = 0; j < view.capacity(); ++j, ++outputIndex) {
+                    ASSERT_TRUE(outputIndex < inputIndex
+                             || inputIndex == inputFrameSizeInBytes * count);
+                    uint8_t expected = outputIndex < inputIndex ? (outputIndex & 0xFF) : 0;
+                    if (expectCount < 10) {
+                        ++expectCount;
+                        EXPECT_EQ(expected, view.data()[j]) << "output index = " << outputIndex;
+                    }
+                }
+            }
+        }
+
+        ASSERT_EQ(inputFrameSizeInBytes * count, inputIndex);
+        size_t encoderFrameSizeInBytes =
+            encoderFrameSize * channelCount * BytesPerSample(encoding);
+        ASSERT_EQ(0, outputIndex % encoderFrameSizeInBytes)
+            << "output size must be multiple of frame size: output size = " << outputIndex
+            << " frame size = " << encoderFrameSizeInBytes;
+        ASSERT_EQ(expectedOutputSize, outputIndex)
+            << "output size must be smallest multiple of frame size, "
+            << "equal to or larger than input size. output size = " << outputIndex
+            << " input size = " << inputIndex << " frame size = " << encoderFrameSizeInBytes;
+    }
+
+private:
+    status_t mInitStatus;
+    std::shared_ptr<C2BlockPool> mPool;
+};
+
+const C2MemoryUsage FrameReassemblerTest::kUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+
+// Push frames with exactly the same size as the encoder requested.
+TEST_F(FrameReassemblerTest, PushExactFrameSize) {
+    ASSERT_EQ(OK, initStatus());
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_8,
+            1024 /* input frame size in bytes = 1024 samples * 1 channel * 1 bytes/sample */,
+            10 /* count */,
+            10240 /* expected output size = 10 * 1024 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_16,
+            2048 /* input frame size in bytes = 1024 samples * 1 channel * 2 bytes/sample */,
+            10 /* count */,
+            20480 /* expected output size = 10 * 2048 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_FLOAT,
+            4096 /* input frame size in bytes = 1024 samples * 1 channel * 4 bytes/sample */,
+            10 /* count */,
+            40960 /* expected output size = 10 * 4096 bytes/frame */);
+}
+
+// Push frames with half the size that the encoder requested.
+TEST_F(FrameReassemblerTest, PushHalfFrameSize) {
+    ASSERT_EQ(OK, initStatus());
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_8,
+            512 /* input frame size in bytes = 512 samples * 1 channel * 1 bytes per sample */,
+            10 /* count */,
+            5120 /* expected output size = 5 * 1024 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_16,
+            1024 /* input frame size in bytes = 512 samples * 1 channel * 2 bytes per sample */,
+            10 /* count */,
+            10240 /* expected output size = 5 * 2048 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_FLOAT,
+            2048 /* input frame size in bytes = 512 samples * 1 channel * 4 bytes per sample */,
+            10 /* count */,
+            20480 /* expected output size = 5 * 4096 bytes/frame */);
+}
+
+// Push frames with twice the size that the encoder requested.
+TEST_F(FrameReassemblerTest, PushDoubleFrameSize) {
+    ASSERT_EQ(OK, initStatus());
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_8,
+            2048 /* input frame size in bytes = 2048 samples * 1 channel * 1 bytes per sample */,
+            10 /* count */,
+            20480 /* expected output size = 20 * 1024 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_16,
+            4096 /* input frame size in bytes = 2048 samples * 1 channel * 2 bytes per sample */,
+            10 /* count */,
+            40960 /* expected output size = 20 * 2048 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_FLOAT,
+            8192 /* input frame size in bytes = 2048 samples * 1 channel * 4 bytes per sample */,
+            10 /* count */,
+            81920 /* expected output size = 20 * 4096 bytes/frame */);
+}
+
+// Push frames with a little bit larger (+5 samples) than the requested size.
+TEST_F(FrameReassemblerTest, PushLittleLargerFrameSize) {
+    ASSERT_EQ(OK, initStatus());
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_8,
+            1029 /* input frame size in bytes = 1029 samples * 1 channel * 1 bytes per sample */,
+            10 /* count */,
+            11264 /* expected output size = 11 * 1024 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_16,
+            2058 /* input frame size in bytes = 1029 samples * 1 channel * 2 bytes per sample */,
+            10 /* count */,
+            22528 /* expected output size = 11 * 2048 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_FLOAT,
+            4116 /* input frame size in bytes = 1029 samples * 1 channel * 4 bytes per sample */,
+            10 /* count */,
+            45056 /* expected output size = 11 * 4096 bytes/frame */);
+}
+
+// Push frames with a little bit smaller (-5 samples) than the requested size.
+TEST_F(FrameReassemblerTest, PushLittleSmallerFrameSize) {
+    ASSERT_EQ(OK, initStatus());
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_8,
+            1019 /* input frame size in bytes = 1019 samples * 1 channel * 1 bytes per sample */,
+            10 /* count */,
+            10240 /* expected output size = 10 * 1024 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_16,
+            2038 /* input frame size in bytes = 1019 samples * 1 channel * 2 bytes per sample */,
+            10 /* count */,
+            20480 /* expected output size = 10 * 2048 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_FLOAT,
+            4076 /* input frame size in bytes = 1019 samples * 1 channel * 4 bytes per sample */,
+            10 /* count */,
+            40960 /* expected output size = 10 * 4096 bytes/frame */);
+}
+
+// Push single-byte frames
+TEST_F(FrameReassemblerTest, PushSingleByte) {
+    ASSERT_EQ(OK, initStatus());
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_8,
+            1 /* input frame size in bytes */,
+            100000 /* count */,
+            100352 /* expected output size = 98 * 1024 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_16,
+            1 /* input frame size in bytes */,
+            100000 /* count */,
+            100352 /* expected output size = 49 * 2048 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_FLOAT,
+            1 /* input frame size in bytes */,
+            100000 /* count */,
+            102400 /* expected output size = 25 * 4096 bytes/frame */);
+}
+
+// Push one big chunk.
+TEST_F(FrameReassemblerTest, PushBigChunk) {
+    ASSERT_EQ(OK, initStatus());
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_8,
+            100000 /* input frame size in bytes */,
+            1 /* count */,
+            100352 /* expected output size = 98 * 1024 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_16,
+            100000 /* input frame size in bytes */,
+            1 /* count */,
+            100352 /* expected output size = 49 * 2048 bytes/frame */);
+    testPushSameSize(
+            1024 /* frame size in samples */,
+            48000 /* sample rate */,
+            1 /* channel count */,
+            PCM_FLOAT,
+            100000 /* input frame size in bytes */,
+            1 /* count */,
+            102400 /* expected output size = 25 * 4096 bytes/frame */);
+}
+
+} // namespace android
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index e0ac7e5..709c656 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -1024,7 +1024,6 @@
 // Stream Control
 // ============================================================
 
-#if __ANDROID_API__ >= 30
 /**
  * Free the audio resources associated with a stream created by
  * AAudioStreamBuilder_openStream().
@@ -1042,11 +1041,12 @@
  * On other "Legacy" streams some audio resources will still be in use
  * and some callbacks may still be in process after this call.
  *
+ * Available since API level 30.
+ *
  * @param stream reference provided by AAudioStreamBuilder_openStream()
  * @return {@link #AAUDIO_OK} or a negative error.
  */
 AAUDIO_API aaudio_result_t  AAudioStream_release(AAudioStream* stream) __INTRODUCED_IN(30);
-#endif // __ANDROID_API__
 
 /**
  * Delete the internal data structures associated with the stream created
@@ -1054,6 +1054,8 @@
  *
  * If AAudioStream_release() has not been called then it will be called automatically.
  *
+ * Available since API level 26.
+ *
  * @param stream reference provided by AAudioStreamBuilder_openStream()
  * @return {@link #AAUDIO_OK} or a negative error.
  */
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 4fe871f..0ed0de1 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -317,7 +317,7 @@
 
 class MediaCodec::ReleaseSurface {
 public:
-    ReleaseSurface() {
+    explicit ReleaseSurface(uint64_t usage) {
         BufferQueue::createBufferQueue(&mProducer, &mConsumer);
         mSurface = new Surface(mProducer, false /* controlledByApp */);
         struct ConsumerListener : public BnConsumerListener {
@@ -328,6 +328,7 @@
         sp<ConsumerListener> listener{new ConsumerListener};
         mConsumer->consumerConnect(listener, false);
         mConsumer->setConsumerName(String8{"MediaCodec.release"});
+        mConsumer->setConsumerUsageBits(usage);
     }
 
     const sp<Surface> &getSurface() {
@@ -3122,7 +3123,11 @@
             if (asyncNotify != nullptr) {
                 if (mSurface != NULL) {
                     if (!mReleaseSurface) {
-                        mReleaseSurface.reset(new ReleaseSurface);
+                        uint64_t usage = 0;
+                        if (mSurface->getConsumerUsage(&usage) != OK) {
+                            usage = 0;
+                        }
+                        mReleaseSurface.reset(new ReleaseSurface(usage));
                     }
                     if (mSurface != mReleaseSurface->getSurface()) {
                         status_t err = connectToSurface(mReleaseSurface->getSurface());
diff --git a/media/libstagefright/tests/Android.bp b/media/libstagefright/tests/Android.bp
index 5f3f72c..4a505d4 100644
--- a/media/libstagefright/tests/Android.bp
+++ b/media/libstagefright/tests/Android.bp
@@ -9,7 +9,6 @@
         "libmedia",
         "libstagefright",
         "libstagefright_foundation",
-        "libstagefright_omx",
         "libutils",
         "liblog",
     ],
@@ -17,11 +16,8 @@
     include_dirs: [
         "frameworks/av/media/libstagefright",
         "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
     ],
 
-    compile_multilib: "prefer32",
-
     cflags: [
         "-Werror",
         "-Wall",
diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h
index 62b8624..e19dd3a 100644
--- a/media/ndk/include/media/NdkImage.h
+++ b/media/ndk/include/media/NdkImage.h
@@ -559,8 +559,6 @@
     int32_t bottom;
 } AImageCropRect;
 
-#if __ANDROID_API__ >= 24
-
 /**
  * Return the image back the the system and delete the AImage object from memory.
  *
@@ -777,10 +775,6 @@
         const AImage* image, int planeIdx,
         /*out*/uint8_t** data, /*out*/int* dataLength) __INTRODUCED_IN(24);
 
-#endif /* __ANDROID_API__ >= 24 */
-
-#if __ANDROID_API__ >= 26
-
 /**
  * Return the image back the the system and delete the AImage object from memory asynchronously.
  *
@@ -829,8 +823,6 @@
  */
 media_status_t AImage_getHardwareBuffer(const AImage* image, /*out*/AHardwareBuffer** buffer) __INTRODUCED_IN(26);
 
-#endif /* __ANDROID_API__ >= 26 */
-
 __END_DECLS
 
 #endif //_NDK_IMAGE_H
diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h
index 600ffc9..d86f3c7 100644
--- a/media/ndk/include/media/NdkImageReader.h
+++ b/media/ndk/include/media/NdkImageReader.h
@@ -53,8 +53,6 @@
  */
 typedef struct AImageReader AImageReader;
 
-#if __ANDROID_API__ >= 24
-
 /**
  * Create a new reader for images of the desired size and format.
  *
@@ -320,10 +318,6 @@
 media_status_t AImageReader_setImageListener(
         AImageReader* reader, AImageReader_ImageListener* listener) __INTRODUCED_IN(24);
 
-#endif /* __ANDROID_API__ >= 24 */
-
-#if __ANDROID_API__ >= 26
-
 /**
  * AImageReader constructor similar to {@link AImageReader_new} that takes an additional parameter
  * for the consumer usage. All other parameters and the return values are identical to those passed
@@ -510,8 +504,6 @@
     AImageReader *reader, /* out */native_handle_t **handle);
 #endif
 
-#endif /* __ANDROID_API__ >= 26 */
-
 __END_DECLS
 
 #endif //_NDK_IMAGE_READER_H
diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h
index 80d5d50..60cdd32 100644
--- a/media/ndk/include/media/NdkMediaCodec.h
+++ b/media/ndk/include/media/NdkMediaCodec.h
@@ -121,8 +121,6 @@
       AMediaCodecOnAsyncError           onAsyncError;
 } AMediaCodecOnAsyncNotifyCallback;
 
-#if __ANDROID_API__ >= 21
-
 /**
  * Create codec by name. Use this if you know the exact codec you want to use.
  * When configuring, you will need to specify whether to use the codec as an
@@ -311,8 +309,6 @@
 media_status_t AMediaCodec_releaseOutputBufferAtTime(
         AMediaCodec *mData, size_t idx, int64_t timestampNs) __INTRODUCED_IN(21);
 
-#if __ANDROID_API__ >= 26
-
 /**
  * Creates a Surface that can be used as the input to encoder, in place of input buffers
  *
@@ -393,10 +389,6 @@
  */
 media_status_t AMediaCodec_signalEndOfInputStream(AMediaCodec *mData) __INTRODUCED_IN(26);
 
-#endif /* __ANDROID_API__ >= 26 */
-
-#if __ANDROID_API__ >= 28
-
 /**
  * Get format of the buffer. The specified buffer index must have been previously obtained from
  * dequeueOutputBuffer.
@@ -482,8 +474,6 @@
  */
 bool AMediaCodecActionCode_isTransient(int32_t actionCode) __INTRODUCED_IN(28);
 
-#endif /* __ANDROID_API__ >= 28 */
-
 typedef enum {
     AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
     AMEDIACODECRYPTOINFO_MODE_AES_CTR = 1,
@@ -580,8 +570,6 @@
  */
 media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst) __INTRODUCED_IN(21);
 
-#endif /* __ANDROID_API__ >= 21 */
-
 __END_DECLS
 
 #endif //_NDK_MEDIA_CODEC_H
diff --git a/media/ndk/include/media/NdkMediaCrypto.h b/media/ndk/include/media/NdkMediaCrypto.h
index 3fa07c7..590d51d 100644
--- a/media/ndk/include/media/NdkMediaCrypto.h
+++ b/media/ndk/include/media/NdkMediaCrypto.h
@@ -47,8 +47,6 @@
 
 typedef uint8_t AMediaUUID[16];
 
-#if __ANDROID_API__ >= 21
-
 /**
  * Available since API level 21.
  */
@@ -69,8 +67,6 @@
  */
 void AMediaCrypto_delete(AMediaCrypto* crypto) __INTRODUCED_IN(21);
 
-#endif /* __ANDROID_API__ >= 21 */
-
 __END_DECLS
 
 #endif // _NDK_MEDIA_CRYPTO_H
diff --git a/media/ndk/include/media/NdkMediaDataSource.h b/media/ndk/include/media/NdkMediaDataSource.h
index 0577df2..4158a97 100644
--- a/media/ndk/include/media/NdkMediaDataSource.h
+++ b/media/ndk/include/media/NdkMediaDataSource.h
@@ -38,8 +38,6 @@
 struct AMediaDataSource;
 typedef struct AMediaDataSource AMediaDataSource;
 
-#if __ANDROID_API__ >= 28
-
 /*
  * AMediaDataSource's callbacks will be invoked on an implementation-defined thread
  * or thread pool. No guarantees are provided about which thread(s) will be used for
@@ -93,8 +91,6 @@
  */
 AMediaDataSource* AMediaDataSource_new() __INTRODUCED_IN(28);
 
-#if __ANDROID_API__ >= 29
-
 /**
  * Called to get an estimate of the number of bytes that can be read from this data source
  * starting at |offset| without blocking for I/O.
@@ -124,8 +120,6 @@
         int numheaders,
         const char * const *key_values) __INTRODUCED_IN(29);
 
-#endif  /*__ANDROID_API__ >= 29 */
-
 /**
  * Delete a previously created media data source.
  *
@@ -185,10 +179,6 @@
         AMediaDataSource*,
         AMediaDataSourceClose) __INTRODUCED_IN(28);
 
-#endif  /*__ANDROID_API__ >= 28 */
-
-#if __ANDROID_API__ >= 29
-
 /**
  * Close the data source, unblock reads, and release associated resources.
  *
@@ -213,8 +203,6 @@
         AMediaDataSource*,
         AMediaDataSourceGetAvailableSize) __INTRODUCED_IN(29);
 
-#endif  /*__ANDROID_API__ >= 29 */
-
 __END_DECLS
 
 #endif // _NDK_MEDIA_DATASOURCE_H
diff --git a/media/ndk/include/media/NdkMediaDrm.h b/media/ndk/include/media/NdkMediaDrm.h
index 31f5c7d..849a8f9 100644
--- a/media/ndk/include/media/NdkMediaDrm.h
+++ b/media/ndk/include/media/NdkMediaDrm.h
@@ -165,8 +165,6 @@
         const AMediaDrmSessionId *sessionId, const AMediaDrmKeyStatus *keyStatus,
         size_t numKeys, bool hasNewUsableKey);
 
-#if __ANDROID_API__ >= 21
-
 /**
  * Query if the given scheme identified by its UUID is supported on this device, and
  * whether the drm plugin is able to handle the media container format specified by mimeType.
@@ -576,8 +574,6 @@
         const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
         const uint8_t *signature, size_t signatureSize) __INTRODUCED_IN(21);
 
-#endif /* __ANDROID_API__ >= 21 */
-
 __END_DECLS
 
 #endif //_NDK_MEDIA_DRM_H
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index a1cd9e3..e429820 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -50,8 +50,6 @@
 struct AMediaExtractor;
 typedef struct AMediaExtractor AMediaExtractor;
 
-#if __ANDROID_API__ >= 21
-
 /**
  * Create new media extractor.
  *
@@ -82,8 +80,6 @@
 media_status_t AMediaExtractor_setDataSource(AMediaExtractor*,
         const char *location) __INTRODUCED_IN(21);
 
-#if __ANDROID_API__ >= 28
-
 /**
  * Set the custom data source implementation from which the extractor will read.
  *
@@ -92,8 +88,6 @@
 media_status_t AMediaExtractor_setDataSourceCustom(AMediaExtractor*,
         AMediaDataSource *src) __INTRODUCED_IN(28);
 
-#endif /* __ANDROID_API__ >= 28 */
-
 /**
  * Return the number of tracks in the previously specified media file
  *
@@ -211,8 +205,6 @@
     AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED = 2,
 };
 
-#if __ANDROID_API__ >= 28
-
 /**
  * Returns the format of the extractor. The caller must free the returned format
  * using AMediaFormat_delete(format).
@@ -266,10 +258,6 @@
 media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex,
         AMediaFormat *fmt) __INTRODUCED_IN(28);
 
-#endif /* __ANDROID_API__ >= 28 */
-
-#endif /* __ANDROID_API__ >= 21 */
-
 __END_DECLS
 
 #endif // _NDK_MEDIA_EXTRACTOR_H
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 6371de4..fbc5553 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -61,8 +61,6 @@
 struct AMediaFormat;
 typedef struct AMediaFormat AMediaFormat;
 
-#if __ANDROID_API__ >= 21
-
 /**
  * Available since API level 21.
  */
@@ -205,9 +203,7 @@
 extern const char* AMEDIAFORMAT_KEY_TRACK_ID __INTRODUCED_IN(28);
 extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX __INTRODUCED_IN(28);
 extern const char* AMEDIAFORMAT_KEY_WIDTH __INTRODUCED_IN(21);
-#endif /* __ANDROID_API__ >= 21 */
 
-#if __ANDROID_API__ >= 28
 /**
  * Available since API level 28.
  */
@@ -231,9 +227,7 @@
  */
 void AMediaFormat_setRect(AMediaFormat*, const char* name,
         int32_t left, int32_t top, int32_t right, int32_t bottom) __INTRODUCED_IN(28);
-#endif /* __ANDROID_API__ >= 28 */
 
-#if __ANDROID_API__ >= 29
 /**
  * Remove all key/value pairs from the given AMediaFormat.
  *
@@ -307,9 +301,6 @@
 extern const char* AMEDIAFORMAT_KEY_VALID_SAMPLES __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_YEAR __INTRODUCED_IN(29);
 
-#endif /* __ANDROID_API__ >= 29 */
-
-#if __ANDROID_API__ >= 30
 /**
  * An optional key describing the low latency decoding mode. This is an optional parameter
  * that applies only to decoders. If enabled, the decoder doesn't hold input and output
@@ -320,7 +311,6 @@
  * Available since API level 30.
  */
 extern const char* AMEDIAFORMAT_KEY_LOW_LATENCY __INTRODUCED_IN(30);
-#endif /* __ANDROID_API__ >= 30 */
 
 __END_DECLS
 
diff --git a/media/ndk/include/media/NdkMediaMuxer.h b/media/ndk/include/media/NdkMediaMuxer.h
index 9de3fbf..519e249 100644
--- a/media/ndk/include/media/NdkMediaMuxer.h
+++ b/media/ndk/include/media/NdkMediaMuxer.h
@@ -54,8 +54,6 @@
     AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP   = 2,
 } OutputFormat;
 
-#if __ANDROID_API__ >= 21
-
 /**
  * Create new media muxer.
  *
@@ -140,8 +138,6 @@
         size_t trackIdx, const uint8_t *data,
         const AMediaCodecBufferInfo *info) __INTRODUCED_IN(21);
 
-#endif /* __ANDROID_API__ >= 21 */
-
 __END_DECLS
 
 #endif // _NDK_MEDIA_MUXER_H