Merge "aaudio_loopback: general cleanup, use dlsym"
diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp
index 38d9406..b012b5d 100644
--- a/media/extractors/mpeg2/Android.bp
+++ b/media/extractors/mpeg2/Android.bp
@@ -22,7 +22,6 @@
         "libhidlbase",
         "liblog",
         "libmediaextractor",
-        "libmediandk",
         "libstagefright_foundation",
         "libutils",
     ],
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index b611160..6b25302 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -104,7 +104,7 @@
     request.setUserId(getuid());
     request.setProcessId(getpid());
     request.setSharingModeMatchRequired(isSharingModeMatchRequired());
-    request.setInService(mInService);
+    request.setInService(isInService());
 
     request.getConfiguration().setDeviceId(getDeviceId());
     request.getConfiguration().setSampleRate(getSampleRate());
@@ -118,11 +118,24 @@
 
     request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
 
+    mDeviceChannelCount = getSamplesPerFrame(); // Assume it will be the same. Update if not.
+
     mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
+    if (mServiceStreamHandle < 0
+            && request.getConfiguration().getSamplesPerFrame() == 1 // mono?
+            && getDirection() == AAUDIO_DIRECTION_OUTPUT
+            && !isInService()) {
+        // if that failed then try switching from mono to stereo if OUTPUT.
+        // Only do this in the client. Otherwise we end up with a mono mixer in the service
+        // that writes to a stereo MMAP stream.
+        ALOGD("%s - openStream() returned %d, try switching from MONO to STEREO",
+              __func__, mServiceStreamHandle);
+        request.getConfiguration().setSamplesPerFrame(2); // stereo
+        mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
+    }
     if (mServiceStreamHandle < 0) {
-        result = mServiceStreamHandle;
-        ALOGE("%s - openStream() returned %d", __func__, result);
-        return result;
+        ALOGE("%s - openStream() returned %d", __func__, mServiceStreamHandle);
+        return mServiceStreamHandle;
     }
 
     result = configurationOutput.validate();
@@ -130,8 +143,12 @@
         goto error;
     }
     // Save results of the open.
+    if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
+        setSamplesPerFrame(configurationOutput.getSamplesPerFrame());
+    }
+    mDeviceChannelCount = configurationOutput.getSamplesPerFrame();
+
     setSampleRate(configurationOutput.getSampleRate());
-    setSamplesPerFrame(configurationOutput.getSamplesPerFrame());
     setDeviceId(configurationOutput.getDeviceId());
     setSessionId(configurationOutput.getSessionId());
     setSharingMode(configurationOutput.getSharingMode());
@@ -160,7 +177,6 @@
         goto error;
     }
 
-
     // Validate result from server.
     framesPerBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
     if (framesPerBurst < MIN_FRAMES_PER_BURST || framesPerBurst > MAX_FRAMES_PER_BURST) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 0f54f8c..0e0724b 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -138,7 +138,14 @@
     // Calculate timeout for an operation involving framesPerOperation.
     int64_t calculateReasonableTimeout(int32_t framesPerOperation);
 
-    aaudio_format_t          mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
+    aaudio_format_t getDeviceFormat() const { return mDeviceFormat; }
+
+    int32_t getDeviceChannelCount() const { return mDeviceChannelCount; }
+
+    /**
+     * @return true if running in audio service, versus in app process
+     */
+    bool isInService() const { return mInService; }
 
     IsochronousClockModel    mClockModel;      // timing model for chasing the HAL
 
@@ -187,6 +194,11 @@
     EndpointDescriptor       mEndpointDescriptor; // buffer description with resolved addresses
 
     int64_t                  mServiceLatencyNanos = 0;
+
+    // Sometimes the hardware is operating with a different format or channel count from the app.
+    // Then we require conversion in AAudio.
+    aaudio_format_t          mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
+    int32_t                  mDeviceChannelCount = 0;
 };
 
 } /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 62f0fc8..0719fe1 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -176,16 +176,16 @@
         int32_t numSamples = framesToProcess * getSamplesPerFrame();
 
         // TODO factor this out into a utility function
-        if (mDeviceFormat == getFormat()) {
+        if (getDeviceFormat() == getFormat()) {
             memcpy(destination, wrappingBuffer.data[partIndex], numBytes);
-        } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16
+        } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16
                    && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
             AAudioConvert_pcm16ToFloat(
                     (const int16_t *) wrappingBuffer.data[partIndex],
                     (float *) destination,
                     numSamples,
                     1.0f);
-        } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT
+        } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_FLOAT
                    && getFormat() == AAUDIO_FORMAT_PCM_I16) {
             AAudioConvert_floatToPcm16(
                     (const float *) wrappingBuffer.data[partIndex],
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index a0a0a54..11b43c3 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -206,7 +206,7 @@
     // ALOGD("AudioStreamInternal::writeNowWithConversion(%p, %d)",
     //              buffer, numFrames);
     WrappingBuffer wrappingBuffer;
-    uint8_t *source = (uint8_t *) buffer;
+    uint8_t *byteBuffer = (uint8_t *) buffer;
     int32_t framesLeft = numFrames;
 
     mAudioEndpoint.getEmptyFramesAvailable(&wrappingBuffer);
@@ -220,69 +220,26 @@
             if (framesToWrite > framesAvailable) {
                 framesToWrite = framesAvailable;
             }
+
             int32_t numBytes = getBytesPerFrame() * framesToWrite;
-            int32_t numSamples = framesToWrite * getSamplesPerFrame();
             // Data conversion.
             float levelFrom;
             float levelTo;
-            bool ramping = mVolumeRamp.nextSegment(framesToWrite, &levelFrom, &levelTo);
-            // The formats are validated when the stream is opened so we do not have to
-            // check for illegal combinations here.
-            // TODO factor this out into a utility function
-            if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
-                if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
-                    AAudio_linearRamp(
-                            (const float *) source,
-                            (float *) wrappingBuffer.data[partIndex],
-                            framesToWrite,
-                            getSamplesPerFrame(),
-                            levelFrom,
-                            levelTo);
-                } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
-                    if (ramping) {
-                        AAudioConvert_floatToPcm16(
-                                (const float *) source,
-                                (int16_t *) wrappingBuffer.data[partIndex],
-                                framesToWrite,
-                                getSamplesPerFrame(),
-                                levelFrom,
-                                levelTo);
-                    } else {
-                        AAudioConvert_floatToPcm16(
-                                (const float *) source,
-                                (int16_t *) wrappingBuffer.data[partIndex],
-                                numSamples,
-                                levelTo);
-                    }
-                }
-            } else if (getFormat() == AAUDIO_FORMAT_PCM_I16) {
-                if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
-                    if (ramping) {
-                        AAudioConvert_pcm16ToFloat(
-                                (const int16_t *) source,
-                                (float *) wrappingBuffer.data[partIndex],
-                                framesToWrite,
-                                getSamplesPerFrame(),
-                                levelFrom,
-                                levelTo);
-                    } else {
-                        AAudioConvert_pcm16ToFloat(
-                                (const int16_t *) source,
-                                (float *) wrappingBuffer.data[partIndex],
-                                numSamples,
-                                levelTo);
-                    }
-                } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
-                    AAudio_linearRamp(
-                            (const int16_t *) source,
-                            (int16_t *) wrappingBuffer.data[partIndex],
-                            framesToWrite,
-                            getSamplesPerFrame(),
-                            levelFrom,
-                            levelTo);
-                }
-            }
-            source += numBytes;
+            mVolumeRamp.nextSegment(framesToWrite, &levelFrom, &levelTo);
+
+            AAudioDataConverter::FormattedData source(
+                    (void *)byteBuffer,
+                    getFormat(),
+                    getSamplesPerFrame());
+            AAudioDataConverter::FormattedData destination(
+                    wrappingBuffer.data[partIndex],
+                    getDeviceFormat(),
+                    getDeviceChannelCount());
+
+            AAudioDataConverter::convert(source, destination, framesToWrite,
+                                         levelFrom, levelTo);
+
+            byteBuffer += numBytes;
             framesLeft -= framesToWrite;
         } else {
             break;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 854c691..40b31b9 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -27,6 +27,7 @@
 #include <aaudio/AAudioTesting.h>
 #include <math.h>
 #include <system/audio-base.h>
+#include <assert.h>
 
 #include "utility/AAudioUtilities.h"
 
@@ -72,7 +73,7 @@
                                 int16_t *destination,
                                 int32_t numSamples,
                                 float amplitude) {
-    float scaler = amplitude;
+    const float scaler = amplitude;
     for (int i = 0; i < numSamples; i++) {
         float sample = *source++;
         *destination++ = clipAndClampFloatToPcm16(sample, scaler);
@@ -103,7 +104,7 @@
                                 float *destination,
                                 int32_t numSamples,
                                 float amplitude) {
-    float scaler = amplitude / SHORT_SCALE;
+    const float scaler = amplitude / SHORT_SCALE;
     for (int i = 0; i < numSamples; i++) {
         destination[i] = source[i] * scaler;
     }
@@ -117,7 +118,7 @@
                                 float amplitude1,
                                 float amplitude2) {
     float scaler = amplitude1 / SHORT_SCALE;
-    float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+    const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
     for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
         for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
             *destination++ = *source++ * scaler;
@@ -134,7 +135,7 @@
                        float amplitude1,
                        float amplitude2) {
     float scaler = amplitude1;
-    float delta = (amplitude2 - amplitude1) / numFrames;
+    const float delta = (amplitude2 - amplitude1) / numFrames;
     for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
         for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
             float sample = *source++;
@@ -158,7 +159,7 @@
                        float amplitude2) {
     // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768.
     float scaler = amplitude1;
-    float delta = (amplitude2 - amplitude1) / numFrames;
+    const float delta = (amplitude2 - amplitude1) / numFrames;
     for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
         for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
             // No need to clip because int16_t range is inherently limited.
@@ -169,6 +170,255 @@
     }
 }
 
+// *************************************************************************************
+// Convert Mono To Stereo at the same time as converting format.
+void AAudioConvert_formatMonoToStereo(const float *source,
+                                      int16_t *destination,
+                                      int32_t numFrames,
+                                      float amplitude) {
+    const float scaler = amplitude;
+    for (int i = 0; i < numFrames; i++) {
+        float sample = *source++;
+        int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler);
+        *destination++ = sample16;
+        *destination++ = sample16;
+    }
+}
+
+void AAudioConvert_formatMonoToStereo(const float *source,
+                                      int16_t *destination,
+                                      int32_t numFrames,
+                                      float amplitude1,
+                                      float amplitude2) {
+    // divide by numFrames so that we almost reach amplitude2
+    const float delta = (amplitude2 - amplitude1) / numFrames;
+    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+        const float scaler = amplitude1 + (frameIndex * delta);
+        const float sample = *source++;
+        int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler);
+        *destination++ = sample16;
+        *destination++ = sample16;
+    }
+}
+
+void AAudioConvert_formatMonoToStereo(const int16_t *source,
+                                      float *destination,
+                                      int32_t numFrames,
+                                      float amplitude) {
+    const float scaler = amplitude / SHORT_SCALE;
+    for (int i = 0; i < numFrames; i++) {
+        float sample = source[i] * scaler;
+        *destination++ = sample;
+        *destination++ = sample;
+    }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudioConvert_formatMonoToStereo(const int16_t *source,
+                                      float *destination,
+                                      int32_t numFrames,
+                                      float amplitude1,
+                                      float amplitude2) {
+    const float scaler1 = amplitude1 / SHORT_SCALE;
+    const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+        float scaler = scaler1 + (frameIndex * delta);
+        float sample = source[frameIndex] * scaler;
+        *destination++ = sample;
+        *destination++ = sample;
+    }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRampMonoToStereo(const float *source,
+                                   float *destination,
+                                   int32_t numFrames,
+                                   float amplitude1,
+                                   float amplitude2) {
+    const float delta = (amplitude2 - amplitude1) / numFrames;
+    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+        float sample = *source++;
+
+        // Clip to valid range of a float sample to prevent excessive volume.
+        if (sample > MAX_HEADROOM) sample = MAX_HEADROOM;
+        else if (sample < MIN_HEADROOM) sample = MIN_HEADROOM;
+
+        const float scaler = amplitude1 + (frameIndex * delta);
+        float sampleScaled = sample * scaler;
+        *destination++ = sampleScaled;
+        *destination++ = sampleScaled;
+    }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRampMonoToStereo(const int16_t *source,
+                                   int16_t *destination,
+                                   int32_t numFrames,
+                                   float amplitude1,
+                                   float amplitude2) {
+    // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768.
+    const float delta = (amplitude2 - amplitude1) / numFrames;
+    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+        const float scaler = amplitude1 + (frameIndex * delta);
+        // No need to clip because int16_t range is inherently limited.
+        const float sample =  *source++ * scaler;
+        int16_t sample16 = (int16_t) roundf(sample);
+        *destination++ = sample16;
+        *destination++ = sample16;
+    }
+}
+
+// *************************************************************************************
+void AAudioDataConverter::convert(
+        const FormattedData &source,
+        const FormattedData &destination,
+        int32_t numFrames,
+        float levelFrom,
+        float levelTo) {
+
+    if (source.channelCount == 1 && destination.channelCount == 2) {
+        convertMonoToStereo(source,
+                            destination,
+                            numFrames,
+                            levelFrom,
+                            levelTo);
+    } else {
+        // We only support mono to stereo conversion. Otherwise source and destination
+        // must match.
+        assert(source.channelCount == destination.channelCount);
+        convertChannelsMatch(source,
+                             destination,
+                             numFrames,
+                             levelFrom,
+                             levelTo);
+    }
+}
+
+void AAudioDataConverter::convertMonoToStereo(
+        const FormattedData &source,
+        const FormattedData &destination,
+        int32_t numFrames,
+        float levelFrom,
+        float levelTo) {
+
+    // The formats are validated when the stream is opened so we do not have to
+    // check for illegal combinations here.
+    if (source.format == AAUDIO_FORMAT_PCM_FLOAT) {
+        if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
+            AAudio_linearRampMonoToStereo(
+                    (const float *) source.data,
+                    (float *) destination.data,
+                    numFrames,
+                    levelFrom,
+                    levelTo);
+        } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
+            if (levelFrom != levelTo) {
+                AAudioConvert_formatMonoToStereo(
+                        (const float *) source.data,
+                        (int16_t *) destination.data,
+                        numFrames,
+                        levelFrom,
+                        levelTo);
+            } else {
+                AAudioConvert_formatMonoToStereo(
+                        (const float *) source.data,
+                        (int16_t *) destination.data,
+                        numFrames,
+                        levelTo);
+            }
+        }
+    } else if (source.format == AAUDIO_FORMAT_PCM_I16) {
+        if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
+            if (levelFrom != levelTo) {
+                AAudioConvert_formatMonoToStereo(
+                        (const int16_t *) source.data,
+                        (float *) destination.data,
+                        numFrames,
+                        levelFrom,
+                        levelTo);
+            } else {
+                AAudioConvert_formatMonoToStereo(
+                        (const int16_t *) source.data,
+                        (float *) destination.data,
+                        numFrames,
+                        levelTo);
+            }
+        } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
+            AAudio_linearRampMonoToStereo(
+                    (const int16_t *) source.data,
+                    (int16_t *) destination.data,
+                    numFrames,
+                    levelFrom,
+                    levelTo);
+        }
+    }
+}
+
+void AAudioDataConverter::convertChannelsMatch(
+        const FormattedData &source,
+        const FormattedData &destination,
+        int32_t numFrames,
+        float levelFrom,
+        float levelTo) {
+    const int32_t numSamples = numFrames * source.channelCount;
+
+    // The formats are validated when the stream is opened so we do not have to
+    // check for illegal combinations here.
+    if (source.format == AAUDIO_FORMAT_PCM_FLOAT) {
+        if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
+            AAudio_linearRamp(
+                    (const float *) source.data,
+                    (float *) destination.data,
+                    numFrames,
+                    source.channelCount,
+                    levelFrom,
+                    levelTo);
+        } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
+            if (levelFrom != levelTo) {
+                AAudioConvert_floatToPcm16(
+                        (const float *) source.data,
+                        (int16_t *) destination.data,
+                        numFrames,
+                        source.channelCount,
+                        levelFrom,
+                        levelTo);
+            } else {
+                AAudioConvert_floatToPcm16(
+                        (const float *) source.data,
+                        (int16_t *) destination.data,
+                        numSamples,
+                        levelTo);
+            }
+        }
+    } else if (source.format == AAUDIO_FORMAT_PCM_I16) {
+        if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
+            if (levelFrom != levelTo) {
+                AAudioConvert_pcm16ToFloat(
+                        (const int16_t *) source.data,
+                        (float *) destination.data,
+                        numFrames,
+                        source.channelCount,
+                        levelFrom,
+                        levelTo);
+            } else {
+                AAudioConvert_pcm16ToFloat(
+                        (const int16_t *) source.data,
+                        (float *) destination.data,
+                        numSamples,
+                        levelTo);
+            }
+        } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
+            AAudio_linearRamp(
+                    (const int16_t *) source.data,
+                    (int16_t *) destination.data,
+                    numFrames,
+                    source.channelCount,
+                    levelFrom,
+                    levelTo);
+        }
+    }
+}
+
 status_t AAudioConvert_aaudioToAndroidStatus(aaudio_result_t result) {
     // This covers the case for AAUDIO_OK and for positive results.
     if (result >= 0) {
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index dc6a671..cea88fb 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -159,6 +159,41 @@
                        float amplitude1,
                        float amplitude2);
 
+class AAudioDataConverter {
+public:
+
+    struct FormattedData {
+
+        FormattedData(void *data, aaudio_format_t format, int32_t channelCount)
+            : data(data)
+            , format(format)
+            , channelCount(channelCount) {}
+
+        const void            *data = nullptr;
+        const aaudio_format_t  format = AAUDIO_FORMAT_UNSPECIFIED;
+        const int32_t          channelCount = 1;
+    };
+
+    static void convert(const FormattedData &source,
+                        const FormattedData &destination,
+                        int32_t numFrames,
+                        float levelFrom,
+                        float levelTo);
+
+private:
+    static void convertMonoToStereo(const FormattedData &source,
+                                    const FormattedData &destination,
+                                    int32_t numFrames,
+                                    float levelFrom,
+                                    float levelTo);
+
+    static void convertChannelsMatch(const FormattedData &source,
+                                     const FormattedData &destination,
+                                     int32_t numFrames,
+                                     float levelFrom,
+                                     float levelTo);
+};
+
 /**
  * Calculate the number of bytes and prevent numeric overflow.
  * @param numFrames frame count
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 3b1b413..5e47b48 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -111,10 +111,6 @@
 static status_t translateErrorCode(media_status_t err) {
     if (err == AMEDIA_OK) {
         return OK;
-    } else if (err == AMEDIA_ERROR_END_OF_STREAM) {
-        return ERROR_END_OF_STREAM;
-    } else if (err == AMEDIA_ERROR_IO) {
-        return ERROR_IO;
     } else if (err == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
         return -EAGAIN;
     }
@@ -1154,15 +1150,6 @@
     return AMediaExtractor_getSampleTime(mAMediaExtractor);
 }
 
-status_t AMediaExtractorWrapper::getSampleFormat(sp<AMediaFormatWrapper> &formatWrapper) {
-    if (mAMediaExtractor == NULL) {
-        return DEAD_OBJECT;
-    }
-    AMediaFormat *format = AMediaFormat_new();
-    formatWrapper = new AMediaFormatWrapper(format);
-    return translateErrorCode(AMediaExtractor_getSampleFormat(mAMediaExtractor, format));
-}
-
 int64_t AMediaExtractorWrapper::getCachedDuration() {
     if (mAMediaExtractor == NULL) {
         return -1;
diff --git a/media/libmedia/include/media/NdkWrapper.h b/media/libmedia/include/media/NdkWrapper.h
index f17d2cc..b71b758 100644
--- a/media/libmedia/include/media/NdkWrapper.h
+++ b/media/libmedia/include/media/NdkWrapper.h
@@ -313,8 +313,6 @@
 
     int64_t getSampleTime();
 
-    status_t getSampleFormat(sp<AMediaFormatWrapper> &formatWrapper);
-
     int64_t getCachedDuration();
 
     bool advance();
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index 936fb20..790581a 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -1239,7 +1239,7 @@
     }
 
     if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
-        meta->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSubtitleTrack.mIndex);
+        meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
     }
 
     uint32_t dataType; // unused
@@ -1255,7 +1255,7 @@
     if (mb->meta_data().findData(
             kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
         sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
-        meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
+        meta->setBuffer("mpegUserData", mpegUserData);
     }
 
     mb->release();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 060b698..5971a8b 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -2842,7 +2842,7 @@
 void NuPlayer2::sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex) {
     int32_t trackIndex;
     int64_t timeUs, durationUs;
-    CHECK(buffer->meta()->findInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, &trackIndex));
+    CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex));
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
     CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
 
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
index e48e388..e4afd5b 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
@@ -21,7 +21,6 @@
 
 #include "NuPlayer2CCDecoder.h"
 
-#include <media/NdkMediaFormat.h>
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -302,7 +301,7 @@
 // returns true if a new CC track is found
 bool NuPlayer2::CCDecoder::extractFromMPEGUserData(const sp<ABuffer> &accessUnit) {
     sp<ABuffer> mpegUserData;
-    if (!accessUnit->meta()->findBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, &mpegUserData)
+    if (!accessUnit->meta()->findBuffer("mpegUserData", &mpegUserData)
             || mpegUserData == NULL) {
         return false;
     }
@@ -539,7 +538,7 @@
         dumpBytePair(ccBuf);
 #endif
 
-        ccBuf->meta()->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSelectedTrack);
+        ccBuf->meta()->setInt32("trackIndex", mSelectedTrack);
         ccBuf->meta()->setInt64("timeUs", timeUs);
         ccBuf->meta()->setInt64("durationUs", 0ll);
 
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index 2b5fa71..a37973b 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -23,7 +23,6 @@
         "libhidlmemory",
         "liblog",
         "libmedia",
-        "libmediandk",
         "libmedia_omx",
         "libmediaextractor",
         "libmediadrm",
diff --git a/media/libmediaplayerservice/nuplayer/Android.bp b/media/libmediaplayerservice/nuplayer/Android.bp
index 58ab8fc..645bb7a 100644
--- a/media/libmediaplayerservice/nuplayer/Android.bp
+++ b/media/libmediaplayerservice/nuplayer/Android.bp
@@ -28,7 +28,6 @@
         "frameworks/av/media/libstagefright/mpeg2ts",
         "frameworks/av/media/libstagefright/rtsp",
         "frameworks/av/media/libstagefright/timedtext",
-        "frameworks/av/media/ndk/include",
     ],
 
     cflags: [
@@ -49,7 +48,6 @@
         "libui",
         "libgui",
         "libmedia",
-        "libmediandk",
         "libmediadrm",
         "libpowermanager",
     ],
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index d86b08d..e7c3deb 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -28,7 +28,6 @@
 #include <media/MediaExtractor.h>
 #include <media/MediaSource.h>
 #include <media/IMediaExtractorService.h>
-#include <media/NdkMediaFormat.h>
 #include <media/IMediaHTTPService.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -1234,7 +1233,7 @@
     }
 
     if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
-        meta->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSubtitleTrack.mIndex);
+        meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
     }
 
     uint32_t dataType; // unused
@@ -1250,7 +1249,7 @@
     if (mb->meta_data().findData(
             kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
         sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
-        meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
+        meta->setBuffer("mpegUserData", mpegUserData);
     }
 
     mb->release();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d252150..dce3e0a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -43,7 +43,6 @@
 #include <media/AudioResamplerPublic.h>
 #include <media/AVSyncSettings.h>
 #include <media/MediaCodecBuffer.h>
-#include <media/NdkMediaFormat.h>
 
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -2703,7 +2702,7 @@
 void NuPlayer::sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex) {
     int32_t trackIndex;
     int64_t timeUs, durationUs;
-    CHECK(buffer->meta()->findInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, &trackIndex));
+    CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex));
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
     CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index 4ada953..0a8b97f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -21,7 +21,6 @@
 
 #include "NuPlayerCCDecoder.h"
 
-#include <media/NdkMediaFormat.h>
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -302,7 +301,7 @@
 // returns true if a new CC track is found
 bool NuPlayer::CCDecoder::extractFromMPEGUserData(const sp<ABuffer> &accessUnit) {
     sp<ABuffer> mpegUserData;
-    if (!accessUnit->meta()->findBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, &mpegUserData)
+    if (!accessUnit->meta()->findBuffer("mpegUserData", &mpegUserData)
             || mpegUserData == NULL) {
         return false;
     }
@@ -539,7 +538,7 @@
         dumpBytePair(ccBuf);
 #endif
 
-        ccBuf->meta()->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSelectedTrack);
+        ccBuf->meta()->setInt32("trackIndex", mSelectedTrack);
         ccBuf->meta()->setInt64("timeUs", timeUs);
         ccBuf->meta()->setInt64("durationUs", 0ll);
 
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index f6fc813..540cf8c 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -446,17 +446,12 @@
 ssize_t NuMediaExtractor::fetchAllTrackSamples(
         int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
     TrackInfo *minInfo = NULL;
-    ssize_t minIndex = ERROR_END_OF_STREAM;
+    ssize_t minIndex = -1;
 
     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
         TrackInfo *info = &mSelectedTracks.editItemAt(i);
         fetchTrackSamples(info, seekTimeUs, mode);
 
-        status_t err = info->mFinalResult;
-        if (err != OK && err != ERROR_END_OF_STREAM) {
-            return err;
-        }
-
         if (info->mSamples.empty()) {
             continue;
         }
@@ -726,8 +721,7 @@
     ssize_t minIndex = fetchAllTrackSamples();
 
     if (minIndex < 0) {
-        status_t err = minIndex;
-        return err;
+        return ERROR_END_OF_STREAM;
     }
 
     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
diff --git a/media/libstagefright/codec2/client/Android.bp b/media/libstagefright/codec2/client/Android.bp
new file mode 100644
index 0000000..0129e15
--- /dev/null
+++ b/media/libstagefright/codec2/client/Android.bp
@@ -0,0 +1,30 @@
+cc_library {
+    name: "libstagefright_codec2_client",
+
+    srcs: [
+        "client.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.media.bufferpool@1.0",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_codec2_hidl@1.0",
+        "libutils",
+        "vendor.google.media.c2@1.0",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "libstagefright_codec2",
+    ],
+
+}
+
diff --git a/media/libstagefright/codec2/client/client.cpp b/media/libstagefright/codec2/client/client.cpp
new file mode 100644
index 0000000..5a176dc
--- /dev/null
+++ b/media/libstagefright/codec2/client/client.cpp
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2Client-interfaces"
+#include <log/log.h>
+
+#include <media/stagefright/codec2/client.h>
+
+#include <codec2/hidl/1.0/types.h>
+
+#include <vendor/google/media/c2/1.0/IComponentListener.h>
+#include <vendor/google/media/c2/1.0/IConfigurable.h>
+#include <vendor/google/media/c2/1.0/IComponentInterface.h>
+#include <vendor/google/media/c2/1.0/IComponent.h>
+#include <vendor/google/media/c2/1.0/IComponentStore.h>
+
+#include <hidl/HidlSupport.h>
+
+#include <limits>
+#include <type_traits>
+
+namespace /* unnamed */ {
+
+// TODO: Find the appropriate error code for this
+constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;
+
+} // unnamed namespace
+
+namespace android {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using namespace ::vendor::google::media::c2::V1_0;
+using namespace ::vendor::google::media::c2::V1_0::implementation;
+
+// Codec2ConfigurableClient
+
+const C2String& Codec2ConfigurableClient::getName() const {
+    return mName;
+}
+
+Codec2ConfigurableClient::Base* Codec2ConfigurableClient::base() const {
+    return static_cast<Base*>(mBase.get());
+}
+
+Codec2ConfigurableClient::Codec2ConfigurableClient(
+        const sp<Codec2ConfigurableClient::Base>& base) : mBase(base) {
+    Return<void> transStatus = base->getName(
+            [this](const hidl_string& name) {
+                mName = name.c_str();
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("Cannot obtain name from IConfigurable.");
+    }
+}
+
+c2_status_t Codec2ConfigurableClient::query(
+        const std::vector<C2Param::Index> &indices,
+        c2_blocking_t mayBlock,
+        std::vector<std::unique_ptr<C2Param>>* const params) const {
+    hidl_vec<ParamIndex> hidlIndices(indices.size());
+    size_t i = 0;
+    for (const C2Param::Index& index : indices) {
+        hidlIndices[i++] = static_cast<ParamIndex>(index.operator uint32_t());
+    }
+    c2_status_t status;
+    Return<void> transStatus = base()->query(
+            hidlIndices,
+            mayBlock == C2_MAY_BLOCK,
+            [&status, params](Status s, const Params& p) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                status = copyParamsFromBlob(params, p);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("query -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2ConfigurableClient::config(
+        const std::vector<C2Param*> &params,
+        c2_blocking_t mayBlock,
+        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+    Params hidlParams;
+    Status hidlStatus = createParamsBlob(&hidlParams, params);
+    if (hidlStatus != Status::OK) {
+        ALOGE("config -- bad input.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status;
+    Return<void> transStatus = base()->config(
+            hidlParams,
+            mayBlock == C2_MAY_BLOCK,
+            [&status, &params, failures](
+                    Status s,
+                    const hidl_vec<SettingResult> f,
+                    const Params& o) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                failures->clear();
+                failures->resize(f.size());
+                size_t i = 0;
+                for (const SettingResult& sf : f) {
+                    status = objcpy(&(*failures)[i++], sf);
+                    if (status != C2_OK) {
+                        return;
+                    }
+                }
+                status = updateParamsFromBlob(params, o);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("config -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2ConfigurableClient::querySupportedParams(
+        std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const {
+    // TODO: Cache and query properly!
+    c2_status_t status;
+    Return<void> transStatus = base()->querySupportedParams(
+            std::numeric_limits<uint32_t>::min(),
+            std::numeric_limits<uint32_t>::max(),
+            [&status, params](
+                    Status s,
+                    const hidl_vec<ParamDescriptor>& p) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                params->resize(p.size());
+                size_t i = 0;
+                for (const ParamDescriptor& sp : p) {
+                    status = objcpy(&(*params)[i++], sp);
+                    if (status != C2_OK) {
+                        return;
+                    }
+                }
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("querySupportedParams -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2ConfigurableClient::querySupportedValues(
+        std::vector<C2FieldSupportedValuesQuery>& fields,
+        c2_blocking_t mayBlock) const {
+    hidl_vec<FieldSupportedValuesQuery> inFields(fields.size());
+    for (size_t i = 0; i < fields.size(); ++i) {
+        Status hidlStatus = objcpy(&inFields[i], fields[i]);
+        if (hidlStatus != Status::OK) {
+            ALOGE("querySupportedValues -- bad input");
+            return C2_TRANSACTION_FAILED;
+        }
+    }
+
+    c2_status_t status;
+    Return<void> transStatus = base()->querySupportedValues(
+            inFields,
+            mayBlock == C2_MAY_BLOCK,
+            [&status, &inFields, &fields](
+                    Status s,
+                    const hidl_vec<FieldSupportedValuesQueryResult>& r) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                if (r.size() != fields.size()) {
+                    ALOGE("querySupportedValues -- input and output lists "
+                            "have different sizes.");
+                    status = C2_CORRUPTED;
+                    return;
+                }
+                for (size_t i = 0; i < fields.size(); ++i) {
+                    status = objcpy(&fields[i], inFields[i], r[i]);
+                    if (status != C2_OK) {
+                        return;
+                    }
+                }
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("querySupportedValues -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+// Codec2Client
+
+Codec2Client::Base* Codec2Client::base() const {
+    return static_cast<Base*>(mBase.get());
+}
+
+Codec2Client::Codec2Client(const sp<Codec2Client::Base>& base) :
+        Codec2ConfigurableClient(base), mListed(false) {
+}
+
+c2_status_t Codec2Client::createComponent(
+        const C2String& name,
+        const std::shared_ptr<Codec2Client::Listener>& listener,
+        std::shared_ptr<Codec2Client::Component>* const component) {
+
+    // TODO: Add support for Bufferpool
+
+    struct HidlListener : public IComponentListener {
+        std::shared_ptr<Codec2Client::Listener> base;
+        std::weak_ptr<Codec2Client::Component> component;
+
+        virtual Return<void> onWorkDone(const WorkBundle& workBundle) override {
+            std::list<std::unique_ptr<C2Work>> workItems;
+            c2_status_t status = objcpy(&workItems, workBundle);
+            if (status != C2_OK) {
+                ALOGE("onWorkDone -- received corrupted WorkBundle. "
+                        "Error code: %d", static_cast<int>(status));
+                return Void();
+            }
+            base->onWorkDone(component, workItems);
+            return Void();
+        }
+
+        virtual Return<void> onTripped(
+                const hidl_vec<SettingResult>& settingResults) override {
+            std::vector<std::shared_ptr<C2SettingResult>> c2SettingResults(
+                    settingResults.size());
+            c2_status_t status;
+            for (size_t i = 0; i < settingResults.size(); ++i) {
+                std::unique_ptr<C2SettingResult> c2SettingResult;
+                status = objcpy(&c2SettingResult, settingResults[i]);
+                if (status != C2_OK) {
+                    ALOGE("onTripped -- received corrupted SettingResult. "
+                            "Error code: %d", static_cast<int>(status));
+                    return Void();
+                }
+                c2SettingResults[i] = std::move(c2SettingResult);
+            }
+            base->onTripped(component, c2SettingResults);
+            return Void();
+        }
+
+        virtual Return<void> onError(Status s, uint32_t errorCode) override {
+            base->onError(component, s == Status::OK ?
+                    errorCode : static_cast<c2_status_t>(s));
+            return Void();
+        }
+    };
+
+    c2_status_t status;
+    sp<HidlListener> hidlListener = new HidlListener();
+    hidlListener->base = listener;
+    Return<void> transStatus = base()->createComponent(
+            name,
+            hidlListener,
+            nullptr,
+            [&status, component](
+                    Status s,
+                    const sp<IComponent>& c) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                *component = std::make_shared<Codec2Client::Component>(c);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("createComponent -- failed transaction.");
+        return C2_TRANSACTION_FAILED;
+    }
+    if (status != C2_OK) {
+        ALOGE("createComponent -- failed to create component.");
+        return status;
+    }
+    hidlListener->component = *component;
+    return status;
+}
+
+c2_status_t Codec2Client::createInterface(
+        const C2String& name,
+        std::shared_ptr<Codec2Client::Interface>* const interface) {
+    c2_status_t status;
+    Return<void> transStatus = base()->createInterface(
+            name,
+            [&status, interface](
+                    Status s,
+                    const sp<IComponentInterface>& i) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                *interface = std::make_shared<Codec2Client::Interface>(i);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("createInterface -- failed transaction.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+const std::vector<C2Component::Traits>& Codec2Client::listComponents()
+        const {
+    if (mListed) {
+        return mTraitsList;
+    }
+    Return<void> transStatus = base()->listComponents(
+            [this](const hidl_vec<IComponentStore::ComponentTraits>& t) {
+                mTraitsList.resize(t.size());
+                mAliasesBuffer.resize(t.size());
+                for (size_t i = 0; i < t.size(); ++i) {
+                    c2_status_t status = objcpy(
+                            &mTraitsList[i], &mAliasesBuffer[i], t[i]);
+                    if (status != C2_OK) {
+                        ALOGE("listComponents -- corrupted output.");
+                        return;
+                    }
+                }
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("listComponents -- failed transaction.");
+    }
+    mListed = true;
+    return mTraitsList;
+}
+
+c2_status_t Codec2Client::copyBuffer(
+        const std::shared_ptr<C2Buffer>& src,
+        const std::shared_ptr<C2Buffer>& dst) {
+    // TODO: Implement?
+    (void)src;
+    (void)dst;
+    ALOGE("copyBuffer not implemented");
+    return C2_OMITTED;
+}
+
+std::shared_ptr<C2ParamReflector>
+        Codec2Client::getParamReflector() {
+    // TODO: Implement this once there is a way to construct C2StructDescriptor
+    // dynamically.
+    ALOGE("getParamReflector -- not implemented.");
+    return nullptr;
+}
+
+std::shared_ptr<Codec2Client> Codec2Client::CreateFromService(
+        const char* instanceName, bool waitForService) {
+    sp<Base> baseStore = waitForService ?
+            Base::getService(instanceName) :
+            Base::tryGetService(instanceName);
+    if (!baseStore) {
+        if (waitForService) {
+            ALOGE("Codec2.0 service inaccessible. Check the device manifest.");
+        } else {
+            ALOGW("Codec2.0 service not available right now. Try again later.");
+        }
+        return nullptr;
+    }
+    return std::make_shared<Codec2Client>(baseStore);
+}
+
+// Codec2Client::Listener
+
+Codec2Client::Listener::~Listener() {
+}
+
+// Codec2Client::Component
+
+Codec2Client::Component::Base* Codec2Client::Component::base() const {
+    return static_cast<Base*>(mBase.get());
+}
+
+Codec2Client::Component::Component(const sp<Codec2Client::Component::Base>& base) :
+        Codec2Client::Configurable(base) {
+}
+
+c2_status_t Codec2Client::Component::createBlockPool(
+        C2Allocator::id_t id,
+        C2BlockPool::local_id_t* localId,
+        std::shared_ptr<Codec2Client::Configurable>* configurable) {
+    c2_status_t status;
+    Return<void> transStatus = base()->createBlockPool(
+            static_cast<uint32_t>(id),
+            [&status, localId, configurable](
+                    Status s,
+                    uint64_t pId,
+                    const sp<IConfigurable>& c) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                *localId = static_cast<C2BlockPool::local_id_t>(pId);
+                *configurable = std::make_shared<Codec2Client::Configurable>(c);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("createBlockPool -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::Component::queue(
+        std::list<std::unique_ptr<C2Work>>* const items) {
+    WorkBundle workBundle;
+    Status hidlStatus = objcpy(&workBundle, *items);
+    if (hidlStatus != Status::OK) {
+        ALOGE("queue -- bad input.");
+        return C2_TRANSACTION_FAILED;
+    }
+    Return<Status> transStatus = base()->queue(workBundle);
+    if (!transStatus.isOk()) {
+        ALOGE("queue -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::flush(
+        C2Component::flush_mode_t mode,
+        std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+    (void)mode; // Flush mode isn't supported in HIDL yet.
+    c2_status_t status;
+    Return<void> transStatus = base()->flush(
+            [&status, flushedWork](
+                    Status s, const WorkBundle& wb) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                status = objcpy(flushedWork, wb);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("flush -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) {
+    Return<Status> transStatus = base()->drain(
+            mode == C2Component::DRAIN_COMPONENT_WITH_EOS);
+    if (!transStatus.isOk()) {
+        ALOGE("drain -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::start() {
+    Return<Status> transStatus = base()->start();
+    if (!transStatus.isOk()) {
+        ALOGE("start -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::stop() {
+    Return<Status> transStatus = base()->stop();
+    if (!transStatus.isOk()) {
+        ALOGE("stop -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::reset() {
+    Return<Status> transStatus = base()->reset();
+    if (!transStatus.isOk()) {
+        ALOGE("reset -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::release() {
+    Return<Status> transStatus = base()->release();
+    if (!transStatus.isOk()) {
+        ALOGE("release -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+
+
+
+}  // namespace android
+
diff --git a/media/libstagefright/codec2/client/include/media/stagefright/codec2/client.h b/media/libstagefright/codec2/client/include/media/stagefright/codec2/client.h
new file mode 100644
index 0000000..1bbf459
--- /dev/null
+++ b/media/libstagefright/codec2/client/include/media/stagefright/codec2/client.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 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 CODEC2_CLIENT_INTERFACES_H_
+#define CODEC2_CLIENT_INTERFACES_H_
+
+#include <C2Component.h>
+#include <C2Buffer.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <utils/StrongPointer.h>
+
+#include <memory>
+
+/**
+ * This file contains minimal interfaces for the framework to access Codec2.0.
+ *
+ * Codec2Client is the main class that contains the following inner classes:
+ * - Listener
+ * - Configurable
+ * - Interface
+ * - Component
+ *
+ * Classes in Codec2Client, interfaces in Codec2.0, and  HIDL interfaces are
+ * related as follows:
+ * - Codec2Client <==> C2ComponentStore <==> IComponentStore
+ * - Codec2Client::Listener <==> C2Component::Listener <==> IComponentListener
+ * - Codec2Client::Configurable <==> [No equivalent] <==> IConfigurable
+ * - Codec2Client::Interface <==> C2ComponentInterface <==> IComponentInterface
+ * - Codec2Client::Component <==> C2Component <==> IComponent
+ *
+ * The entry point is Codec2Client::CreateFromService(), which creates a
+ * Codec2Client object. From Codec2Client, Interface and Component objects can
+ * be created by calling createComponent() and createInterface().
+ *
+ * createComponent() takes a Listener object, which must be implemented by the
+ * user.
+ *
+ * At the present, createBlockPool() is the only method that yields a
+ * Configurable object. Note, however, that Interface, Component and
+ * Codec2Client are all subclasses of Configurable.
+ */
+
+// Forward declaration of HIDL interfaces
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+struct IConfigurable;
+struct IComponentInterface;
+struct IComponent;
+struct IComponentStore;
+} // namespace V1_0
+} // namespace c2
+} // namespace media
+} // namespace google
+} // namespace vendor
+
+namespace android {
+
+// This class is supposed to be called Codec2Client::Configurable, but forward
+// declaration of an inner class is not possible.
+struct Codec2ConfigurableClient {
+
+    typedef ::vendor::google::media::c2::V1_0::IConfigurable Base;
+
+    const C2String& getName() const;
+
+    c2_status_t query(
+            const std::vector<C2Param::Index> &indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const params) const;
+
+    c2_status_t config(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures
+            );
+
+    c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+            ) const;
+
+    c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery>& fields,
+            c2_blocking_t mayBlock) const;
+
+    // base cannot be null.
+    Codec2ConfigurableClient(const sp<Base>& base);
+
+protected:
+    C2String mName;
+    sp<Base> mBase;
+
+    Base* base() const;
+
+    friend struct Codec2Client;
+};
+
+struct Codec2Client : public Codec2ConfigurableClient {
+
+    typedef ::vendor::google::media::c2::V1_0::IComponentStore Base;
+
+    struct Listener;
+
+    typedef Codec2ConfigurableClient Configurable;
+
+    typedef Configurable Interface; // These two types may diverge in the future.
+
+    struct Component;
+
+    typedef Codec2Client Store;
+
+    c2_status_t createComponent(
+            const C2String& name,
+            const std::shared_ptr<Listener>& listener,
+            std::shared_ptr<Component>* const component);
+
+    c2_status_t createInterface(
+            const C2String& name,
+            std::shared_ptr<Interface>* const interface);
+
+    const std::vector<C2Component::Traits>&
+            listComponents() const;
+
+    c2_status_t copyBuffer(
+            const std::shared_ptr<C2Buffer>& src,
+            const std::shared_ptr<C2Buffer>& dst);
+
+    std::shared_ptr<C2ParamReflector> getParamReflector();
+
+    static std::shared_ptr<Codec2Client> CreateFromService(
+            const char* instanceName,
+            bool waitForService = true);
+
+    // base cannot be null.
+    Codec2Client(const sp<Base>& base);
+
+protected:
+    mutable bool mListed;
+    mutable std::vector<C2Component::Traits> mTraitsList;
+    mutable std::vector<std::unique_ptr<std::vector<std::string>>>
+            mAliasesBuffer;
+
+    Base* base() const;
+};
+
+struct Codec2Client::Listener {
+
+    virtual void onWorkDone(
+            const std::weak_ptr<Codec2Client::Component>& comp,
+            const std::list<std::unique_ptr<C2Work>>& workItems) = 0;
+
+    virtual void onTripped(
+            const std::weak_ptr<Codec2Client::Component>& comp,
+            const std::vector<std::shared_ptr<C2SettingResult>>& settingResults
+            ) = 0;
+
+    virtual void onError(
+            const std::weak_ptr<Codec2Client::Component>& comp,
+            uint32_t errorCode) = 0;
+
+    virtual ~Listener();
+
+};
+
+struct Codec2Client::Component : public Codec2Client::Configurable {
+
+    typedef ::vendor::google::media::c2::V1_0::IComponent Base;
+
+    c2_status_t createBlockPool(
+            C2Allocator::id_t id,
+            C2BlockPool::local_id_t* localId,
+            std::shared_ptr<Codec2Client::Configurable>* configurable);
+
+    c2_status_t queue(
+            std::list<std::unique_ptr<C2Work>>* const items);
+
+    c2_status_t flush(
+            C2Component::flush_mode_t mode,
+            std::list<std::unique_ptr<C2Work>>* const flushedWork);
+
+    c2_status_t drain(C2Component::drain_mode_t mode);
+
+    c2_status_t start();
+
+    c2_status_t stop();
+
+    c2_status_t reset();
+
+    c2_status_t release();
+
+    // base cannot be null.
+    Component(const sp<Base>& base);
+
+protected:
+    Base* base() const;
+
+    friend struct Codec2Client;
+};
+
+}  // namespace android
+
+#endif  // CODEC2_CLIENT_INTERFACES_H_
+
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/Android.bp b/media/libstagefright/codec2/hidl/impl/1.0/Android.bp
new file mode 100644
index 0000000..3d930c6
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/Android.bp
@@ -0,0 +1,42 @@
+cc_library {
+    name: "libstagefright_codec2_hidl@1.0",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+
+    defaults: ["hidl_defaults"],
+
+    srcs: [
+        "Component.cpp",
+        "ComponentStore.cpp",
+        "Configurable.cpp",
+        "types.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.media.bufferpool@1.0",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libutils",
+        "vendor.google.media.c2@1.0",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "libstagefright_codec2",
+    ],
+
+    // Private include directories
+    header_libs: [
+        "libstagefright_codec2_internal",
+    ],
+}
+
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/Component.cpp b/media/libstagefright/codec2/hidl/impl/1.0/Component.cpp
new file mode 100644
index 0000000..2b34fde
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/Component.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2-Component"
+#include <log/log.h>
+
+#include <codec2/hidl/1.0/Component.h>
+#include <codec2/hidl/1.0/types.h>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using namespace ::android;
+
+// Implementation of ConfigurableC2Intf based on C2ComponentInterface
+struct CompIntf : public ConfigurableC2Intf {
+    CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
+        ConfigurableC2Intf(intf->getName()),
+        mIntf(intf) {
+    }
+
+    virtual c2_status_t config(
+            const std::vector<C2Param*>& params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures
+            ) override {
+        return mIntf->config_vb(params, mayBlock, failures);
+    }
+
+    virtual c2_status_t query(
+            const std::vector<C2Param::Index>& indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const params) const override {
+        return mIntf->query_vb({}, indices, mayBlock, params);
+    }
+
+    virtual c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+            ) const override {
+        return mIntf->querySupportedParams_nb(params);
+    }
+
+    virtual c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery>& fields,
+            c2_blocking_t mayBlock) const override {
+        return mIntf->querySupportedValues_vb(fields, mayBlock);
+    }
+
+protected:
+    std::shared_ptr<C2ComponentInterface> mIntf;
+};
+
+// ComponentInterface
+ComponentInterface::ComponentInterface(
+        const std::shared_ptr<C2ComponentInterface>& intf,
+        const sp<ComponentStore>& store) :
+    Configurable(new CachedConfigurable(std::make_unique<CompIntf>(intf))),
+    mInterface(intf) {
+    mInit = init(store.get());
+}
+
+c2_status_t ComponentInterface::status() const {
+    return mInit;
+}
+
+// ComponentListener wrapper
+struct Listener : public C2Component::Listener {
+    Listener(const wp<IComponentListener>& listener) : mListener(listener) {
+        // TODO: Should we track interface errors? We could reuse onError() or
+        // create our own error channel.
+    }
+
+    virtual void onError_nb(
+            std::weak_ptr<C2Component> /* c2component */,
+            uint32_t errorCode) override {
+        sp<IComponentListener> listener = mListener.promote();
+        if (listener) {
+            listener->onError(Status::OK, errorCode);
+        }
+    }
+
+    virtual void onTripped_nb(
+            std::weak_ptr<C2Component> /* c2component */,
+            std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+            ) override {
+        sp<IComponentListener> listener = mListener.promote();
+        if (listener) {
+            hidl_vec<SettingResult> settingResults(c2settingResult.size());
+            size_t ix = 0;
+            for (const std::shared_ptr<C2SettingResult> &c2result :
+                    c2settingResult) {
+                if (c2result) {
+                    if (objcpy(&settingResults[ix++], *c2result) != Status::OK) {
+                        break;
+                    }
+                }
+            }
+            settingResults.resize(ix);
+            listener->onTripped(settingResults);
+        }
+    }
+
+    virtual void onWorkDone_nb(
+            std::weak_ptr<C2Component> /* c2component */,
+            std::list<std::unique_ptr<C2Work>> c2workItems) override {
+        sp<IComponentListener> listener = mListener.promote();
+        if (listener) {
+            WorkBundle workBundle;
+
+            // TODO: Connect with bufferpool API to send Works & Buffers
+            if (objcpy(&workBundle, c2workItems) != Status::OK) {
+                ALOGE("onWorkDone() received corrupted work items.");
+                return;
+            }
+            listener->onWorkDone(workBundle);
+
+            // Finish buffer transfers: nothing else to do
+        }
+    }
+
+protected:
+    wp<IComponentListener> mListener;
+};
+
+// Component
+Component::Component(
+        const std::shared_ptr<C2Component>& component,
+        const sp<IComponentListener>& listener,
+        const sp<ComponentStore>& store) :
+    Configurable(new CachedConfigurable(
+            std::make_unique<CompIntf>(component->intf()))),
+    mComponent(component),
+    mInterface(component->intf()),
+    mListener(listener) /* , // TODO: Do we need store for anything?
+    mStore(store)*/ {
+    std::shared_ptr<C2Component::Listener> c2listener =
+            std::make_shared<Listener>(listener);
+    c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
+    // Retrieve supported parameters from store
+    // TODO: We could cache this per component/interface type
+    mInit = init(store.get());
+    mInit = mInit != C2_OK ? res : mInit;
+}
+
+// Methods from ::android::hardware::media::c2::V1_0::IComponent
+Return<Status> Component::queue(const WorkBundle& workBundle) {
+    std::list<std::unique_ptr<C2Work>> c2works;
+
+    // TODO: Connect with bufferpool API for buffer transfers
+    if (objcpy(&c2works, workBundle) != C2_OK) {
+        return Status::CORRUPTED;
+    }
+    return static_cast<Status>(mComponent->queue_nb(&c2works));
+}
+
+Return<void> Component::flush(flush_cb _hidl_cb) {
+    std::list<std::unique_ptr<C2Work>> c2flushedWorks;
+    c2_status_t c2res = mComponent->flush_sm(
+            C2Component::FLUSH_COMPONENT,
+            &c2flushedWorks);
+    WorkBundle flushedWorkBundle;
+
+    Status res = static_cast<Status>(c2res);
+    if (c2res == C2_OK) {
+        // TODO: Connect with bufferpool API for buffer transfers
+        res = objcpy(&flushedWorkBundle, c2flushedWorks);
+    }
+    _hidl_cb(res, flushedWorkBundle);
+    return Void();
+}
+
+Return<Status> Component::drain(bool withEos) {
+    return static_cast<Status>(mComponent->drain_nb(withEos ?
+            C2Component::DRAIN_COMPONENT_WITH_EOS :
+            C2Component::DRAIN_COMPONENT_NO_EOS));
+}
+
+Return<Status> Component::connectToInputSurface(const sp<IInputSurface>& surface) {
+    // TODO implement
+    (void)surface;
+    return Status::OK;
+}
+
+Return<Status> Component::connectToOmxInputSurface(
+        const sp<::android::hardware::graphics::bufferqueue::V1_0::
+        IGraphicBufferProducer>& producer,
+        const sp<::android::hardware::media::omx::V1_0::
+        IGraphicBufferSource>& source) {
+    // TODO implement
+    (void)producer;
+    (void)source;
+    return Status::OK;
+}
+
+Return<Status> Component::disconnectFromInputSurface() {
+    // TODO implement
+    return Status::OK;
+}
+
+Return<void> Component::createBlockPool(uint32_t allocatorId, createBlockPool_cb _hidl_cb) {
+    // TODO implement
+    (void)allocatorId;
+    _hidl_cb(Status::OK, 0 /* blockPoolId */, nullptr /* configurable */);
+    return Void();
+}
+
+Return<Status> Component::start() {
+    return static_cast<Status>(mComponent->start());
+}
+
+Return<Status> Component::stop() {
+    return static_cast<Status>(mComponent->stop());
+}
+
+Return<Status> Component::reset() {
+    return static_cast<Status>(mComponent->reset());
+}
+
+Return<Status> Component::release() {
+    return static_cast<Status>(mComponent->release());
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace google
+}  // namespace vendor
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/ComponentStore.cpp b/media/libstagefright/codec2/hidl/impl/1.0/ComponentStore.cpp
new file mode 100644
index 0000000..4d51fba
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/ComponentStore.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2-ComponentStore"
+#include <log/log.h>
+
+#include <codec2/hidl/1.0/ComponentStore.h>
+#include <codec2/hidl/1.0/Component.h>
+#include <codec2/hidl/1.0/ConfigurableC2Intf.h>
+#include <codec2/hidl/1.0/types.h>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using namespace ::android;
+
+struct StoreIntf : public ConfigurableC2Intf {
+    StoreIntf(const std::shared_ptr<C2ComponentStore>& store) :
+        ConfigurableC2Intf(store ? store->getName() : ""),
+        mStore(store) {
+    }
+
+    c2_status_t config(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>> *const failures
+            ) override {
+        // Assume all params are blocking
+        // TODO: Filter for supported params
+        if (mayBlock == C2_DONT_BLOCK && params.size() != 0) {
+            return C2_BLOCKING;
+        }
+        return mStore->config_sm(params, failures);
+    }
+
+    c2_status_t query(
+            const std::vector<C2Param::Index> &indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>> *const params) const override {
+        // Assume all params are blocking
+        // TODO: Filter for supported params
+        if (mayBlock == C2_DONT_BLOCK && indices.size() != 0) {
+            return C2_BLOCKING;
+        }
+        return mStore->query_sm({}, indices, params);
+    }
+
+    c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> *const params
+            ) const override {
+        return mStore->querySupportedParams_nb(params);
+    }
+
+    c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery> &fields,
+            c2_blocking_t mayBlock) const override {
+        // Assume all params are blocking
+        // TODO: Filter for supported params
+        if (mayBlock == C2_DONT_BLOCK && fields.size() != 0) {
+            return C2_BLOCKING;
+        }
+        return mStore->querySupportedValues_sm(fields);
+    }
+
+protected:
+    std::shared_ptr<C2ComponentStore> mStore;
+};
+
+
+ComponentStore::ComponentStore(const std::shared_ptr<C2ComponentStore>& store) :
+    Configurable(new CachedConfigurable(std::make_unique<StoreIntf>(store))),
+    mStore(store) {
+    // Retrieve struct descriptors
+    mParamReflector = mStore->getParamReflector();
+
+    // Retrieve supported parameters from store
+    mInit = init(this);
+}
+
+c2_status_t ComponentStore::validateSupportedParams(
+        const std::vector<std::shared_ptr<C2ParamDescriptor>>& params) {
+    c2_status_t res = C2_OK;
+
+    for (const std::shared_ptr<C2ParamDescriptor> &desc : params) {
+        if (!desc) {
+            // All descriptors should be valid
+            res = res ? res : C2_BAD_VALUE;
+            continue;
+        }
+        C2Param::CoreIndex coreIndex = desc->index().coreIndex();
+        auto it = mStructDescriptors.find(coreIndex);
+        if (it == mStructDescriptors.end()) {
+            std::shared_ptr<C2StructDescriptor> structDesc =
+                    mParamReflector->describe(coreIndex);
+            if (!structDesc) {
+                // All supported params must be described
+                res = C2_BAD_INDEX;
+            }
+            mStructDescriptors.insert({ coreIndex, structDesc });
+        }
+    }
+    return res;
+}
+
+// Methods from ::android::hardware::media::c2::V1_0::IComponentStore
+Return<void> ComponentStore::createComponent(
+        const hidl_string& name,
+        const sp<IComponentListener>& listener,
+        // TODO: Return the pool if the component has it.
+        const sp<IClientManager>& /* pool */,
+        createComponent_cb _hidl_cb) {
+    std::shared_ptr<C2Component> c2component;
+    c2_status_t res = mStore->createComponent(name, &c2component);
+    sp<IComponent> component;
+    if (res == C2_OK) {
+        component = new Component(c2component, listener, this);
+    }
+    _hidl_cb((Status)res, component);
+    return Void();
+}
+
+Return<void> ComponentStore::createInterface(
+        const hidl_string& name,
+        createInterface_cb _hidl_cb) {
+    std::shared_ptr<C2ComponentInterface> c2interface;
+    c2_status_t res = mStore->createInterface(name, &c2interface);
+    sp<IComponentInterface> interface;
+    if (res == C2_OK) {
+        interface = new ComponentInterface(c2interface, this);
+    }
+    _hidl_cb((Status)res, interface);
+    return Void();
+}
+
+Return<void> ComponentStore::listComponents(listComponents_cb _hidl_cb) {
+    std::vector<std::shared_ptr<const C2Component::Traits>> c2traits =
+            mStore->listComponents();
+    hidl_vec<IComponentStore::ComponentTraits> traits(c2traits.size());
+    size_t ix = 0;
+    for (const std::shared_ptr<const C2Component::Traits> &c2trait : c2traits) {
+        if (c2trait) {
+            objcpy(&traits[ix++], *c2trait);
+        }
+    }
+    traits.resize(ix);
+    _hidl_cb(traits);
+    return Void();
+}
+
+Return<sp<IInputSurface>> ComponentStore::createInputSurface() {
+    // TODO implement
+    return sp<IInputSurface> {};
+}
+
+Return<void> ComponentStore::getStructDescriptors(
+        const hidl_vec<uint32_t>& indices,
+        getStructDescriptors_cb _hidl_cb) {
+    hidl_vec<StructDescriptor> descriptors(indices.size());
+    size_t dstIx = 0;
+    Status res;
+    for (size_t srcIx = 0; srcIx < indices.size(); ++srcIx) {
+        const auto item = mStructDescriptors.find(
+                C2Param::CoreIndex(indices[srcIx]).coreIndex());
+        if (item == mStructDescriptors.end()) {
+            res = Status::NOT_FOUND;
+        } else if (item->second) {
+            objcpy(&descriptors[dstIx++], *item->second);
+        } else {
+            res = Status::NO_MEMORY;
+        }
+    }
+    descriptors.resize(dstIx);
+    _hidl_cb(res, descriptors);
+    return Void();
+}
+
+Return<sp<IClientManager>> ComponentStore::getPoolClientManager() {
+    // TODO implement
+    return sp<IClientManager> {};
+}
+
+Return<Status> ComponentStore::copyBuffer(const Buffer& src, const Buffer& dst) {
+    // TODO implement
+    (void)src;
+    (void)dst;
+    return Status::OMITTED;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace google
+}  // namespace vendor
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/Configurable.cpp b/media/libstagefright/codec2/hidl/impl/1.0/Configurable.cpp
new file mode 100644
index 0000000..3f041a9
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/Configurable.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2-Configurable"
+#include <log/log.h>
+
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/1.0/ComponentStore.h>
+#include <codec2/hidl/1.0/types.h>
+#include <C2ParamInternal.h>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using namespace ::android;
+
+CachedConfigurable::CachedConfigurable(
+        std::unique_ptr<ConfigurableC2Intf>&& intf) :
+    mIntf(std::move(intf)) {
+}
+
+c2_status_t CachedConfigurable::init(ComponentStore* store) {
+    // Retrieve supported parameters from store
+    c2_status_t init = mIntf->querySupportedParams(&mSupportedParams);
+    c2_status_t validate = store->validateSupportedParams(mSupportedParams);
+    return init == C2_OK ? C2_OK : validate;
+}
+
+// Methods from ::android::hardware::media::c2::V1_0::IConfigurable follow.
+Return<void> CachedConfigurable::getName(getName_cb _hidl_cb) {
+    _hidl_cb(mIntf->getName());
+    return Void();
+}
+
+Return<void> CachedConfigurable::query(
+        const hidl_vec<uint32_t>& indices,
+        bool mayBlock,
+        query_cb _hidl_cb) {
+    typedef C2Param::Index Index;
+    std::vector<Index> c2heapParamIndices(
+            (Index*)indices.data(),
+            (Index*)indices.data() + indices.size());
+    std::vector<std::unique_ptr<C2Param>> c2heapParams;
+    c2_status_t c2res = mIntf->query(
+            c2heapParamIndices,
+            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK,
+            &c2heapParams);
+
+    hidl_vec<uint8_t> params;
+    createParamsBlob(&params, c2heapParams);
+    _hidl_cb(static_cast<Status>(c2res), params);
+
+    return Void();
+}
+
+Return<void> CachedConfigurable::config(
+        const hidl_vec<uint8_t>& inParams,
+        bool mayBlock,
+        config_cb _hidl_cb) {
+    std::vector<C2Param*> c2params;
+    if (parseParamsBlob(&c2params, inParams) != C2_OK) {
+        _hidl_cb(Status::CORRUPTED,
+                hidl_vec<SettingResult>(),
+                hidl_vec<uint8_t>());
+        return Void();
+    }
+    // TODO: check if blob was invalid
+    std::vector<std::unique_ptr<C2SettingResult>> c2failures;
+    c2_status_t c2res = mIntf->config(
+            c2params,
+            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK,
+            &c2failures);
+    hidl_vec<SettingResult> failures(c2failures.size());
+    {
+        size_t ix = 0;
+        for (const std::unique_ptr<C2SettingResult>& c2result : c2failures) {
+            if (c2result) {
+                objcpy(&failures[ix++], *c2result);
+            }
+        }
+        failures.resize(ix);
+    }
+    hidl_vec<uint8_t> outParams;
+    createParamsBlob(&outParams, c2params);
+    _hidl_cb((Status)c2res, failures, outParams);
+    return Void();
+}
+
+Return<void> CachedConfigurable::querySupportedParams(
+        uint32_t start,
+        uint32_t count,
+        querySupportedParams_cb _hidl_cb) {
+    C2LinearRange request = C2LinearCapacity(mSupportedParams.size()).range(
+            start, count);
+    hidl_vec<ParamDescriptor> params(request.size());
+    Status res = Status::OK;
+    size_t dstIx = 0;
+    for (size_t srcIx = request.offset(); srcIx < request.endOffset(); ++srcIx) {
+        if (mSupportedParams[srcIx]) {
+            objcpy(&params[dstIx++], *mSupportedParams[srcIx]);
+        } else {
+            res = Status::CORRUPTED;
+        }
+    }
+    params.resize(dstIx);
+    _hidl_cb(res, params);
+    return Void();
+}
+
+Return<void> CachedConfigurable::querySupportedValues(
+        const hidl_vec<FieldSupportedValuesQuery>& inFields,
+        bool mayBlock,
+        querySupportedValues_cb _hidl_cb) {
+    std::vector<C2FieldSupportedValuesQuery> c2fields;
+    {
+        // C2FieldSupportedValuesQuery objects are restricted in that some
+        // members are const.
+        // C2ParamField - required for its constructor - has no constructors
+        // from fields. Use C2ParamInspector.
+        for (const FieldSupportedValuesQuery &query : inFields) {
+            c2fields.emplace_back(_C2ParamInspector::CreateParamField(
+                    query.field.index,
+                    query.field.fieldId.offset,
+                    query.field.fieldId.size),
+                    query.type == FieldSupportedValuesQuery::Type::POSSIBLE ?
+                    C2FieldSupportedValuesQuery::POSSIBLE :
+                    C2FieldSupportedValuesQuery::CURRENT);
+        }
+    }
+    c2_status_t c2res = mIntf->querySupportedValues(
+            c2fields,
+            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK);
+    hidl_vec<FieldSupportedValuesQueryResult> outFields(inFields.size());
+    {
+        size_t ix = 0;
+        for (const C2FieldSupportedValuesQuery &result : c2fields) {
+            objcpy(&outFields[ix++], result);
+        }
+    }
+    _hidl_cb((Status)c2res, outFields);
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace google
+}  // namespace vendor
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/Component.h b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/Component.h
new file mode 100644
index 0000000..0308fe0
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/Component.h
@@ -0,0 +1,85 @@
+#ifndef VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENT_H
+#define VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENT_H
+
+#include <codec2/hidl/1.0/Configurable.h>
+
+#include <vendor/google/media/c2/1.0/IComponentListener.h>
+#include <vendor/google/media/c2/1.0/IComponentStore.h>
+#include <vendor/google/media/c2/1.0/IComponent.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <C2Component.h>
+#include <C2.h>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ComponentStore;
+
+struct ComponentInterface : public Configurable<IComponentInterface> {
+    ComponentInterface(
+            const std::shared_ptr<C2ComponentInterface>& interface,
+            const sp<ComponentStore>& store);
+    c2_status_t status() const;
+
+protected:
+    c2_status_t mInit;
+    std::shared_ptr<C2ComponentInterface> mInterface;
+    // sp<ComponentStore> mStore; // TODO needed?
+};
+
+struct Component : public Configurable<IComponent> {
+    Component(
+            const std::shared_ptr<C2Component>&,
+            const sp<IComponentListener>& listener,
+            const sp<ComponentStore>& store);
+
+    // Methods from gIComponent follow.
+    virtual Return<Status> queue(const WorkBundle& workBundle) override;
+    virtual Return<void> flush(flush_cb _hidl_cb) override;
+    virtual Return<Status> drain(bool withEos) override;
+    virtual Return<Status> connectToInputSurface(
+            const sp<IInputSurface>& surface) override;
+    virtual Return<Status> connectToOmxInputSurface(
+            const sp<::android::hardware::graphics::bufferqueue::V1_0::
+            IGraphicBufferProducer>& producer,
+            const sp<::android::hardware::media::omx::V1_0::
+            IGraphicBufferSource>& source) override;
+    virtual Return<Status> disconnectFromInputSurface() override;
+    virtual Return<void> createBlockPool(
+            uint32_t allocatorId,
+            createBlockPool_cb _hidl_cb) override;
+    virtual Return<Status> start() override;
+    virtual Return<Status> stop() override;
+    virtual Return<Status> reset() override;
+    virtual Return<Status> release() override;
+
+protected:
+    c2_status_t mInit;
+    std::shared_ptr<C2Component> mComponent;
+    std::shared_ptr<C2ComponentInterface> mInterface;
+    sp<IComponentListener> mListener;
+    // sp<ComponentStore> mStore; // TODO needed?
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace google
+}  // namespace vendor
+
+#endif  // VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENT_H
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ComponentStore.h b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ComponentStore.h
new file mode 100644
index 0000000..c3f92a0
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ComponentStore.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 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 VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENTSTORE_H
+#define VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENTSTORE_H
+
+#include <codec2/hidl/1.0/Component.h>
+#include <codec2/hidl/1.0/Configurable.h>
+#include <C2Component.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <vendor/google/media/c2/1.0/IComponentStore.h>
+#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <vector>
+#include <map>
+#include <memory>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::bufferpool::V1_0::IClientManager;
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ComponentStore : public Configurable<IComponentStore> {
+    ComponentStore(const std::shared_ptr<C2ComponentStore>& store);
+    virtual ~ComponentStore() = default;
+
+    c2_status_t status() const {
+        return mInit;
+    }
+
+    c2_status_t validateSupportedParams(
+            const std::vector<std::shared_ptr<C2ParamDescriptor>>& params);
+
+    // Methods from ::android::hardware::media::c2::V1_0::IComponentStore
+    Return<void> createComponent(
+            const hidl_string& name,
+            const sp<IComponentListener>& listener,
+            const sp<IClientManager>& pool,
+            createComponent_cb _hidl_cb) override;
+    Return<void> createInterface(
+            const hidl_string& name,
+            createInterface_cb _hidl_cb) override;
+    Return<void> listComponents(listComponents_cb _hidl_cb) override;
+    Return<sp<IInputSurface>> createInputSurface() override;
+    Return<void> getStructDescriptors(
+            const hidl_vec<uint32_t>& indices,
+            getStructDescriptors_cb _hidl_cb) override;
+    Return<sp<IClientManager>> getPoolClientManager() override;
+    Return<Status> copyBuffer(
+            const Buffer& src,
+            const Buffer& dst) override;
+
+protected:
+    c2_status_t mInit;
+    std::shared_ptr<C2ComponentStore> mStore;
+    std::shared_ptr<C2ParamReflector> mParamReflector;
+    std::map<C2Param::CoreIndex, std::shared_ptr<C2StructDescriptor>>
+            mStructDescriptors;
+
+    sp<IClientManager> mPoolManager;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace google
+}  // namespace vendor
+
+#endif  // VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENTSTORE_H
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/Configurable.h b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/Configurable.h
new file mode 100644
index 0000000..697d483
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/Configurable.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 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 VENDOR_GOOGLE_MEDIA_C2_V1_0_CONFIGURABLE_H
+#define VENDOR_GOOGLE_MEDIA_C2_V1_0_CONFIGURABLE_H
+
+#include <codec2/hidl/1.0/ConfigurableC2Intf.h>
+
+#include <C2Component.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <vendor/google/media/c2/1.0/IConfigurable.h>
+#include <hidl/Status.h>
+
+#include <memory>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ComponentStore;
+
+/**
+ * Implementation of the IConfigurable interface that supports caching of
+ * supported parameters from a supplied ComponentStore.
+ *
+ * This is mainly the same for all of the configurable C2 interfaces though
+ * there are slight differences in the blocking behavior. This is handled in the
+ * ConfigurableC2Intf implementations.
+ */
+struct CachedConfigurable : public IConfigurable {
+    CachedConfigurable(std::unique_ptr<ConfigurableC2Intf>&& intf);
+
+    c2_status_t init(ComponentStore* store);
+
+    // Methods from ::android::hardware::media::c2::V1_0::IConfigurable
+
+    virtual Return<void> getName(getName_cb _hidl_cb) override;
+
+    virtual Return<void> query(
+            const hidl_vec<uint32_t>& indices,
+            bool mayBlock,
+            query_cb _hidl_cb) override;
+
+    virtual Return<void> config(
+            const hidl_vec<uint8_t>& inParams,
+            bool mayBlock,
+            config_cb _hidl_cb) override;
+
+    virtual Return<void> querySupportedParams(
+            uint32_t start,
+            uint32_t count,
+            querySupportedParams_cb _hidl_cb) override;
+
+    virtual Return<void> querySupportedValues(
+            const hidl_vec<FieldSupportedValuesQuery>& inFields,
+            bool mayBlock,
+            querySupportedValues_cb _hidl_cb) override;
+
+protected:
+    // Common Codec2.0 interface wrapper
+    std::unique_ptr<ConfigurableC2Intf> mIntf;
+
+    // Cached supported params
+    std::vector<std::shared_ptr<C2ParamDescriptor>> mSupportedParams;
+};
+
+/**
+ * Template that implements the `IConfigurable` interface for an inherited
+ * interface. Classes that implement a child interface `I` of `IConfigurable`
+ * can derive from `Configurable<I>`.
+ */
+template <typename I>
+struct Configurable : public I {
+    Configurable(const sp<CachedConfigurable>& intf): mIntf(intf) {
+    }
+
+    c2_status_t init(ComponentStore* store) {
+        return mIntf->init(store);
+    }
+
+    // Methods from ::android::hardware::media::c2::V1_0::IConfigurable
+
+    using getName_cb = typename I::getName_cb;
+    virtual Return<void> getName(getName_cb _hidl_cb) override {
+        return mIntf->getName(_hidl_cb);
+    }
+
+    using query_cb = typename I::query_cb;
+    virtual Return<void> query(
+            const hidl_vec<uint32_t>& indices,
+            bool mayBlock,
+            query_cb _hidl_cb) override {
+        return mIntf->query(indices, mayBlock, _hidl_cb);
+    }
+
+    using config_cb = typename I::config_cb;
+    virtual Return<void> config(
+            const hidl_vec<uint8_t>& inParams,
+            bool mayBlock,
+            config_cb _hidl_cb) override {
+        return mIntf->config(inParams, mayBlock, _hidl_cb);
+    }
+
+    using querySupportedParams_cb = typename I::querySupportedParams_cb;
+    virtual Return<void> querySupportedParams(
+            uint32_t start,
+            uint32_t count,
+            querySupportedParams_cb _hidl_cb) override {
+        return mIntf->querySupportedParams(start, count, _hidl_cb);
+    }
+
+    using querySupportedValues_cb = typename I::querySupportedValues_cb;
+    virtual Return<void> querySupportedValues(
+            const hidl_vec<FieldSupportedValuesQuery>& inFields,
+            bool mayBlock,
+            querySupportedValues_cb _hidl_cb) override {
+        return mIntf->querySupportedValues(inFields, mayBlock, _hidl_cb);
+    }
+
+protected:
+    sp<CachedConfigurable> mIntf;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace google
+}  // namespace vendor
+
+#endif  // VENDOR_GOOGLE_MEDIA_C2_V1_0_CONFIGURABLE_H
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ConfigurableC2Intf.h b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ConfigurableC2Intf.h
new file mode 100644
index 0000000..da90996
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ConfigurableC2Intf.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 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 VENDOR_GOOGLE_MEDIA_C2_V1_0_CONFIGURABLEC2INTF_H
+#define VENDOR_GOOGLE_MEDIA_C2_V1_0_CONFIGURABLEC2INTF_H
+
+#include <C2Work.h>
+#include <C2Component.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <hidl/HidlSupport.h>
+#include <utils/StrongPointer.h>
+#include <vector>
+#include <memory>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+/**
+ * Common Codec 2.0 interface wrapper.
+ */
+struct ConfigurableC2Intf {
+    C2String getName() const { return mName; }
+    /** C2ComponentInterface::query_vb sans stack params */
+    virtual c2_status_t query(
+            const std::vector<C2Param::Index> &indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const params) const = 0;
+    /** C2ComponentInterface::config_vb */
+    virtual c2_status_t config(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+    /** C2ComponentInterface::querySupportedParams_nb */
+    virtual c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const = 0;
+    /** C2ComponentInterface::querySupportedParams_nb */
+    virtual c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery>& fields, c2_blocking_t mayBlock) const = 0;
+
+    virtual ~ConfigurableC2Intf() = default;
+
+    ConfigurableC2Intf(const C2String& name) : mName(name) {}
+
+protected:
+    C2String mName; /* cache component name */
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace google
+}  // namespace vendor
+
+#endif  // VENDOR_GOOGLE_MEDIA_C2_V1_0_CONFIGURABLEC2INTF_H
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/types.h b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/types.h
new file mode 100644
index 0000000..1eace56
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/types.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 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 VENDOR_GOOGLE_MEDIA_C2_V1_0_TYPES_H
+#define VENDOR_GOOGLE_MEDIA_C2_V1_0_TYPES_H
+
+#include <vendor/google/media/c2/1.0/types.h>
+#include <vendor/google/media/c2/1.0/IComponentStore.h>
+
+#include <C2Param.h>
+#include <C2Component.h>
+#include <C2Work.h>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+
+// Types of metadata for Blocks.
+struct C2Hidl_Range {
+    uint32_t offset;
+    uint32_t length; // Do not use "size" because the name collides with C2Info::size().
+};
+typedef C2GlobalParam<C2Info, C2Hidl_Range, 0> C2Hidl_RangeInfo;
+
+struct C2Hidl_Rect {
+    uint32_t left;
+    uint32_t top;
+    uint32_t width;
+    uint32_t height;
+};
+typedef C2GlobalParam<C2Info, C2Hidl_Rect, 1> C2Hidl_RectInfo;
+
+// C2SettingResult -> SettingResult
+Status objcpy(
+        SettingResult* d,
+        const C2SettingResult& s);
+
+// SettingResult -> std::unique_ptr<C2SettingResult>
+c2_status_t objcpy(
+        std::unique_ptr<C2SettingResult>* d,
+        const SettingResult& s);
+
+// C2ParamDescriptor -> ParamDescriptor
+Status objcpy(
+        ParamDescriptor* d,
+        const C2ParamDescriptor& s);
+
+// ParamDescriptor -> std::shared_ptr<C2ParamDescriptor>
+c2_status_t objcpy(
+        std::shared_ptr<C2ParamDescriptor>* d,
+        const ParamDescriptor& s);
+
+// C2FieldSupportedValuesQuery -> FieldSupportedValuesQuery
+Status objcpy(
+        FieldSupportedValuesQuery* d,
+        const C2FieldSupportedValuesQuery& s);
+
+// FieldSupportedValuesQuery -> C2FieldSupportedValuesQuery
+c2_status_t objcpy(
+        C2FieldSupportedValuesQuery* d,
+        const FieldSupportedValuesQuery& s);
+
+// C2FieldSupportedValuesQuery -> FieldSupportedValuesQueryResult
+Status objcpy(
+        FieldSupportedValuesQueryResult* d,
+        const C2FieldSupportedValuesQuery& s);
+
+// FieldSupportedValuesQuery, FieldSupportedValuesQueryResult -> C2FieldSupportedValuesQuery
+c2_status_t objcpy(
+        C2FieldSupportedValuesQuery* d,
+        const FieldSupportedValuesQuery& sq,
+        const FieldSupportedValuesQueryResult& sr);
+
+// C2Component::Traits -> ComponentTraits
+Status objcpy(
+        IComponentStore::ComponentTraits* d,
+        const C2Component::Traits& s);
+
+// ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
+// Note: The output d is only valid as long as aliasesBuffer remains alive.
+c2_status_t objcpy(
+        C2Component::Traits* d,
+        std::unique_ptr<std::vector<std::string>>* aliasesBuffer,
+        const IComponentStore::ComponentTraits& s);
+
+// C2StructDescriptor -> StructDescriptor
+Status objcpy(
+        StructDescriptor* d,
+        const C2StructDescriptor& s);
+
+// StructDescriptor -> C2StructDescriptor
+// TODO: This cannot be implemented yet because C2StructDescriptor does not
+// allow dynamic construction/modification.
+c2_status_t objcpy(
+        C2StructDescriptor* d,
+        const StructDescriptor& s);
+
+// std::list<std::unique_ptr<C2Work>> -> WorkBundle
+// TODO: Connect with Bufferpool
+Status objcpy(
+        WorkBundle* d,
+        const std::list<std::unique_ptr<C2Work>>& s);
+
+// WorkBundle -> std::list<std::unique_ptr<C2Work>>
+// TODO: Connect with Bufferpool
+c2_status_t objcpy(
+        std::list<std::unique_ptr<C2Work>>* d,
+        const WorkBundle& s);
+
+/**
+ * Parses a params blob and returns C2Param pointers to its params.
+ * \param[out] params target vector of C2Param pointers
+ * \param[in] blob parameter blob to parse
+ * \retval C2_OK if the full blob was parsed
+ * \retval C2_BAD_VALUE otherwise
+ */
+c2_status_t parseParamsBlob(
+        std::vector<C2Param*> *params,
+        const hidl_vec<uint8_t> &blob);
+
+/**
+ * Concatenates a list of C2Params into a params blob.
+ * \param[out] blob target blob
+ * \param[in] params parameters to concatenate
+ * \retval C2_OK if the blob was successfully created
+ * \retval C2_BAD_VALUE if the blob was not successful (this only happens if the parameters were
+ *         not const)
+ */
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<C2Param*> &params);
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<std::unique_ptr<C2Param>> &params);
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<std::shared_ptr<const C2Info>> &params);
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<std::unique_ptr<C2Tuning>> &params);
+
+/**
+ * Parses a params blob and create a vector of C2Params whose members are copies
+ * of the params in the blob.
+ * \param[out] params the resulting vector
+ * \param[in] blob parameter blob to parse
+ * \retval C2_OK if the full blob was parsed and params was constructed
+ * \retval C2_BAD_VALUE otherwise
+ */
+c2_status_t copyParamsFromBlob(
+        std::vector<std::unique_ptr<C2Param>>* params,
+        Params blob);
+
+/**
+ * Parses a params blob and applies updates to params
+ * \param[in,out] params params to be updated
+ * \param[in] blob parameter blob containing updates
+ * \retval C2_OK if the full blob was parsed and params was updated
+ * \retval C2_BAD_VALUE otherwise
+ */
+c2_status_t updateParamsFromBlob(
+        const std::vector<C2Param*>& params,
+        const Params& blob);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace google
+}  // namespace vendor
+
+#endif  // VENDOR_GOOGLE_MEDIA_C2_V1_0_TYPES_H
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/types.cpp b/media/libstagefright/codec2/hidl/impl/1.0/types.cpp
new file mode 100644
index 0000000..f14c21a
--- /dev/null
+++ b/media/libstagefright/codec2/hidl/impl/1.0/types.cpp
@@ -0,0 +1,1265 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2-types"
+#include <log/log.h>
+
+#include <codec2/hidl/1.0/types.h>
+
+#include <C2AllocatorIon.h>
+#include <C2AllocatorGralloc.h>
+#include <C2PlatformSupport.h>
+#include <C2BlockInternal.h>
+#include <C2ParamInternal.h>
+#include <C2Param.h>
+#include <C2Buffer.h>
+#include <C2Work.h>
+#include <C2Component.h>
+#include <util/C2ParamUtils.h>
+
+#include <unordered_map>
+#include <algorithm>
+
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using namespace ::android;
+
+namespace /* unnamed */ {
+
+template <typename Common, typename DstVector, typename SrcVector>
+void copyVector(DstVector* d, const SrcVector& s) {
+    static_assert(sizeof(Common) == sizeof(decltype((*d)[0])),
+            "DstVector's component size does not match Common");
+    static_assert(sizeof(Common) == sizeof(decltype(s[0])),
+            "SrcVector's component size does not match Common");
+    d->resize(s.size());
+    std::copy(
+            reinterpret_cast<const Common*>(&s[0]),
+            reinterpret_cast<const Common*>(&s[0] + s.size()),
+            reinterpret_cast<Common*>(&(*d)[0]));
+}
+
+// C2ParamField -> ParamField
+void objcpy(ParamField *d, const C2ParamField &s) {
+    d->index = static_cast<ParamIndex>(_C2ParamInspector::GetIndex(s));
+    d->fieldId.offset = static_cast<uint32_t>(_C2ParamInspector::GetOffset(s));
+    d->fieldId.size = static_cast<uint32_t>(_C2ParamInspector::GetSize(s));
+}
+
+struct C2ParamFieldBuilder : public C2ParamField {
+    C2ParamFieldBuilder() : C2ParamField(
+            static_cast<C2Param::Index>(static_cast<uint32_t>(0)), 0, 0) {
+    }
+    // ParamField -> C2ParamField
+    C2ParamFieldBuilder(const ParamField& s) : C2ParamField(
+            static_cast<C2Param::Index>(static_cast<uint32_t>(s.index)),
+            static_cast<uint32_t>(s.fieldId.offset),
+            static_cast<uint32_t>(s.fieldId.size)) {
+    }
+};
+
+// C2WorkOrdinalStruct -> WorkOrdinal
+void objcpy(WorkOrdinal *d, const C2WorkOrdinalStruct &s) {
+    d->frameIndex = static_cast<uint64_t>(s.frameIndex.peeku());
+    d->timestampUs = static_cast<uint64_t>(s.timestamp.peeku());
+    d->customOrdinal = static_cast<uint64_t>(s.customOrdinal.peeku());
+}
+
+// WorkOrdinal -> C2WorkOrdinalStruct
+void objcpy(C2WorkOrdinalStruct *d, const WorkOrdinal &s) {
+    d->frameIndex = c2_cntr64_t(s.frameIndex);
+    d->timestamp = c2_cntr64_t(s.timestampUs);
+    d->customOrdinal = c2_cntr64_t(s.customOrdinal);
+}
+
+// C2FieldSupportedValues::range's type -> FieldSupportedValues::Range
+void objcpy(
+        FieldSupportedValues::Range* d,
+        const decltype(C2FieldSupportedValues::range)& s) {
+    d->min = static_cast<PrimitiveValue>(s.min.u64);
+    d->max = static_cast<PrimitiveValue>(s.max.u64);
+    d->step = static_cast<PrimitiveValue>(s.step.u64);
+    d->num = static_cast<PrimitiveValue>(s.num.u64);
+    d->denom = static_cast<PrimitiveValue>(s.denom.u64);
+}
+
+// C2FieldSupportedValues -> FieldSupportedValues
+Status objcpy(FieldSupportedValues *d, const C2FieldSupportedValues &s) {
+    d->typeOther = static_cast<int32_t>(s.type);
+    switch (s.type) {
+    case C2FieldSupportedValues::EMPTY:
+        d->type = FieldSupportedValues::Type::EMPTY;
+        d->values.resize(0);
+        return Status::OK;
+    case C2FieldSupportedValues::RANGE:
+        d->type = FieldSupportedValues::Type::RANGE;
+        objcpy(&d->range, s.range);
+        d->values.resize(0);
+        return Status::OK;
+    default:
+        switch (s.type) {
+        case C2FieldSupportedValues::VALUES:
+            d->type = FieldSupportedValues::Type::VALUES;
+            break;
+        case C2FieldSupportedValues::FLAGS:
+            d->type = FieldSupportedValues::Type::FLAGS;
+            break;
+        default:
+            d->type = FieldSupportedValues::Type::OTHER;
+            // Copy all fields in this case
+            objcpy(&d->range, s.range);
+        }
+        d->values.resize(s.values.size());
+        copyVector<uint64_t>(&d->values, s.values);
+        return Status::OK;
+    }
+}
+
+// FieldSupportedValues::Range -> C2FieldSupportedValues::range's type
+void objcpy(
+        decltype(C2FieldSupportedValues::range)* d,
+        const FieldSupportedValues::Range& s) {
+    d->min.u64 = static_cast<uint64_t>(s.min);
+    d->max.u64 = static_cast<uint64_t>(s.max);
+    d->step.u64 = static_cast<uint64_t>(s.step);
+    d->num.u64 = static_cast<uint64_t>(s.num);
+    d->denom.u64 = static_cast<uint64_t>(s.denom);
+}
+
+// FieldSupportedValues -> C2FieldSupportedValues
+c2_status_t objcpy(C2FieldSupportedValues *d, const FieldSupportedValues &s) {
+    switch (s.type) {
+    case FieldSupportedValues::Type::EMPTY:
+        d->type = C2FieldSupportedValues::EMPTY;
+        return C2_OK;
+    case FieldSupportedValues::Type::RANGE:
+        d->type = C2FieldSupportedValues::RANGE;
+        objcpy(&d->range, s.range);
+        d->values.resize(0);
+        return C2_OK;
+    default:
+        switch (s.type) {
+        case FieldSupportedValues::Type::VALUES:
+            d->type = C2FieldSupportedValues::VALUES;
+            break;
+        case FieldSupportedValues::Type::FLAGS:
+            d->type = C2FieldSupportedValues::FLAGS;
+            break;
+        default:
+            d->type = static_cast<C2FieldSupportedValues::type_t>(s.typeOther);
+            // Copy all fields in this case
+            objcpy(&d->range, s.range);
+        }
+        copyVector<uint64_t>(&d->values, s.values);
+        return C2_OK;
+    }
+}
+
+} // unnamed namespace
+
+// C2FieldSupportedValuesQuery -> FieldSupportedValuesQuery
+Status objcpy(
+        FieldSupportedValuesQuery* d,
+        const C2FieldSupportedValuesQuery& s) {
+    objcpy(&d->field, s.field());
+    switch (s.type()) {
+    case C2FieldSupportedValuesQuery::POSSIBLE:
+        d->type = FieldSupportedValuesQuery::Type::POSSIBLE;
+        break;
+    case C2FieldSupportedValuesQuery::CURRENT:
+        d->type = FieldSupportedValuesQuery::Type::CURRENT;
+        break;
+    default:
+        ALOGE("Unknown type of C2FieldSupportedValuesQuery: %u",
+                static_cast<unsigned>(s.type()));
+        return Status::BAD_VALUE;
+    }
+    return Status::OK;
+}
+
+// FieldSupportedValuesQuery -> C2FieldSupportedValuesQuery
+c2_status_t objcpy(
+        C2FieldSupportedValuesQuery* d,
+        const FieldSupportedValuesQuery& s) {
+    C2FieldSupportedValuesQuery::type_t dType;
+    switch (s.type) {
+    case FieldSupportedValuesQuery::Type::POSSIBLE:
+        dType = C2FieldSupportedValuesQuery::POSSIBLE;
+        break;
+    case FieldSupportedValuesQuery::Type::CURRENT:
+        dType = C2FieldSupportedValuesQuery::CURRENT;
+        break;
+    default:
+        ALOGE("Unknown type of FieldSupportedValuesQuery: %u",
+                static_cast<unsigned>(s.type));
+        return C2_BAD_VALUE;
+    }
+    *d = C2FieldSupportedValuesQuery(C2ParamFieldBuilder(s.field), dType);
+    return C2_OK;
+}
+
+// C2FieldSupportedValuesQuery -> FieldSupportedValuesQueryResult
+Status objcpy(
+        FieldSupportedValuesQueryResult* d,
+        const C2FieldSupportedValuesQuery& s) {
+    d->status = static_cast<Status>(s.status);
+    return objcpy(&d->values, s.values);
+}
+
+// FieldSupportedValuesQuery, FieldSupportedValuesQueryResult ->
+// C2FieldSupportedValuesQuery
+c2_status_t objcpy(
+        C2FieldSupportedValuesQuery* d,
+        const FieldSupportedValuesQuery& sq,
+        const FieldSupportedValuesQueryResult& sr) {
+    c2_status_t status = objcpy(d, sq);
+    if (status != C2_OK) {
+        return status;
+    }
+    d->status = static_cast<c2_status_t>(sr.status);
+    return objcpy(&d->values, sr.values);
+}
+
+// C2Component::Traits -> IComponentStore::ComponentTraits
+Status objcpy(
+        IComponentStore::ComponentTraits *d,
+        const C2Component::Traits &s) {
+    d->name = s.name;
+
+    // TODO: Currently, we do not have any domain values defined in Codec2.0.
+    d->domain = IComponentStore::ComponentTraits::Domain::OTHER;
+    d->domainOther = static_cast<uint32_t>(s.domain);
+
+    // TODO: Currently, we do not have any kind values defined in Codec2.0.
+    d->kind = IComponentStore::ComponentTraits::Kind::OTHER;
+    d->kindOther = static_cast<uint32_t>(s.kind);
+
+    d->rank = static_cast<uint32_t>(s.rank);
+
+    d->mediaType = s.mediaType;
+
+    d->aliases.resize(s.aliases.size());
+    for (size_t ix = s.aliases.size(); ix > 0; ) {
+        --ix;
+        d->aliases[ix] = s.aliases[ix];
+    }
+    return Status::OK;
+}
+
+// ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
+c2_status_t objcpy(
+        C2Component::Traits* d,
+        std::unique_ptr<std::vector<std::string>>* aliasesBuffer,
+        const IComponentStore::ComponentTraits& s) {
+    d->name = s.name.c_str();
+    d->domain = static_cast<C2Component::domain_t>(s.domainOther);
+    d->kind = static_cast<C2Component::kind_t>(s.kindOther);
+    d->rank = static_cast<C2Component::rank_t>(s.rank);
+    d->mediaType = s.mediaType.c_str();
+
+    // aliasesBuffer must not be resized after this.
+    *aliasesBuffer = std::make_unique<std::vector<std::string>>(
+            s.aliases.size());
+    (*aliasesBuffer)->resize(s.aliases.size());
+    std::vector<C2StringLiteral> dAliases(s.aliases.size());
+    for (size_t i = 0; i < s.aliases.size(); ++i) {
+        (**aliasesBuffer)[i] = s.aliases[i].c_str();
+        d->aliases[i] = (**aliasesBuffer)[i].c_str();
+    }
+    return C2_OK;
+}
+
+namespace /* unnamed */ {
+
+// C2ParamFieldValues -> ParamFieldValues
+Status objcpy(ParamFieldValues *d, const C2ParamFieldValues &s) {
+    objcpy(&d->paramOrField, s.paramOrField);
+    if (s.values) {
+        d->values.resize(1);
+        return objcpy(&d->values[0], *s.values);
+    }
+    d->values.resize(0);
+    return Status::OK;
+}
+
+// ParamFieldValues -> C2ParamFieldValues
+c2_status_t objcpy(C2ParamFieldValues *d, const ParamFieldValues &s) {
+    d->paramOrField = C2ParamFieldBuilder(s.paramOrField);
+    if (s.values.size() == 1) {
+        d->values = std::make_unique<C2FieldSupportedValues>();
+        return objcpy(d->values.get(), s.values[0]);
+    } else if (s.values.size() == 0) {
+        d->values.reset();
+        return C2_OK;
+    }
+    ALOGE("Multiple FieldSupportedValues objects. "
+            "(Only one is allowed.)");
+    return C2_BAD_VALUE;
+}
+
+} // unnamed namespace
+
+// C2SettingResult -> SettingResult
+Status objcpy(SettingResult *d, const C2SettingResult &s) {
+    d->failureOther = static_cast<uint32_t>(s.failure);
+    switch (s.failure) {
+    case C2SettingResult::READ_ONLY:
+        d->failure = SettingResult::Failure::READ_ONLY;
+        break;
+    case C2SettingResult::MISMATCH:
+        d->failure = SettingResult::Failure::MISMATCH;
+        break;
+    case C2SettingResult::BAD_VALUE:
+        d->failure = SettingResult::Failure::BAD_VALUE;
+        break;
+    case C2SettingResult::BAD_TYPE:
+        d->failure = SettingResult::Failure::BAD_TYPE;
+        break;
+    case C2SettingResult::BAD_PORT:
+        d->failure = SettingResult::Failure::BAD_PORT;
+        break;
+    case C2SettingResult::BAD_INDEX:
+        d->failure = SettingResult::Failure::BAD_INDEX;
+        break;
+    case C2SettingResult::CONFLICT:
+        d->failure = SettingResult::Failure::CONFLICT;
+        break;
+    case C2SettingResult::UNSUPPORTED:
+        d->failure = SettingResult::Failure::UNSUPPORTED;
+        break;
+    case C2SettingResult::INFO_CONFLICT:
+        d->failure = SettingResult::Failure::INFO_CONFLICT;
+        break;
+    default:
+        d->failure = SettingResult::Failure::OTHER;
+    }
+    Status status = objcpy(&d->field, s.field);
+    if (status != Status::OK) {
+        return status;
+    }
+    d->conflicts.resize(s.conflicts.size());
+    size_t i = 0;
+    for (const C2ParamFieldValues& sConflict : s.conflicts) {
+        ParamFieldValues &dConflict = d->conflicts[i++];
+        status = objcpy(&dConflict, sConflict);
+        if (status != Status::OK) {
+            return status;
+        }
+    }
+    return Status::OK;
+}
+
+// SettingResult -> std::unique_ptr<C2SettingResult>
+c2_status_t objcpy(std::unique_ptr<C2SettingResult> *d, const SettingResult &s) {
+    *d = std::unique_ptr<C2SettingResult>(new C2SettingResult {
+            .field = C2ParamFieldValues(C2ParamFieldBuilder()) });
+    if (!*d) {
+        return C2_NO_MEMORY;
+    }
+
+    // failure
+    switch (s.failure) {
+    case SettingResult::Failure::READ_ONLY:
+        (*d)->failure = C2SettingResult::READ_ONLY;
+        break;
+    case SettingResult::Failure::MISMATCH:
+        (*d)->failure = C2SettingResult::MISMATCH;
+        break;
+    case SettingResult::Failure::BAD_VALUE:
+        (*d)->failure = C2SettingResult::BAD_VALUE;
+        break;
+    case SettingResult::Failure::BAD_TYPE:
+        (*d)->failure = C2SettingResult::BAD_TYPE;
+        break;
+    case SettingResult::Failure::BAD_PORT:
+        (*d)->failure = C2SettingResult::BAD_PORT;
+        break;
+    case SettingResult::Failure::BAD_INDEX:
+        (*d)->failure = C2SettingResult::BAD_INDEX;
+        break;
+    case SettingResult::Failure::CONFLICT:
+        (*d)->failure = C2SettingResult::CONFLICT;
+        break;
+    case SettingResult::Failure::UNSUPPORTED:
+        (*d)->failure = C2SettingResult::UNSUPPORTED;
+        break;
+    case SettingResult::Failure::INFO_CONFLICT:
+        (*d)->failure = C2SettingResult::INFO_CONFLICT;
+        break;
+    default:
+        (*d)->failure = static_cast<C2SettingResult::Failure>(s.failureOther);
+    }
+
+    // field
+    c2_status_t status = objcpy(&(*d)->field, s.field);
+    if (status != C2_OK) {
+        return status;
+    }
+
+    // conflicts
+    (*d)->conflicts.clear();
+    (*d)->conflicts.reserve(s.conflicts.size());
+    for (const ParamFieldValues& sConflict : s.conflicts) {
+        (*d)->conflicts.emplace_back(
+                C2ParamFieldValues{ C2ParamFieldBuilder(), nullptr });
+        status = objcpy(&(*d)->conflicts.back(), sConflict);
+        if (status != C2_OK) {
+            return status;
+        }
+    }
+    return C2_OK;
+}
+
+// C2ParamDescriptor -> ParamDescriptor
+Status objcpy(ParamDescriptor *d, const C2ParamDescriptor &s) {
+    d->index = static_cast<ParamIndex>(s.index());
+    d->attrib = static_cast<hidl_bitfield<ParamDescriptor::Attrib>>(
+            _C2ParamInspector::GetAttrib(s));
+    d->name = s.name();
+    copyVector<uint32_t>(&d->dependencies, s.dependencies());
+    return Status::OK;
+}
+
+// ParamDescriptor -> C2ParamDescriptor
+c2_status_t objcpy(std::shared_ptr<C2ParamDescriptor> *d, const ParamDescriptor &s) {
+    std::vector<C2Param::Index> dDependencies;
+    dDependencies.reserve(s.dependencies.size());
+    for (const ParamIndex& sDependency : s.dependencies) {
+        dDependencies.emplace_back(static_cast<uint32_t>(sDependency));
+    }
+    *d = std::make_shared<C2ParamDescriptor>(
+            C2Param::Index(static_cast<uint32_t>(s.index)),
+            static_cast<C2ParamDescriptor::attrib_t>(s.attrib),
+            C2String(s.name.c_str()),
+            std::move(dDependencies));
+    return C2_OK;
+}
+
+// C2StructDescriptor -> StructDescriptor
+Status objcpy(StructDescriptor *d, const C2StructDescriptor &s) {
+    d->type = static_cast<ParamIndex>(s.coreIndex().coreIndex());
+    d->fields.resize(s.numFields());
+    size_t i = 0;
+    for (const auto& sField : s) {
+        FieldDescriptor& dField = d->fields[i++];
+        dField.fieldId.offset = static_cast<uint32_t>(
+                _C2ParamInspector::GetOffset(sField));
+        dField.fieldId.size = static_cast<uint32_t>(
+                _C2ParamInspector::GetSize(sField));
+        dField.type = static_cast<hidl_bitfield<FieldDescriptor::Type>>(
+                sField.type());
+        dField.length = static_cast<uint32_t>(sField.extent());
+        dField.name = static_cast<hidl_string>(sField.name());
+        const auto& sNamedValues = sField.namedValues();
+        dField.namedValues.resize(sNamedValues.size());
+        size_t j = 0;
+        for (const auto& sNamedValue : sNamedValues) {
+            FieldDescriptor::NamedValue& dNamedValue = dField.namedValues[j++];
+            dNamedValue.name = static_cast<hidl_string>(sNamedValue.first);
+            dNamedValue.value = static_cast<PrimitiveValue>(
+                    sNamedValue.second.u64);
+        }
+    }
+    return Status::OK;
+}
+
+// StructDescriptor -> C2StructDescriptor
+c2_status_t objcpy(C2StructDescriptor *d, const StructDescriptor &s) {
+    // TODO: Implement this when C2StructDescriptor can be dynamically
+    // constructed.
+    (void)d;
+    (void)s;
+    ALOGE("Conversion StructDescriptor -> C2StructDescriptor "
+            "not implemented.");
+    return C2_OMITTED;
+}
+
+// Finds or adds a hidl BaseBlock object from a given C2Handle* to a list and an
+// associated map.
+// Note: Native handles are not duplicated. The original handles must not be
+// closed before the transaction is complete.
+namespace /* unnamed */ {
+
+Status addBaseBlock(uint32_t* index, const C2Handle* handle,
+        std::vector<BaseBlock>* baseBlocks,
+        std::map<const C2Handle*, uint32_t>* baseBlockIndices) {
+    if (handle == nullptr) {
+        return Status::BAD_VALUE;
+    }
+    auto it = baseBlockIndices->find(handle);
+    if (it != baseBlockIndices->end()) {
+        *index = it->second;
+    } else {
+        *index = baseBlocks->size();
+        BaseBlock dBaseBlock;
+        // TODO: Use BufferPool.
+        dBaseBlock.type = BaseBlock::Type::NATIVE;
+        // This does not clone the handle.
+        dBaseBlock.nativeBlock =
+                reinterpret_cast<const native_handle_t*>(handle);
+        baseBlocks->push_back(dBaseBlock);
+        baseBlockIndices->emplace(handle, *index);
+    }
+    return Status::OK;
+}
+
+// C2Fence -> hidl_handle
+// Note: File descriptors are not duplicated. The original file descriptor must
+// not be closed before the transaction is complete.
+Status objcpy(hidl_handle* d, const C2Fence& s) {
+    (void)s; // TODO: implement s.fd()
+    int fenceFd = -1;
+    d->setTo(nullptr);
+    if (fenceFd >= 0) {
+        native_handle_t *handle = native_handle_create(1, 0);
+        if (!handle) {
+            return Status::NO_MEMORY;
+        }
+        handle->data[0] = fenceFd;
+        d->setTo(handle, true /* owns */);
+    }
+    return Status::OK;
+}
+
+// C2ConstLinearBlock -> Block
+// Note: Native handles are not duplicated. The original handles must not be
+// closed before the transaction is complete.
+Status objcpy(Block* d, const C2ConstLinearBlock& s,
+        std::vector<BaseBlock>* baseBlocks,
+        std::map<const C2Handle*, uint32_t>* baseBlockIndices) {
+    // Find the BaseBlock index.
+    // TODO: Use BufferPool.
+    Status status = addBaseBlock(
+            &d->index, s.handle(), baseBlocks, baseBlockIndices);
+    if (status != Status::OK) {
+        return status;
+    }
+
+    // Create the metadata.
+    C2Hidl_RangeInfo dRangeInfo;
+    dRangeInfo.offset = static_cast<uint32_t>(s.offset());
+    dRangeInfo.length = static_cast<uint32_t>(s.size());
+    status = createParamsBlob(&d->meta,
+            std::vector<C2Param*>{ &dRangeInfo });
+    if (status != Status::OK) {
+        return Status::BAD_VALUE;
+    }
+
+    // Copy the fence
+    return objcpy(&d->fence, s.fence());
+}
+
+// C2ConstGraphicBlock -> Block
+// Note: Native handles are not duplicated. The original handles must not be
+// closed before the transaction is complete.
+Status objcpy(Block* d, const C2ConstGraphicBlock& s,
+        std::vector<BaseBlock>* baseBlocks,
+        std::map<const C2Handle*, uint32_t>* baseBlockIndices) {
+    // Find the BaseBlock index.
+    // TODO: Use BufferPool.
+    Status status = addBaseBlock(
+            &d->index, s.handle(), baseBlocks, baseBlockIndices);
+    if (status != Status::OK) {
+        return status;
+    }
+
+    // Create the metadata.
+    C2Hidl_RectInfo dRectInfo;
+    C2Rect sRect = s.crop();
+    dRectInfo.left = static_cast<uint32_t>(sRect.left);
+    dRectInfo.top = static_cast<uint32_t>(sRect.top);
+    dRectInfo.width = static_cast<uint32_t>(sRect.width);
+    dRectInfo.height = static_cast<uint32_t>(sRect.height);
+    status = createParamsBlob(&d->meta,
+            std::vector<C2Param*>{ &dRectInfo });
+    if (status != Status::OK) {
+        return Status::BAD_VALUE;
+    }
+
+    // Copy the fence
+    return objcpy(&d->fence, s.fence());
+}
+
+// C2BufferData -> Buffer
+// This function only fills in d->blocks.
+Status objcpy(Buffer* d, const C2BufferData& s,
+        std::vector<BaseBlock>* baseBlocks,
+        std::map<const C2Handle*, uint32_t>* baseBlockIndices) {
+    Status status;
+    d->blocks.resize(
+            s.linearBlocks().size() +
+            s.graphicBlocks().size());
+    size_t i = 0;
+    for (const C2ConstLinearBlock& linearBlock : s.linearBlocks()) {
+        Block& dBlock = d->blocks[i++];
+        status = objcpy(
+                &dBlock, linearBlock, baseBlocks, baseBlockIndices);
+        if (status != Status::OK) {
+            return status;
+        }
+    }
+    for (const C2ConstGraphicBlock& graphicBlock : s.graphicBlocks()) {
+        Block& dBlock = d->blocks[i++];
+        status = objcpy(
+                &dBlock, graphicBlock, baseBlocks, baseBlockIndices);
+        if (status != Status::OK) {
+            return status;
+        }
+    }
+    return Status::OK;
+}
+
+// C2Buffer -> Buffer
+Status objcpy(Buffer* d, const C2Buffer& s,
+        std::vector<BaseBlock>* baseBlocks,
+        std::map<const C2Handle*, uint32_t>* baseBlockIndices) {
+    Status status = createParamsBlob(&d->info, s.info());
+    if (status != Status::OK) {
+        return status;
+    }
+    return objcpy(d, s.data(), baseBlocks, baseBlockIndices);
+}
+
+// C2InfoBuffer -> InfoBuffer
+Status objcpy(InfoBuffer* d, const C2InfoBuffer& s,
+        std::vector<BaseBlock>* baseBlocks,
+        std::map<const C2Handle*, uint32_t>* baseBlockIndices) {
+    // TODO: C2InfoBuffer is not implemented.
+    (void)d;
+    (void)s;
+    (void)baseBlocks;
+    (void)baseBlockIndices;
+    return Status::OK;
+    /*
+    // Stub implementation that may work in the future.
+    d->index = static_cast<uint32_t>(s.index());
+    d->buffer.info.resize(0);
+    return objcpy(&d->buffer, s.data(), baseBlocks, baseBlockIndices);
+    */
+}
+
+// C2FrameData -> FrameData
+Status objcpy(FrameData* d, const C2FrameData& s,
+        std::vector<BaseBlock>* baseBlocks,
+        std::map<const C2Handle*, uint32_t>* baseBlockIndices) {
+    d->flags = static_cast<hidl_bitfield<FrameData::Flags>>(s.flags);
+    objcpy(&d->ordinal, s.ordinal);
+
+    Status status;
+    d->buffers.resize(s.buffers.size());
+    size_t i = 0;
+    for (const std::shared_ptr<C2Buffer>& sBuffer : s.buffers) {
+        Buffer& dBuffer = d->buffers[i++];
+        if (!sBuffer) {
+            ALOGE("Null C2Buffer");
+            return Status::BAD_VALUE;
+        }
+        status = objcpy(&dBuffer, *sBuffer, baseBlocks, baseBlockIndices);
+        if (status != Status::OK) {
+            return status;
+        }
+    }
+
+    status = createParamsBlob(&d->configUpdate, s.configUpdate);
+    if (status != Status::OK) {
+        return status;
+    }
+
+    d->infoBuffers.resize(s.infoBuffers.size());
+    i = 0;
+    for (const std::shared_ptr<C2InfoBuffer>& sInfoBuffer : s.infoBuffers) {
+        InfoBuffer& dInfoBuffer = d->infoBuffers[i++];
+        if (!sInfoBuffer) {
+            ALOGE("Null C2InfoBuffer");
+            return Status::BAD_VALUE;
+        }
+        status = objcpy(&dInfoBuffer, *sInfoBuffer, baseBlocks, baseBlockIndices);
+        if (status != Status::OK) {
+            return status;
+        }
+    }
+
+    return status;
+}
+
+} // unnamed namespace
+
+// std::list<std::unique_ptr<C2Work>> -> WorkBundle
+// TODO: Connect with Bufferpool
+Status objcpy(WorkBundle* d, const std::list<std::unique_ptr<C2Work>>& s) {
+    Status status = Status::OK;
+
+    std::vector<BaseBlock> baseBlocks;
+    std::map<const C2Handle*, uint32_t> baseBlockIndices;
+    d->works.resize(s.size());
+    size_t i = 0;
+    for (const std::unique_ptr<C2Work>& sWork : s) {
+        Work &dWork = d->works[i++];
+        if (!sWork) {
+            ALOGW("Null C2Work encountered.");
+            continue;
+        }
+        status = objcpy(&dWork.input, sWork->input,
+                &baseBlocks, &baseBlockIndices);
+        if (status != Status::OK) {
+            return status;
+        }
+        if (sWork->worklets.size() == 0) {
+            ALOGW("Work with no worklets.");
+        } else {
+            if (sWork->worklets.size() > 1) {
+                ALOGW("Work with multiple worklets. "
+                        "Only the first worklet will be marshalled.");
+            }
+            if (!sWork->worklets.front()) {
+                ALOGE("Null worklet encountered.");
+                return Status::BAD_VALUE;
+            }
+
+            // Parcel the first worklet.
+            const C2Worklet &sWorklet = *sWork->worklets.front();
+            Worklet &dWorklet = dWork.worklet;
+
+            dWorklet.tunings.resize(sWorklet.tunings.size());
+            size_t j = 0;
+            for (const std::unique_ptr<C2Tuning>& sTuning : sWorklet.tunings) {
+                status = createParamsBlob(
+                        &dWorklet.tunings[j++],
+                        std::vector<C2Param*>
+                        { reinterpret_cast<C2Param*>(sTuning.get()) });
+                if (status != Status::OK) {
+                    return status;
+                }
+            }
+
+            dWorklet.failures.resize(sWorklet.failures.size());
+            j = 0;
+            for (const std::unique_ptr<C2SettingResult>& sFailure :
+                    sWorklet.failures) {
+                if (!sFailure) {
+                    ALOGE("Null C2SettingResult");
+                    return Status::BAD_VALUE;
+                }
+                status = objcpy(&dWorklet.failures[j++], *sFailure);
+                if (status != Status::OK) {
+                    return status;
+                }
+            }
+
+            status = objcpy(&dWorklet.output, sWorklet.output,
+                    &baseBlocks, &baseBlockIndices);
+            if (status != Status::OK) {
+                return status;
+            }
+        }
+        dWork.workletProcessed = sWork->workletsProcessed > 0;
+        dWork.result = static_cast<Status>(sWork->result);
+    }
+
+    d->baseBlocks = baseBlocks;
+
+    return Status::OK;
+}
+
+namespace /* unnamed */ {
+
+// hidl_handle -> C2Fence
+// Note: File descriptors are not duplicated. The original file descriptor must
+// not be closed before the transaction is complete.
+c2_status_t objcpy(C2Fence* d, const hidl_handle& s) {
+    // TODO: Implement.
+    (void)s;
+    *d = C2Fence();
+    return C2_OK;
+}
+
+// Buffer -> C2Buffer
+// Note: The native handles will be cloned.
+c2_status_t objcpy(std::shared_ptr<C2Buffer>* d, const Buffer& s,
+        const hidl_vec<BaseBlock>& baseBlocks) {
+    c2_status_t status;
+
+    // First, construct C2Buffer with blocks from s.blocks.
+    *d = nullptr;
+
+    // TODO: Only buffers with 1 block are supported.
+    if (s.blocks.size() == 1) {
+        // Obtain the BaseBlock.
+        const Block &sBlock = s.blocks[0];
+        if (sBlock.index >= baseBlocks.size()) {
+            ALOGE("Index into baseBlocks is out of range.");
+            return C2_BAD_VALUE;
+        }
+        const BaseBlock &sBaseBlock = baseBlocks[sBlock.index];
+
+        // Parse meta.
+        std::vector<C2Param*> sBlockMeta;
+        status = parseParamsBlob(&sBlockMeta, sBlock.meta);
+        if (status != C2_OK) {
+            ALOGE("Invalid block params blob.");
+            return C2_BAD_VALUE;
+        }
+
+        // Copy fence.
+        C2Fence dFence;
+        status = objcpy(&dFence, sBlock.fence);
+
+        // Construct a block.
+        switch (sBaseBlock.type) {
+        case BaseBlock::Type::NATIVE: {
+            const native_handle_t* sHandle = sBaseBlock.nativeBlock;
+            if (sHandle == nullptr) {
+                ALOGE("Null native handle in a block.");
+                return C2_BAD_VALUE;
+            }
+            sHandle = native_handle_clone(sHandle);
+            if (sHandle == nullptr) {
+                ALOGE("Cannot clone native handle.");
+                return C2_NO_MEMORY;
+            }
+            const C2Handle *sC2Handle =
+                    reinterpret_cast<const C2Handle*>(sHandle);
+
+            // Currently, there are only 2 types of C2Allocation: ion and
+            // gralloc.
+            if (C2AllocatorIon::isValid(sC2Handle)) {
+                // Check the block meta. It should have exactly 1 C2Info:
+                // C2Hidl_RangeInfo.
+                if ((sBlockMeta.size() != 1) || !sBlockMeta[0]) {
+                    ALOGE("Invalid block metadata for ion block.");
+                    return C2_BAD_VALUE;
+                }
+                if (sBlockMeta[0]->size() != sizeof(C2Hidl_RangeInfo)) {
+                    ALOGE("Invalid block metadata for ion block: range.");
+                    return C2_BAD_VALUE;
+                }
+                C2Hidl_RangeInfo *rangeInfo =
+                        reinterpret_cast<C2Hidl_RangeInfo*>(sBlockMeta[0]);
+
+                std::shared_ptr<C2Allocator> allocator;
+                c2_status_t status = GetCodec2PlatformAllocatorStore(
+                        )->fetchAllocator(
+                        C2PlatformAllocatorStore::ION,
+                        &allocator);
+                if (status != C2_OK) {
+                    ALOGE("Cannot fetch platform linear allocator.");
+                    return status;
+                }
+                std::shared_ptr<C2LinearAllocation> allocation;
+                status = allocator->priorLinearAllocation(
+                        sC2Handle, &allocation);
+                if (status != C2_OK) {
+                    ALOGE("Error constructing linear allocation.");
+                    return status;
+                } else if (!allocation) {
+                    ALOGE("Null linear allocation.");
+                    return C2_BAD_VALUE;
+                }
+                std::shared_ptr<C2LinearBlock> block =
+                        _C2BlockFactory::CreateLinearBlock(allocation);
+                if (!block) {
+                    ALOGE("Cannot create a block.");
+                    return C2_BAD_VALUE;
+                }
+                *d = C2Buffer::CreateLinearBuffer(block->share(
+                        rangeInfo->offset, rangeInfo->length, dFence));
+                if (!(*d)) {
+                    ALOGE("Cannot create a linear buffer.");
+                    return C2_BAD_VALUE;
+                }
+            } else if (C2AllocatorGralloc::isValid(sC2Handle)) {
+                // Check the block meta. It should have exactly 1 C2Info:
+                // C2Hidl_RectInfo.
+                if ((sBlockMeta.size() != 1) || !sBlockMeta[0]) {
+                    ALOGE("Invalid block metadata for graphic block.");
+                    return C2_BAD_VALUE;
+                }
+                if (sBlockMeta[0]->size() != sizeof(C2Hidl_RectInfo)) {
+                    ALOGE("Invalid block metadata for graphic block: crop rect.");
+                    return C2_BAD_VALUE;
+                }
+                C2Hidl_RectInfo *rectInfo =
+                        reinterpret_cast<C2Hidl_RectInfo*>(sBlockMeta[0]);
+
+                std::shared_ptr<C2Allocator> allocator;
+                c2_status_t status = GetCodec2PlatformAllocatorStore(
+                        )->fetchAllocator(
+                        C2PlatformAllocatorStore::GRALLOC,
+                        &allocator);
+                if (status != C2_OK) {
+                    ALOGE("Cannot fetch platform graphic allocator.");
+                    return status;
+                }
+
+                std::shared_ptr<C2GraphicAllocation> allocation;
+                status = allocator->priorGraphicAllocation(
+                        sC2Handle, &allocation);
+                if (status != C2_OK) {
+                    ALOGE("Error constructing graphic allocation.");
+                    return status;
+                } else if (!allocation) {
+                    ALOGE("Null graphic allocation.");
+                    return C2_BAD_VALUE;
+                }
+                std::shared_ptr<C2GraphicBlock> block =
+                        _C2BlockFactory::CreateGraphicBlock(allocation);
+                if (!block) {
+                    ALOGE("Cannot create a block.");
+                    return C2_BAD_VALUE;
+                }
+                *d = C2Buffer::CreateGraphicBuffer(block->share(
+                        C2Rect(rectInfo->width, rectInfo->height,
+                               rectInfo->left, rectInfo->top),
+                        dFence));
+                if (!(*d)) {
+                    ALOGE("Cannot create a graphic buffer.");
+                    return C2_BAD_VALUE;
+                }
+            } else {
+                ALOGE("Unknown handle type.");
+                return C2_BAD_VALUE;
+            }
+            break;
+        }
+        case BaseBlock::Type::POOLED: {
+            // TODO: Implement. Use BufferPool.
+            return C2_OMITTED;
+        }
+        default:
+            ALOGE("Invalid BaseBlock type.");
+            return C2_BAD_VALUE;
+        }
+    } else {
+        ALOGE("Currently a buffer must contain exactly 1 block.");
+        return C2_BAD_VALUE;
+    }
+
+    // Parse info
+    std::vector<C2Param*> params;
+    status = parseParamsBlob(&params, s.info);
+    if (status != C2_OK) {
+        ALOGE("Invalid buffer params blob.");
+        return status;
+    }
+    for (C2Param* param : params) {
+        if (param == nullptr) {
+            ALOGE("Null buffer param encountered.");
+            return C2_BAD_VALUE;
+        }
+        std::shared_ptr<C2Param> c2param(
+                C2Param::Copy(*param).release());
+        if (!c2param) {
+            ALOGE("Invalid buffer param inside a blob.");
+            return C2_BAD_VALUE;
+        }
+        status = (*d)->setInfo(std::static_pointer_cast<C2Info>(c2param));
+        if (status != C2_OK) {
+            ALOGE("C2Buffer::setInfo failed().");
+            return C2_BAD_VALUE;
+        }
+    }
+
+    return C2_OK;
+}
+
+// FrameData -> C2FrameData
+c2_status_t objcpy(C2FrameData* d, const FrameData& s,
+        const hidl_vec<BaseBlock>& baseBlocks) {
+    c2_status_t status;
+    d->flags = static_cast<C2FrameData::flags_t>(s.flags);
+    objcpy(&d->ordinal, s.ordinal);
+    d->buffers.clear();
+    d->buffers.reserve(s.buffers.size());
+    for (const Buffer& sBuffer : s.buffers) {
+        std::shared_ptr<C2Buffer> dBuffer;
+        status = objcpy(&dBuffer, sBuffer, baseBlocks);
+        if (status != C2_OK) {
+            return status;
+        }
+        d->buffers.emplace_back(dBuffer);
+    }
+
+    std::vector<C2Param*> params;
+    status = parseParamsBlob(&params, s.configUpdate);
+    if (status != C2_OK) {
+        ALOGE("Failed to parse frame data params.");
+        return status;
+    }
+    d->configUpdate.clear();
+    for (C2Param* param : params) {
+        d->configUpdate.emplace_back(C2Param::Copy(*param));
+        if (!d->configUpdate.back()) {
+            ALOGE("Unexpected error while parsing frame data params.");
+            return C2_BAD_VALUE;
+        }
+    }
+
+    // TODO: Implement this once C2InfoBuffer has constructors.
+    d->infoBuffers.clear();
+    return C2_OK;
+}
+
+} // unnamed namespace
+
+// WorkBundle -> std::list<std::unique_ptr<C2Work>>
+// TODO: Connect with Bufferpool
+c2_status_t objcpy(std::list<std::unique_ptr<C2Work>>* d, const WorkBundle& s) {
+    c2_status_t status;
+    d->clear();
+    for (const Work& sWork : s.works) {
+        d->emplace_back(std::make_unique<C2Work>());
+        C2Work& dWork = *d->back();
+
+        // input
+        status = objcpy(&dWork.input, sWork.input, s.baseBlocks);
+        if (status != C2_OK) {
+            ALOGE("Error constructing C2Work's input.");
+            return C2_BAD_VALUE;
+        }
+
+        // worklet(s)
+        // TODO: Currently, tunneling is not supported.
+        if (sWork.workletProcessed) {
+            dWork.worklets.clear();
+            dWork.workletsProcessed = 1;
+
+            const Worklet &sWorklet = sWork.worklet;
+            std::unique_ptr<C2Worklet> dWorklet = std::make_unique<C2Worklet>();
+
+            // tunings
+            dWorklet->tunings.clear();
+            dWorklet->tunings.reserve(sWorklet.tunings.size());
+            for (const Params& sTuning : sWorklet.tunings) {
+                std::vector<C2Param*> dParams;
+                status = parseParamsBlob(&dParams, sTuning);
+                if (status != C2_OK) {
+                    ALOGE("Failed to parse C2Tuning in C2Worklet.");
+                    return C2_BAD_VALUE;
+                }
+                for (C2Param* param : dParams) {
+                    std::unique_ptr<C2Param> dParam = C2Param::Copy(*param);
+                    if (!dParam) {
+                        ALOGE("Null C2Tuning encountered while "
+                                "parsing C2Worklet.");
+                        return C2_BAD_VALUE;
+                    }
+                    dWorklet->tunings.emplace_back(
+                            std::unique_ptr<C2Tuning>(
+                            reinterpret_cast<C2Tuning*>(
+                            dParam.release())));
+                }
+            }
+            // failures
+            dWorklet->failures.clear();
+            dWorklet->failures.reserve(sWorklet.failures.size());
+            for (const SettingResult& sFailure : sWorklet.failures) {
+                std::unique_ptr<C2SettingResult> dFailure;
+                status = objcpy(&dFailure, sFailure);
+                if (status != C2_OK) {
+                    ALOGE("Failed to create C2SettingResult in C2Worklet.");
+                    return C2_BAD_VALUE;
+                }
+                dWorklet->failures.emplace_back(std::move(dFailure));
+            }
+            // output
+            status = objcpy(&dWorklet->output, sWorklet.output, s.baseBlocks);
+            if (status != C2_OK) {
+                ALOGE("Failed to create output C2FrameData.");
+                return C2_BAD_VALUE;
+            }
+            dWork.worklets.emplace_back(std::move(dWorklet));
+        } else {
+            dWork.worklets.clear();
+            dWork.workletsProcessed = 0;
+        }
+
+        // result
+        dWork.result = static_cast<c2_status_t>(sWork.result);
+    }
+
+    return C2_OK;
+}
+
+// Params -> std::vector<C2Param*>
+c2_status_t parseParamsBlob(std::vector<C2Param*> *params, const hidl_vec<uint8_t> &blob) {
+    // assuming blob is const here
+    size_t size = blob.size();
+    const uint8_t *data = blob.data();
+    C2Param *p = nullptr;
+
+    do {
+        p = C2ParamUtils::ParseFirst(data, size);
+        if (p) {
+            params->emplace_back(p);
+            size -= p->size();
+            data += p->size();
+        }
+    } while (p);
+
+    return size == 0 ? C2_OK : C2_BAD_VALUE;
+}
+
+namespace /* unnamed */ {
+
+/**
+ * Concatenates a list of C2Params into a params blob.
+ * \param[out] blob target blob
+ * \param[in] params parameters to concatenate
+ * \retval C2_OK if the blob was successfully created
+ * \retval C2_BAD_VALUE if the blob was not successful (this only happens if the parameters were
+ *         not const)
+ */
+template<typename T>
+Status _createParamsBlob(hidl_vec<uint8_t> *blob, const T &params) {
+    // assuming the parameter values are const
+    size_t size = 0;
+    for (const auto &p : params) {
+        size += p->size();
+    }
+    blob->resize(size);
+    size_t ix = 0;
+    for (const auto &p : params) {
+        // NEVER overwrite even if param values (e.g. size) changed
+        size_t paramSize = std::min(p->size(), size - ix);
+//        memcpy(&blob[ix], &*p, paramSize);
+        std::copy(
+                reinterpret_cast<const uint8_t*>(&*p),
+                reinterpret_cast<const uint8_t*>(&*p) + paramSize,
+                &blob[ix]);
+        ix += paramSize;
+    }
+    blob->resize(ix);
+    return ix == size ? Status::OK : Status::CORRUPTED;
+}
+
+} // unnamed namespace
+
+// std::vector<const C2Param*> -> Params
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<const C2Param*> &params) {
+    return _createParamsBlob(blob, params);
+}
+
+// std::vector<C2Param*> -> Params
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<C2Param*> &params) {
+    return _createParamsBlob(blob, params);
+}
+
+// std::vector<std::unique_ptr<C2Param>> -> Params
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<std::unique_ptr<C2Param>> &params) {
+    return _createParamsBlob(blob, params);
+}
+
+// std::vector<std::unique_ptr<C2Tuning>> -> Params
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<std::unique_ptr<C2Tuning>> &params) {
+    return _createParamsBlob(blob, params);
+}
+
+// std::vector<std::shared_ptr<const C2Info>> -> Params
+Status createParamsBlob(
+        hidl_vec<uint8_t> *blob,
+        const std::vector<std::shared_ptr<const C2Info>> &params) {
+    return _createParamsBlob(blob, params);
+}
+
+// Params -> std::vector<std::unique_ptr<C2Param>>
+c2_status_t copyParamsFromBlob(
+        std::vector<std::unique_ptr<C2Param>>* params,
+        Params blob) {
+    std::vector<C2Param*> paramPointers;
+    c2_status_t status = parseParamsBlob(&paramPointers, blob);
+    if (status != C2_OK) {
+        ALOGE("copyParamsFromBlob -- blob parsing failed.");
+        return status;
+    }
+    params->resize(paramPointers.size());
+    size_t i = 0;
+    for (C2Param* const& paramPointer : paramPointers) {
+        if (!paramPointer) {
+            ALOGE("copyParamsFromBlob -- corrupted params blob.");
+            return C2_BAD_VALUE;
+        }
+        (*params)[i++] = C2Param::Copy(*paramPointer);
+    }
+    return C2_OK;
+}
+
+// Params -> update std::vector<std::unique_ptr<C2Param>>
+c2_status_t updateParamsFromBlob(
+        const std::vector<C2Param*>& params,
+        const Params& blob) {
+    std::unordered_map<uint32_t, C2Param*> index2param;
+    for (C2Param* const& param : params) {
+        if (!param) {
+            ALOGE("updateParamsFromBlob -- corrupted input params.");
+            return C2_BAD_VALUE;
+        }
+        if (index2param.find(param->index()) == index2param.end()) {
+            index2param.emplace(param->index(), param);
+        }
+    }
+
+    std::vector<C2Param*> paramPointers;
+    c2_status_t status = parseParamsBlob(&paramPointers, blob);
+    if (status != C2_OK) {
+        ALOGE("updateParamsFromBlob -- blob parsing failed.");
+        return status;
+    }
+
+    for (C2Param* const& paramPointer : paramPointers) {
+        if (!paramPointer) {
+            ALOGE("updateParamsFromBlob -- corrupted param in blob.");
+            return C2_BAD_VALUE;
+        }
+        decltype(index2param)::iterator i = index2param.find(
+                paramPointer->index());
+        if (i == index2param.end()) {
+            ALOGW("updateParamsFromBlob -- unseen param index.");
+            continue;
+        }
+        if (!i->second->updateFrom(*paramPointer)) {
+            ALOGE("updateParamsFromBlob -- mismatching sizes: "
+                    "%u vs %u (index = %u).",
+                    static_cast<unsigned>(params.size()),
+                    static_cast<unsigned>(paramPointer->size()),
+                    static_cast<unsigned>(i->first));
+            return C2_BAD_VALUE;
+        }
+    }
+    return C2_OK;
+}
+
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index d7709a0..47afd42 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -6,6 +6,9 @@
     ],
 
     vendor_available: false,
+    vndk: {
+        enabled: true,
+    },
 }
 
 cc_library_shared {
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
index 3d7fc8d..a90e094 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
@@ -646,4 +646,8 @@
     return mImpl->status();
 }
 
+bool C2AllocatorGralloc::isValid(const C2Handle* const o) {
+    return C2HandleGralloc::isValid(o);
+}
+
 } // namespace android
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
index 6b0cffb..cf7658a 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
@@ -466,5 +466,9 @@
     return ret;
 }
 
+bool C2AllocatorIon::isValid(const C2Handle* const o) {
+    return C2HandleIon::isValid(o);
+}
+
 } // namespace android
 
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp b/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
index 3571ed5..1b1b9be 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
@@ -43,8 +43,8 @@
     return Void();
 }
 
-Accessor::Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear)
-    : mImpl(new Impl(allocator, linear)) {}
+Accessor::Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator)
+    : mImpl(new Impl(allocator)) {}
 
 Accessor::~Accessor() {
 }
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Accessor.h b/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
index 6fd82ba..ad42245 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
@@ -20,7 +20,6 @@
 #include <android/hardware/media/bufferpool/1.0/IAccessor.h>
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
-#include <C2Buffer.h>
 #include <BufferPoolTypes.h>
 #include "BufferStatus.h"
 
@@ -53,9 +52,8 @@
      * Creates a buffer pool accessor which uses the specified allocator.
      *
      * @param allocator buffer allocator.
-     * @param linear    whether the allocator is linear or not.
      */
-    Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear);
+    explicit Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator);
 
     /** Destructs a buffer pool accessor. */
     ~Accessor();
diff --git a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
index f8aec53..32d76c0 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
@@ -22,7 +22,6 @@
 #include <time.h>
 #include <unistd.h>
 #include <utils/Log.h>
-#include <C2Buffer.h>
 #include "AccessorImpl.h"
 #include "Connection.h"
 
@@ -38,48 +37,18 @@
     BufferId mId;
     size_t mOwnerCount;
     size_t mTransactionCount;
-    const bool mLinear;
-    const std::shared_ptr<C2LinearAllocation> mLinearAllocation;
-    const std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
+    const std::shared_ptr<BufferPoolAllocation> mAllocation;
     const std::vector<uint8_t> mConfig;
 
     InternalBuffer(
             BufferId id,
-            const std::shared_ptr<C2LinearAllocation> &alloc,
+            const std::shared_ptr<BufferPoolAllocation> &alloc,
             const std::vector<uint8_t> &allocConfig)
             : mId(id), mOwnerCount(0), mTransactionCount(0),
-            mLinear(true), mLinearAllocation(alloc),
-            mGraphicAllocation(nullptr),
-            mConfig(allocConfig) {}
-
-    InternalBuffer(
-            BufferId id,
-            const std::shared_ptr<C2GraphicAllocation> &alloc,
-            const std::vector<uint8_t> &allocConfig)
-            : mId(id), mOwnerCount(0), mTransactionCount(0),
-            mLinear(false), mLinearAllocation(nullptr),
-            mGraphicAllocation(alloc),
-            mConfig(allocConfig) {}
+            mAllocation(alloc), mConfig(allocConfig) {}
 
     const native_handle_t *handle() {
-        if (mLinear) {
-            return mLinearAllocation->handle();
-        } else {
-            return mGraphicAllocation->handle();
-        }
-    }
-
-    // TODO : support non exact matching. e.g) capacity
-    bool isRecyclable(const std::vector<uint8_t> &config) {
-        if (mConfig.size() == config.size()) {
-            for (size_t i = 0; i < config.size(); ++i) {
-                if (mConfig[i] != config[i]) {
-                    return false;
-                }
-            }
-                return true;
-        }
-        return false;
+        return mAllocation->handle();
     }
 };
 
@@ -154,8 +123,8 @@
 uint32_t Accessor::Impl::sSeqId = time(NULL);
 
 Accessor::Impl::Impl(
-        const std::shared_ptr<C2Allocator> &allocator, bool linear)
-        : mAllocator(allocator), mLinear(linear) {}
+        const std::shared_ptr<BufferPoolAllocator> &allocator)
+        : mAllocator(allocator) {}
 
 Accessor::Impl::~Impl() {
 }
@@ -193,14 +162,8 @@
     std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
     mBufferPool.processStatusMessages();
     ResultStatus status = ResultStatus::OK;
-    if (!mBufferPool.getFreeBuffer(params, bufferId, handle)) {
-        if (mLinear) {
-            status = mBufferPool.getNewLinearBuffer(
-                    mAllocator, params, bufferId, handle);
-        } else {
-            status = mBufferPool.getNewGraphicBuffer(
-                    mAllocator, params, bufferId, handle);
-        }
+    if (!mBufferPool.getFreeBuffer(mAllocator, params, bufferId, handle)) {
+        status = mBufferPool.getNewBuffer(mAllocator, params, bufferId, handle);
         ALOGV("create a buffer %d : %u %p",
               status == ResultStatus::OK, *bufferId, *handle);
     }
@@ -437,12 +400,13 @@
 }
 
 bool Accessor::Impl::BufferPool::getFreeBuffer(
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
         const std::vector<uint8_t> &params, BufferId *pId,
         const native_handle_t** handle) {
     auto bufferIt = mFreeBuffers.begin();
     for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
         BufferId bufferId = *bufferIt;
-        if (mBuffers[bufferId]->isRecyclable(params)) {
+        if (allocator->compatible(params, mBuffers[bufferId]->mConfig)) {
             break;
         }
     }
@@ -457,81 +421,30 @@
     return false;
 }
 
-ResultStatus Accessor::Impl::BufferPool::getNewLinearBuffer(
-        const std::shared_ptr<C2Allocator> &allocator,
+ResultStatus Accessor::Impl::BufferPool::getNewBuffer(
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
         const std::vector<uint8_t> &params, BufferId *pId,
         const native_handle_t** handle) {
-    union LinearParam {
-        struct {
-            uint32_t capacity;
-            C2MemoryUsage usage;
-        } data;
-        uint8_t array[0];
-        LinearParam() : data{0, {0, 0}} {}
-    } linearParam;
-    memcpy(&linearParam, params.data(),
-           std::min(sizeof(linearParam), params.size()));
-    std::shared_ptr<C2LinearAllocation> linearAlloc;
-    c2_status_t status = allocator->newLinearAllocation(
-            linearParam.data.capacity, linearParam.data.usage, &linearAlloc);
-    if (status == C2_OK) {
+    std::shared_ptr<BufferPoolAllocation> alloc;
+    ResultStatus status = allocator->allocate(params, &alloc);
+
+    if (status == ResultStatus::OK) {
         BufferId bufferId = mSeq++;
         std::unique_ptr<InternalBuffer> buffer =
                 std::make_unique<InternalBuffer>(
-                        bufferId, linearAlloc, params);
+                        bufferId, alloc, params);
         if (buffer) {
             auto res = mBuffers.insert(std::make_pair(
                     bufferId, std::move(buffer)));
             if (res.second) {
-                *handle = linearAlloc->handle();
+                *handle = alloc->handle();
                 *pId = bufferId;
                 return ResultStatus::OK;
             }
         }
         return ResultStatus::NO_MEMORY;
     }
-    // TODO: map C2 error code
-    return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus Accessor::Impl::BufferPool::getNewGraphicBuffer(
-        const std::shared_ptr<C2Allocator> &allocator,
-        const std::vector<uint8_t> &params, BufferId *pId,
-        const native_handle_t** handle) {
-    union GraphicParam {
-        struct {
-            uint32_t width;
-            uint32_t height;
-            uint32_t format;
-            C2MemoryUsage usage;
-        } data;
-        uint8_t array[0];
-        GraphicParam() : data{0, 0, 0, {0, 0}} {}
-    } graphicParam;
-    memcpy(&graphicParam, params.data(),
-           std::min(sizeof(graphicParam), params.size()));
-    std::shared_ptr<C2GraphicAllocation> graphicAlloc;
-    c2_status_t status = allocator->newGraphicAllocation(
-            graphicParam.data.width, graphicParam.data.height,
-            graphicParam.data.format, graphicParam.data.usage, &graphicAlloc);
-    if (status == C2_OK) {
-        BufferId bufferId = mSeq;
-        std::unique_ptr<InternalBuffer> buffer =
-                std::make_unique<InternalBuffer>(
-                        bufferId, graphicAlloc, params);
-        if (buffer) {
-            auto res = mBuffers.insert(std::make_pair(
-                    bufferId, std::move(buffer)));
-            if (res.second) {
-                *handle = graphicAlloc->handle();
-                *pId = bufferId;
-                return ResultStatus::OK;
-            }
-        }
-        return ResultStatus::NO_MEMORY;
-    }
-    // TODO: map C2 error code
-    return ResultStatus::CRITICAL_ERROR;
+    return status;
 }
 
 }  // namespace implementation
diff --git a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
index 92d926c..1260550 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
@@ -35,7 +35,7 @@
  * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
 class Accessor::Impl {
 public:
-    Impl(const std::shared_ptr<C2Allocator> &allocator, bool linear);
+    Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
 
     ~Impl();
 
@@ -64,8 +64,7 @@
     static uint32_t sSeqId;
     static int32_t sPid;
 
-    const std::shared_ptr<C2Allocator> mAllocator;
-    bool mLinear;
+    const std::shared_ptr<BufferPoolAllocator> mAllocator;
 
     /**
      * Buffer pool implementation.
@@ -172,6 +171,7 @@
         /**
          * Recycles a existing free buffer if it is possible.
          *
+         * @param allocator the buffer allocator
          * @param params    the allocation parameters.
          * @param pId       the id of the recycled buffer.
          * @param handle    the native handle of the recycled buffer.
@@ -179,40 +179,25 @@
          * @return {@code true} when a buffer is recycled, {@code false}
          *         otherwise.
          */
-        bool getFreeBuffer(const std::vector<uint8_t> &params, BufferId *pId,
-                           const native_handle_t **handle);
+        bool getFreeBuffer(
+                const std::shared_ptr<BufferPoolAllocator> &allocator,
+                const std::vector<uint8_t> &params,
+                BufferId *pId, const native_handle_t **handle);
 
         /**
-         * Creates a new linear buffer.
+         * Creates a new buffer.
          *
-         * @param allocator the linear buffer allocator
+         * @param allocator the buffer allocator
          * @param params    the allocator parameters
          * @param pId       the buffer id for the newly allocated buffer.
          * @param handle    the native handle for the newly allocated buffer.
          *
-         * @return OK when a linear allocation is successfully allocated.
+         * @return OK when an allocation is successfully allocated.
          *         NO_MEMORY when there is no memory.
          *         CRITICAL_ERROR otherwise.
          */
-        ResultStatus getNewLinearBuffer(
-                const std::shared_ptr<C2Allocator> &allocator,
-                const std::vector<uint8_t> &params, BufferId *pId,
-                const native_handle_t **handle);
-
-        /**
-         * Creates a new graphic buffer.
-         *
-         * @param allocator the graphic buffer allocator
-         * @param params    the allocator parameters
-         * @param pId       the buffer id for the newly allocated buffer.
-         * @param handle    the native handle for the newly allocated buffer.
-         *
-         * @return OK when a graphic allocation is successfully allocated.
-         *         NO_MEMORY when there is no memory.
-         *         CRITICAL_ERROR otherwise.
-         */
-        ResultStatus getNewGraphicBuffer(
-                const std::shared_ptr<C2Allocator> &allocator,
+        ResultStatus getNewBuffer(
+                const std::shared_ptr<BufferPoolAllocator> &allocator,
                 const std::vector<uint8_t> &params, BufferId *pId,
                 const native_handle_t **handle);
 
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Android.bp b/media/libstagefright/codec2/vndk/bufferpool/Android.bp
index c7aa07b..1ea1f35 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/Android.bp
+++ b/media/libstagefright/codec2/vndk/bufferpool/Android.bp
@@ -24,7 +24,6 @@
         "libhidlbase",
         "libhidltransport",
         "liblog",
-        "libstagefright_codec2",
         "libutils",
         "android.hardware.media.bufferpool@1.0",
     ],
diff --git a/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp b/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
index 97efee4..89aee8b 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
@@ -36,8 +36,7 @@
     ResultStatus registerSender(const sp<IAccessor> &accessor,
                                 ConnectionId *pConnectionId);
 
-    ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
-                        bool linear,
+    ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
                         ConnectionId *pConnectionId);
 
     ResultStatus close(ConnectionId connectionId);
@@ -139,10 +138,9 @@
 }
 
 ResultStatus ClientManager::Impl::create(
-        const std::shared_ptr<C2Allocator> &allocator,
-        bool linear,
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
         ConnectionId *pConnectionId) {
-    const sp<Accessor> accessor = new Accessor(allocator, linear);
+    const sp<Accessor> accessor = new Accessor(allocator);
     if (!accessor || !accessor->isValid()) {
         return ResultStatus::CRITICAL_ERROR;
     }
@@ -273,11 +271,10 @@
 }
 
 ResultStatus ClientManager::create(
-        const std::shared_ptr<C2Allocator> &allocator,
-        bool linear,
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
         ConnectionId *pConnectionId) {
     if (mImpl) {
-        return mImpl->create(allocator, linear, pConnectionId);
+        return mImpl->create(allocator, pConnectionId);
     }
     return ResultStatus::CRITICAL_ERROR;
 }
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
index 4b7363f..0cf023c 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
@@ -22,9 +22,8 @@
 #include <fmq/MessageQueue.h>
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
-#include <C2Buffer.h>
 
-struct C2_HIDE _C2BlockPoolData {
+struct __attribute__((visibility("hidden"))) _C2BlockPoolData {
     uint32_t mId; //BufferId
     native_handle_t *mHandle;
 
@@ -57,6 +56,52 @@
 typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
 typedef BufferStatusQueue::Descriptor QueueDescriptor;
 
+/**
+ * Allocation wrapper class for buffer pool.
+ */
+struct BufferPoolAllocation {
+    const native_handle_t *mHandle;
+
+    const native_handle_t *handle() {
+        return mHandle;
+    }
+
+    BufferPoolAllocation(const native_handle_t *handle) : mHandle(handle) {}
+
+    ~BufferPoolAllocation() {};
+};
+
+/**
+ * Allocator wrapper class for buffer pool.
+ */
+class BufferPoolAllocator {
+public:
+
+    /**
+     * Allocate an allocation(buffer) for bufer pool.
+     *
+     * @param params    allocation parameters
+     * @param alloc     created allocation
+     *
+     * @return OK when an allocation is created successfully.
+     */
+    virtual ResultStatus allocate(
+            const std::vector<uint8_t> &params,
+            std::shared_ptr<BufferPoolAllocation> *alloc) = 0;
+
+    /**
+     * Returns whether allocation parameters of an old allocation are
+     * compatible with new allocation parameters.
+     */
+    virtual bool compatible(const std::vector<uint8_t> &newParams,
+                            const std::vector<uint8_t> &oldParams) = 0;
+
+protected:
+    BufferPoolAllocator() = default;
+
+    virtual ~BufferPoolAllocator() = default;
+};
+
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace bufferpool
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h b/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
index 412fa59..f91f46b 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
@@ -20,7 +20,6 @@
 #include <android/hardware/media/bufferpool/1.0/IClientManager.h>
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
-#include <C2Buffer.h>
 #include <memory>
 #include <BufferPoolTypes.h>
 
@@ -52,7 +51,6 @@
      * Creates a local connection with a newly created buffer pool.
      *
      * @param allocator     for new buffer allocation.
-     * @param linear        whether the allocator is linear or not.
      * @param pConnectionId Id of the created connection. This is
      *                      system-wide unique.
      *
@@ -61,8 +59,7 @@
      *         NO_MEMORY when there is no memory.
      *         CRITICAL_ERROR otherwise.
      */
-    ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
-                        bool linear,
+    ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
                         ConnectionId *pConnectionId);
 
     /**
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/Android.bp b/media/libstagefright/codec2/vndk/bufferpool/vts/Android.bp
index 074d4bc..62286f3 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/vts/Android.bp
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/Android.bp
@@ -18,6 +18,7 @@
     name: "VtsVndkHidlBufferpoolV1_0TargetSingleTest",
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: [
+        "allocator.cpp",
         "single.cpp",
     ],
     static_libs: [
@@ -37,6 +38,7 @@
     name: "VtsVndkHidlBufferpoolV1_0TargetMultiTest",
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: [
+        "allocator.cpp",
         "multi.cpp",
     ],
     static_libs: [
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.cpp b/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.cpp
new file mode 100644
index 0000000..230ee3f
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <C2Buffer.h>
+#include "allocator.h"
+
+union Params {
+  struct {
+    uint32_t capacity;
+    C2MemoryUsage usage;
+  } data;
+  uint8_t array[0];
+  Params() : data{0, {0, 0}} {}
+  Params(uint32_t size)
+      : data{size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}} {}
+};
+
+struct AllocationDtor {
+  AllocationDtor(const std::shared_ptr<C2LinearAllocation> &alloc)
+      : mAlloc(alloc) {}
+
+  void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+  const std::shared_ptr<C2LinearAllocation> mAlloc;
+};
+
+ResultStatus VtsBufferPoolAllocator::allocate(
+    const std::vector<uint8_t> &params,
+    std::shared_ptr<BufferPoolAllocation> *alloc) {
+  Params ionParams;
+  memcpy(&ionParams, params.data(), std::min(sizeof(Params), params.size()));
+
+  std::shared_ptr<C2LinearAllocation> linearAlloc;
+  c2_status_t status = mAllocator->newLinearAllocation(
+      ionParams.data.capacity, ionParams.data.usage, &linearAlloc);
+  if (status == C2_OK && linearAlloc) {
+    BufferPoolAllocation *ptr = new BufferPoolAllocation(linearAlloc->handle());
+    if (ptr) {
+      *alloc = std::shared_ptr<BufferPoolAllocation>(
+          ptr, AllocationDtor(linearAlloc));
+      if (*alloc) {
+        return ResultStatus::OK;
+      }
+      delete ptr;
+      return ResultStatus::NO_MEMORY;
+    }
+  }
+  return ResultStatus::CRITICAL_ERROR;
+}
+
+bool VtsBufferPoolAllocator::compatible(const std::vector<uint8_t> &newParams,
+                                        const std::vector<uint8_t> &oldParams) {
+  size_t newSize = newParams.size();
+  size_t oldSize = oldParams.size();
+  if (newSize == oldSize) {
+    for (size_t i = 0; i < newSize; ++i) {
+      if (newParams[i] != oldParams[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+  return false;
+}
+
+void getVtsAllocatorParams(std::vector<uint8_t> *params) {
+  constexpr static int kAllocationSize = 1024 * 10;
+  Params ionParams(kAllocationSize);
+
+  params->assign(ionParams.array, ionParams.array + sizeof(ionParams));
+}
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.h b/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.h
new file mode 100644
index 0000000..2fbb7fb
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 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 VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
+#define VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
+
+#include <BufferPoolTypes.h>
+
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::
+    BufferPoolAllocation;
+using android::hardware::media::bufferpool::V1_0::implementation::
+    BufferPoolAllocator;
+
+// buffer allocator for the tests
+class VtsBufferPoolAllocator : public BufferPoolAllocator {
+ public:
+  VtsBufferPoolAllocator(const std::shared_ptr<C2Allocator> &allocator)
+      : mAllocator(allocator) {}
+
+  ~VtsBufferPoolAllocator() override {}
+
+  ResultStatus allocate(const std::vector<uint8_t> &params,
+                        std::shared_ptr<BufferPoolAllocation> *alloc) override;
+
+  bool compatible(const std::vector<uint8_t> &newParams,
+                  const std::vector<uint8_t> &oldParams) override;
+
+ private:
+  const std::shared_ptr<C2Allocator> mAllocator;
+};
+
+// retrieve buffer allocator paramters
+void getVtsAllocatorParams(std::vector<uint8_t> *params);
+
+#endif  // VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/multi.cpp b/media/libstagefright/codec2/vndk/bufferpool/vts/multi.cpp
index 3ad8b6c..35127b8 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/vts/multi.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/multi.cpp
@@ -35,6 +35,7 @@
 #include <iostream>
 #include <memory>
 #include <vector>
+#include "allocator.h"
 
 using android::C2AllocatorIon;
 using android::C2PlatformAllocatorStore;
@@ -50,9 +51,6 @@
 
 namespace {
 
-// Buffer allocation size for tests.
-constexpr static int kAllocationSize = 1024 * 10;
-
 // communication message types between processes.
 enum PipeCommand : int32_t {
     INIT_OK = 0,
@@ -95,11 +93,14 @@
     mManager = ClientManager::getInstance();
     ASSERT_NE(mManager, nullptr);
 
-    mAllocator =
+    std::shared_ptr<C2Allocator> allocator =
         std::make_shared<C2AllocatorIon>(C2PlatformAllocatorStore::ION);
+    ASSERT_TRUE((bool)allocator);
+
+    mAllocator = std::make_shared<VtsBufferPoolAllocator>(allocator);
     ASSERT_TRUE((bool)mAllocator);
 
-    status = mManager->create(mAllocator, true, &mConnectionId);
+    status = mManager->create(mAllocator, &mConnectionId);
     ASSERT_TRUE(status == ResultStatus::OK);
 
     status = mManager->getAccessor(mConnectionId, &mAccessor);
@@ -121,26 +122,12 @@
 
   android::sp<ClientManager> mManager;
   android::sp<IAccessor> mAccessor;
-  std::shared_ptr<C2Allocator> mAllocator;
+  std::shared_ptr<BufferPoolAllocator> mAllocator;
   ConnectionId mConnectionId;
   pid_t mReceiverPid;
   int mCommandPipeFds[2];
   int mResultPipeFds[2];
 
-  void getAllocationParams(std::vector<uint8_t>* vecParams) {
-    union Params {
-      struct {
-        uint32_t capacity;
-        C2MemoryUsage usage;
-      } data;
-      uint8_t array[0];
-      Params()
-          : data{kAllocationSize,
-                 {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}} {}
-    } params;
-    vecParams->assign(params.array, params.array + sizeof(params));
-  }
-
   bool sendMessage(int *pipes, const PipeMessage &message) {
     int ret = write(pipes[1], message.array, sizeof(PipeMessage));
     return ret == sizeof(PipeMessage);
@@ -211,7 +198,7 @@
     int64_t postUs;
     std::vector<uint8_t> vecParams;
 
-    getAllocationParams(&vecParams);
+    getVtsAllocatorParams(&vecParams);
     status = mManager->allocate(mConnectionId, vecParams, &sbuffer);
     ASSERT_TRUE(status == ResultStatus::OK);
 
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/single.cpp b/media/libstagefright/codec2/vndk/bufferpool/vts/single.cpp
index 89eb631..c8878f3 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/vts/single.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/single.cpp
@@ -32,6 +32,7 @@
 #include <iostream>
 #include <memory>
 #include <vector>
+#include "allocator.h"
 
 using android::C2AllocatorIon;
 using android::C2PlatformAllocatorStore;
@@ -45,9 +46,6 @@
 
 namespace {
 
-// Buffer allocation size for tests.
-constexpr static int kAllocationSize = 1024 * 10;
-
 // Number of iteration for buffer allocation test.
 constexpr static int kNumAllocationTest = 3;
 
@@ -63,11 +61,14 @@
     mManager = ClientManager::getInstance();
     ASSERT_NE(mManager, nullptr);
 
-    mAllocator =
+    std::shared_ptr<C2Allocator> allocator =
         std::make_shared<C2AllocatorIon>(C2PlatformAllocatorStore::ION);
+    ASSERT_TRUE((bool)allocator);
+
+    mAllocator = std::make_shared<VtsBufferPoolAllocator>(allocator);
     ASSERT_TRUE((bool)mAllocator);
 
-    status = mManager->create(mAllocator, true, &mConnectionId);
+    status = mManager->create(mAllocator, &mConnectionId);
     ASSERT_TRUE(status == ResultStatus::OK);
 
     status = mManager->getAccessor(mConnectionId, &mAccessor);
@@ -91,23 +92,10 @@
 
   android::sp<ClientManager> mManager;
   android::sp<IAccessor> mAccessor;
-  std::shared_ptr<C2Allocator> mAllocator;
+  std::shared_ptr<BufferPoolAllocator> mAllocator;
   ConnectionId mConnectionId;
   ConnectionId mReceiverId;
 
-  void getAllocationParams(std::vector<uint8_t>* vecParams) {
-    union Params {
-      struct {
-        uint32_t capacity;
-        C2MemoryUsage usage;
-      } data;
-      uint8_t array[0];
-      Params()
-          : data{kAllocationSize,
-                 {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}} {}
-    } params;
-    vecParams->assign(params.array, params.array + sizeof(params));
-  }
 };
 
 // Buffer allocation test.
@@ -116,7 +104,7 @@
 TEST_F(BufferpoolSingleTest, AllocateBuffer) {
   ResultStatus status;
   std::vector<uint8_t> vecParams;
-  getAllocationParams(&vecParams);
+  getVtsAllocatorParams(&vecParams);
 
   std::shared_ptr<_C2BlockPoolData> buffer[kNumAllocationTest];
   for (int i = 0; i < kNumAllocationTest; ++i) {
@@ -136,7 +124,7 @@
 TEST_F(BufferpoolSingleTest, RecycleBuffer) {
   ResultStatus status;
   std::vector<uint8_t> vecParams;
-  getAllocationParams(&vecParams);
+  getVtsAllocatorParams(&vecParams);
 
   BufferId bid[kNumRecycleTest];
   for (int i = 0; i < kNumRecycleTest; ++i) {
@@ -156,7 +144,7 @@
 TEST_F(BufferpoolSingleTest, TransferBuffer) {
   ResultStatus status;
   std::vector<uint8_t> vecParams;
-  getAllocationParams(&vecParams);
+  getVtsAllocatorParams(&vecParams);
   std::shared_ptr<_C2BlockPoolData> sbuffer, rbuffer;
 
   TransactionId transactionId;
diff --git a/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h b/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
index 9dc7152..8d28192 100644
--- a/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
+++ b/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
@@ -65,6 +65,8 @@
 
     virtual ~C2AllocatorGralloc() override;
 
+    static bool isValid(const C2Handle* const o);
+
 private:
     class Impl;
     Impl *mImpl;
diff --git a/media/libstagefright/codec2/vndk/include/C2AllocatorIon.h b/media/libstagefright/codec2/vndk/include/C2AllocatorIon.h
index 716eae0..8c0992e 100644
--- a/media/libstagefright/codec2/vndk/include/C2AllocatorIon.h
+++ b/media/libstagefright/codec2/vndk/include/C2AllocatorIon.h
@@ -49,6 +49,8 @@
 
     virtual ~C2AllocatorIon() override;
 
+    static bool isValid(const C2Handle* const o);
+
 private:
     std::shared_ptr<const Traits> mTraits;
     c2_status_t mInit;
diff --git a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
index 25003cb..ff67788 100644
--- a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
@@ -39,7 +39,7 @@
      *         create this block.
      */
     static
-    std::shared_ptr<C2LinearBlock> C2_HIDE CreateLinearBlock(
+    std::shared_ptr<C2LinearBlock> CreateLinearBlock(
             const std::shared_ptr<C2LinearAllocation> &alloc,
             const std::shared_ptr<_C2BlockPoolData> &data = nullptr,
             size_t offset = 0,
diff --git a/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
index 34797a9..ba7c5d5 100644
--- a/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
@@ -20,6 +20,14 @@
 #include <C2Param.h>
 
 struct C2_HIDE _C2ParamInspector {
+    inline static uint32_t GetOffset(const C2FieldDescriptor &fd) {
+        return fd._mFieldId._mOffset;
+    }
+
+    inline static uint32_t GetSize(const C2FieldDescriptor &fd) {
+        return fd._mFieldId._mSize;
+    }
+
     inline static uint32_t GetIndex(const C2ParamField &pf) {
         return pf._mIndex;
     }
diff --git a/media/libstagefright/httplive/Android.bp b/media/libstagefright/httplive/Android.bp
index b6b0829..8a77401 100644
--- a/media/libstagefright/httplive/Android.bp
+++ b/media/libstagefright/httplive/Android.bp
@@ -37,7 +37,6 @@
         "libcutils",
         "libmedia",
         "libmediaextractor",
-        "libmediandk",
         "libstagefright",
         "libstagefright_foundation",
         "libutils",
diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index 52a7afc..fbf1496 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -11,7 +11,6 @@
 
     include_dirs: [
         "frameworks/av/media/libstagefright",
-        "frameworks/av/media/ndk/include",
         "frameworks/native/include/media/openmax",
     ],
 
@@ -34,7 +33,6 @@
     shared_libs: [
         "libcrypto",
         "libmedia",
-        "libmediandk",
         "libhidlallocatorutils",
         "android.hardware.cas.native@1.0",
         "android.hidl.memory@1.0",
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 972babd..8488d10 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -19,7 +19,6 @@
 
 #include "AnotherPacketSource.h"
 
-#include <media/NdkMediaFormat.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -219,7 +218,7 @@
         }
 
         sp<ABuffer> mpegUserData;
-        if (buffer->meta()->findBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, &mpegUserData) && mpegUserData != NULL) {
+        if (buffer->meta()->findBuffer("mpegUserData", &mpegUserData) && mpegUserData != NULL) {
             bufmeta.setData(
                     kKeyMpegUserData, 0, mpegUserData->data(), mpegUserData->size());
         }
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 1147555..9e18fd3 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -20,7 +20,6 @@
 
 #include "ESQueue.h"
 
-#include <media/NdkMediaFormat.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -1476,7 +1475,7 @@
                                     mpegUserData->data() + i * sizeof(size_t),
                                     &userDataPositions[i], sizeof(size_t));
                         }
-                        accessUnit->meta()->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
+                        accessUnit->meta()->setBuffer("mpegUserData", mpegUserData);
                     }
                 }
 
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index ac837a3..ea43d2e 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -43,12 +43,7 @@
 static media_status_t translate_error(status_t err) {
     if (err == OK) {
         return AMEDIA_OK;
-    } else if (err == ERROR_END_OF_STREAM) {
-        return AMEDIA_ERROR_END_OF_STREAM;
-    } else if (err == ERROR_IO) {
-        return AMEDIA_ERROR_IO;
     }
-
     ALOGE("sf error code: %d", err);
     return AMEDIA_ERROR_UNKNOWN;
 }
@@ -416,64 +411,5 @@
     return -1;
 }
 
-EXPORT
-media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt) {
-    if (fmt == NULL) {
-        return AMEDIA_ERROR_INVALID_PARAMETER;
-    }
-
-    sp<MetaData> sampleMeta;
-    status_t err = ex->mImpl->getSampleMeta(&sampleMeta);
-    if (err != OK) {
-        return translate_error(err);
-    }
-
-    sp<AMessage> meta;
-    AMediaFormat_getFormat(fmt, &meta);
-    meta->clear();
-
-    int32_t layerId;
-    if (sampleMeta->findInt32(kKeyTemporalLayerId, &layerId)) {
-        meta->setInt32(AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId);
-    }
-
-    size_t trackIndex;
-    err = ex->mImpl->getSampleTrackIndex(&trackIndex);
-    if (err == OK) {
-        meta->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, trackIndex);
-        sp<AMessage> trackFormat;
-        AString mime;
-        err = ex->mImpl->getTrackFormat(trackIndex, &trackFormat);
-        if (err == OK
-                && trackFormat != NULL
-                && trackFormat->findString(AMEDIAFORMAT_KEY_MIME, &mime)) {
-            meta->setString(AMEDIAFORMAT_KEY_MIME, mime);
-        }
-    }
-
-    int64_t durationUs;
-    if (sampleMeta->findInt64(kKeyDuration, &durationUs)) {
-        meta->setInt64(AMEDIAFORMAT_KEY_DURATION, durationUs);
-    }
-
-    uint32_t dataType; // unused
-    const void *seiData;
-    size_t seiLength;
-    if (sampleMeta->findData(kKeySEI, &dataType, &seiData, &seiLength)) {
-        sp<ABuffer> sei = ABuffer::CreateAsCopy(seiData, seiLength);;
-        meta->setBuffer(AMEDIAFORMAT_KEY_SEI, sei);
-    }
-
-    const void *mpegUserDataPointer;
-    size_t mpegUserDataLength;
-    if (sampleMeta->findData(
-            kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
-        sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
-        meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
-    }
-
-    return AMEDIA_OK;
-}
-
 } // extern "C"
 
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index abcaa0a..b86876b 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -309,7 +309,6 @@
 EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
 EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
 EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime";
-EXPORT const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA = "mpeg-user-data";
 EXPORT const char* AMEDIAFORMAT_KEY_OPERATING_RATE = "operating-rate";
 EXPORT const char* AMEDIAFORMAT_KEY_PCM_ENCODING = "pcm-encoding";
 EXPORT const char* AMEDIAFORMAT_KEY_PRIORITY = "priority";
@@ -318,13 +317,10 @@
 EXPORT const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
 EXPORT const char* AMEDIAFORMAT_KEY_ROTATION = "rotation-degrees";
 EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate";
-EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei";
 EXPORT const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT = "slice-height";
 EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
-EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = "temporal-layer-id";
 EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING = "ts-schema";
 EXPORT const char* AMEDIAFORMAT_KEY_TRACK_ID = "track-id";
-EXPORT const char* AMEDIAFORMAT_KEY_TRACK_INDEX = "track-index";
 EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width";
 
 
diff --git a/media/ndk/include/media/NdkMediaError.h b/media/ndk/include/media/NdkMediaError.h
index 13aacc9..e48fcbe 100644
--- a/media/ndk/include/media/NdkMediaError.h
+++ b/media/ndk/include/media/NdkMediaError.h
@@ -53,8 +53,6 @@
     AMEDIA_ERROR_INVALID_OBJECT        = AMEDIA_ERROR_BASE - 3,
     AMEDIA_ERROR_INVALID_PARAMETER     = AMEDIA_ERROR_BASE - 4,
     AMEDIA_ERROR_INVALID_OPERATION     = AMEDIA_ERROR_BASE - 5,
-    AMEDIA_ERROR_END_OF_STREAM         = AMEDIA_ERROR_BASE - 6,
-    AMEDIA_ERROR_IO                    = AMEDIA_ERROR_BASE - 7,
 
     AMEDIA_DRM_ERROR_BASE              = -20000,
     AMEDIA_DRM_NOT_PROVISIONED         = AMEDIA_DRM_ERROR_BASE - 1,
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index f7b9cfd..3c9e23d 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -203,17 +203,6 @@
  */
 int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *);
 
-/**
- * Read the current sample's metadata format into |fmt|. Examples of sample metadata are
- * SEI (supplemental enhancement information) and MPEG user data, both of which can embed
- * closed-caption data.
- *
- * Returns AMEDIA_OK on success or AMEDIA_ERROR_* to indicate failure reason.
- * Existing key-value pairs in |fmt| would be removed if this API returns AMEDIA_OK.
- * The contents of |fmt| is undefined if this API returns AMEDIA_ERROR_*.
- */
-media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt);
-
 #endif /* __ANDROID_API__ >= 28 */
 
 #endif /* __ANDROID_API__ >= 21 */
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 464c127..b6489c7 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -123,7 +123,6 @@
 extern const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE;
 extern const char* AMEDIAFORMAT_KEY_MAX_WIDTH;
 extern const char* AMEDIAFORMAT_KEY_MIME;
-extern const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA;
 extern const char* AMEDIAFORMAT_KEY_OPERATING_RATE;
 extern const char* AMEDIAFORMAT_KEY_PCM_ENCODING;
 extern const char* AMEDIAFORMAT_KEY_PRIORITY;
@@ -132,13 +131,10 @@
 extern const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER;
 extern const char* AMEDIAFORMAT_KEY_ROTATION;
 extern const char* AMEDIAFORMAT_KEY_SAMPLE_RATE;
-extern const char* AMEDIAFORMAT_KEY_SEI;
 extern const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT;
 extern const char* AMEDIAFORMAT_KEY_STRIDE;
-extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID;
 extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING;
 extern const char* AMEDIAFORMAT_KEY_TRACK_ID;
-extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX;
 extern const char* AMEDIAFORMAT_KEY_WIDTH;
 
 #endif /* __ANDROID_API__ >= 21 */
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index d8c41d2..17c1a0d 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -159,7 +159,6 @@
     AMediaExtractor_getPsshInfo;
     AMediaExtractor_getSampleCryptoInfo;
     AMediaExtractor_getSampleFlags;
-    AMediaExtractor_getSampleFormat;   # introduced=28
     AMediaExtractor_getSampleSize;     # introduced=28
     AMediaExtractor_getSampleTime;
     AMediaExtractor_getSampleTrackIndex;
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 4e29d37..f6cad09 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -16,6 +16,8 @@
 
 package com.android.media;
 
+import static android.media.MediaSession2.*;
+
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -39,8 +41,10 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.UserHandle;
 import android.support.annotation.GuardedBy;
 import android.util.Log;
 
@@ -77,7 +81,7 @@
     @GuardedBy("mLock")
     private PendingIntent mSessionActivity;
     @GuardedBy("mLock")
-    private CommandGroup mCommandGroup;
+    private CommandGroup mAllowedCommands;
 
     // Assignment should be used with the lock hold, but should be used without a lock to prevent
     // potential deadlock.
@@ -125,6 +129,24 @@
             connectToSession(SessionToken2Impl.from(mToken).getSessionBinder());
         } else {
             // Session service
+            if (Process.myUid() == Process.SYSTEM_UID) {
+                // It's system server (MediaSessionService) that wants to monitor session.
+                // Don't bind if able..
+                IMediaSession2 binder = SessionToken2Impl.from(mToken).getSessionBinder();
+                if (binder != null) {
+                    // Use binder in the session token instead of bind by its own.
+                    // Otherwise server will holds the binding to the service *forever* and service
+                    // will never stop.
+                    mServiceConnection = null;
+                    connectToSession(SessionToken2Impl.from(mToken).getSessionBinder());
+                    return;
+                } else if (DEBUG) {
+                    // Should happen only when system server wants to dispatch media key events to
+                    // a dead service.
+                    Log.d(TAG, "System server binds to a session service. Should unbind"
+                            + " immediately after the use.");
+                }
+            }
             mServiceConnection = new SessionServiceConnection();
             connectToService();
         }
@@ -150,8 +172,15 @@
         //    If a service wants to keep running, it should be either foreground service or
         //    bounded service. But there had been request for the feature for system apps
         //    and using bindService() will be better fit with it.
-        // TODO(jaewan): Use bindServiceAsUser()??
-        boolean result = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        boolean result;
+        if (Process.myUid() == Process.SYSTEM_UID) {
+            // Use bindServiceAsUser() for binding from system service to avoid following warning.
+            // ContextImpl: Calling a method in the system process without a qualified user
+            result = mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE,
+                    UserHandle.getUserHandleForUid(mToken.getUid()));
+        } else {
+            result = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        }
         if (!result) {
             Log.w(TAG, "bind to " + mToken + " failed");
         } else if (DEBUG) {
@@ -221,6 +250,42 @@
         return mInstance;
     }
 
+    // Returns session binder if the controller can send the command.
+    IMediaSession2 getSessionBinderIfAble(int commandCode) {
+        synchronized (mLock) {
+            if (!mAllowedCommands.hasCommand(commandCode)) {
+                // Cannot send because isn't allowed to.
+                Log.w(TAG, "Controller isn't allowed to call command, commandCode="
+                        + commandCode);
+                return null;
+            }
+        }
+        // TODO(jaewan): Should we do this with the lock hold?
+        final IMediaSession2 binder = mSessionBinder;
+        if (binder == null) {
+            // Cannot send because disconnected.
+            Log.w(TAG, "Session is disconnected");
+        }
+        return binder;
+    }
+
+    // Returns session binder if the controller can send the command.
+    IMediaSession2 getSessionBinderIfAble(Command command) {
+        synchronized (mLock) {
+            if (!mAllowedCommands.hasCommand(command)) {
+                Log.w(TAG, "Controller isn't allowed to call command, command=" + command);
+                return null;
+            }
+        }
+        // TODO(jaewan): Should we do this with the lock hold?
+        final IMediaSession2 binder = mSessionBinder;
+        if (binder == null) {
+            // Cannot send because disconnected.
+            Log.w(TAG, "Session is disconnected");
+        }
+        return binder;
+    }
+
     @Override
     public SessionToken2 getSessionToken_impl() {
         return mToken;
@@ -285,7 +350,7 @@
     @Override
     public void setVolumeTo_impl(int value, int flags) {
         // TODO(hdmoon): sanity check
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
         if (binder != null) {
             try {
                 binder.setVolumeTo(mSessionCallbackStub, value, flags);
@@ -300,7 +365,7 @@
     @Override
     public void adjustVolume_impl(int direction, int flags) {
         // TODO(hdmoon): sanity check
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
         if (binder != null) {
             try {
                 binder.adjustVolume(mSessionCallbackStub, direction, flags);
@@ -314,7 +379,7 @@
 
     @Override
     public void prepareFromUri_impl(Uri uri, Bundle extras) {
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PREPARE_FROM_URI);
         if (binder != null) {
             try {
                 binder.prepareFromUri(mSessionCallbackStub, uri, extras);
@@ -328,7 +393,7 @@
 
     @Override
     public void prepareFromSearch_impl(String query, Bundle extras) {
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PREPARE_FROM_SEARCH);
         if (binder != null) {
             try {
                 binder.prepareFromSearch(mSessionCallbackStub, query, extras);
@@ -342,7 +407,7 @@
 
     @Override
     public void prepareMediaId_impl(String mediaId, Bundle extras) {
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PREPARE_FROM_MEDIA_ID);
         if (binder != null) {
             try {
                 binder.prepareFromMediaId(mSessionCallbackStub, mediaId, extras);
@@ -356,7 +421,7 @@
 
     @Override
     public void playFromUri_impl(Uri uri, Bundle extras) {
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAY_FROM_URI);
         if (binder != null) {
             try {
                 binder.playFromUri(mSessionCallbackStub, uri, extras);
@@ -370,7 +435,7 @@
 
     @Override
     public void playFromSearch_impl(String query, Bundle extras) {
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAY_FROM_SEARCH);
         if (binder != null) {
             try {
                 binder.playFromSearch(mSessionCallbackStub, query, extras);
@@ -384,7 +449,7 @@
 
     @Override
     public void playFromMediaId_impl(String mediaId, Bundle extras) {
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAY_FROM_MEDIA_ID);
         if (binder != null) {
             try {
                 binder.playFromMediaId(mSessionCallbackStub, mediaId, extras);
@@ -422,8 +487,7 @@
         if (command == null) {
             throw new IllegalArgumentException("command shouldn't be null");
         }
-        // TODO(jaewan): Also check if the command is allowed.
-        final IMediaSession2 binder = mSessionBinder;
+        final IMediaSession2 binder = getSessionBinderIfAble(command);
         if (binder != null) {
             try {
                 binder.sendCustomCommand(mSessionCallbackStub, command.toBundle(), args, cb);
@@ -609,16 +673,16 @@
 
     // Should be used without a lock to prevent potential deadlock.
     void onConnectedNotLocked(IMediaSession2 sessionBinder,
-            final CommandGroup commandGroup, final PlaybackState2 state, final PlaybackInfo info,
+            final CommandGroup allowedCommands, final PlaybackState2 state, final PlaybackInfo info,
             final PlaylistParams params, final List<MediaItem2> playlist,
             final PendingIntent sessionActivity) {
         if (DEBUG) {
             Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
-                    + ", commands=" + commandGroup);
+                    + ", allowedCommands=" + allowedCommands);
         }
         boolean close = false;
         try {
-            if (sessionBinder == null || commandGroup == null) {
+            if (sessionBinder == null || allowedCommands == null) {
                 // Connection rejected.
                 close = true;
                 return;
@@ -633,7 +697,7 @@
                     close = true;
                     return;
                 }
-                mCommandGroup = commandGroup;
+                mAllowedCommands = allowedCommands;
                 mPlaybackState = state;
                 mPlaybackInfo = info;
                 mPlaylistParams = params;
@@ -657,7 +721,7 @@
                 // Note: We may trigger ControllerCallbacks with the initial values
                 // But it's hard to define the order of the controller callbacks
                 // Only notify about the
-                mCallback.onConnected(commandGroup);
+                mCallback.onConnected(allowedCommands);
             });
         } finally {
             if (close) {
diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
index 4c4ef24..252512c 100644
--- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.media.DataSourceDesc;
 import android.media.MediaItem2;
+import android.media.MediaItem2.Builder;
 import android.media.MediaItem2.Flags;
 import android.media.MediaMetadata2;
 import android.media.update.MediaItem2Provider;
@@ -43,7 +44,7 @@
     private DataSourceDesc mDataSourceDesc;
 
     // From the public API
-    public MediaItem2Impl(Context context, MediaItem2 instance, String mediaId,
+    public MediaItem2Impl(Context context, String mediaId,
             DataSourceDesc dsd, MediaMetadata2 metadata, @Flags int flags) {
         if (mediaId == null) {
             throw new IllegalArgumentException("mediaId shouldn't be null");
@@ -56,12 +57,12 @@
         }
 
         mContext = context;
-        mInstance = instance;
-
         mId = mediaId;
         mDataSourceDesc = dsd;
         mMetadata = metadata;
         mFlags = flags;
+
+        mInstance = new MediaItem2(this);
     }
 
     // Create anonymized version
@@ -157,4 +158,52 @@
     public @Nullable DataSourceDesc getDataSourceDesc_impl() {
         return mDataSourceDesc;
     }
+
+    public static class BuilderImpl implements MediaItem2Provider.BuilderProvider {
+        private Context mContext;
+        private Builder mInstance;
+        private @Flags int mFlags;
+        private String mMediaId;
+        private MediaMetadata2 mMetadata;
+        private DataSourceDesc mDataSourceDesc;
+
+        public BuilderImpl(Context context, Builder instance, int flags) {
+            mContext = context;
+            mInstance = instance;
+            mFlags = flags;
+        }
+
+        @Override
+        public Builder setMediaId_impl(@Nullable String mediaId) {
+            mMediaId = mediaId;
+            return mInstance;
+        }
+
+        @Override
+        public Builder setMetadata_impl(@Nullable MediaMetadata2 metadata) {
+            mMetadata = metadata;
+            return mInstance;
+        }
+
+        @Override
+        public Builder setDataSourceDesc_impl(@NonNull DataSourceDesc dataSourceDesc) {
+            if (dataSourceDesc == null) {
+                throw new IllegalArgumentException("dataSourceDesc shouldn't be null");
+            }
+            mDataSourceDesc = dataSourceDesc;
+            return mInstance;
+        }
+
+        @Override
+        public MediaItem2 build_impl() {
+            String id = (mMetadata != null)
+                    ? mMetadata.getString(MediaMetadata2.METADATA_KEY_MEDIA_ID) : null;
+            if (id == null) {
+                //  TODO(jaewan): Double check if its sufficient (e.g. Use UUID instead?)
+                id = (mMediaId != null) ? mMediaId : toString();
+            }
+            return new MediaItem2Impl(mContext, id, mDataSourceDesc, mMetadata, mFlags)
+                    .getInstance();
+        }
+    }
 }
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
index 2178ccd..7ebf716 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -125,7 +125,7 @@
     @Override
     public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
             Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
-            playlist, PendingIntent sessionActivity) {
+            itemBundleList, PendingIntent sessionActivity) {
         final MediaController2Impl controller = mController.get();
         if (controller == null) {
             if (DEBUG) {
@@ -134,11 +134,14 @@
             return;
         }
         final Context context = controller.getContext();
-        List<MediaItem2> list = new ArrayList<>();
-        for (int i = 0; i < playlist.size(); i++) {
-            MediaItem2 item = MediaItem2.fromBundle(context, playlist.get(i));
-            if (item != null) {
-                list.add(item);
+        List<MediaItem2> itemList = null;
+        if (itemBundleList != null) {
+            itemList = new ArrayList<>();
+            for (int i = 0; i < itemBundleList.size(); i++) {
+                MediaItem2 item = MediaItem2.fromBundle(context, itemBundleList.get(i));
+                if (item != null) {
+                    itemList.add(item);
+                }
             }
         }
         controller.onConnectedNotLocked(sessionBinder,
@@ -146,7 +149,7 @@
                 PlaybackState2.fromBundle(context, playbackState),
                 PlaybackInfoImpl.fromBundle(context, playbackInfo),
                 PlaylistParams.fromBundle(context, playlistParams),
-                list, sessionActivity);
+                itemList, sessionActivity);
     }
 
     @Override
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 5e7af3b..073ac15 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -149,10 +149,10 @@
                     + " session services define the same id=" + id);
         } else if (libraryService != null) {
             mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_LIBRARY_SERVICE,
-                    mContext.getPackageName(), libraryService, id, null).getInstance();
+                    mContext.getPackageName(), libraryService, id, mSessionStub).getInstance();
         } else if (sessionService != null) {
             mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_SESSION_SERVICE,
-                    mContext.getPackageName(), sessionService, id, null).getInstance();
+                    mContext.getPackageName(), sessionService, id, mSessionStub).getInstance();
         } else {
             mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_SESSION,
                     mContext.getPackageName(), null, id, mSessionStub).getInstance();
@@ -807,7 +807,7 @@
         }
 
         /**
-         * @ 7return a new Bundle instance from the Command
+         * @return a new Bundle instance from the Command
          */
         public Bundle toBundle_impl() {
             Bundle bundle = new Bundle();
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index b1e71e2..0d0fc68 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -47,9 +47,9 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.HashSet;
 
 public class MediaSession2Stub extends IMediaSession2.Stub {
 
@@ -67,6 +67,10 @@
     @GuardedBy("mLock")
     private final ArrayMap<IBinder, ControllerInfo> mControllers = new ArrayMap<>();
     @GuardedBy("mLock")
+    private final Set<IBinder> mConnectingControllers = new HashSet<>();
+    @GuardedBy("mLock")
+    private final ArrayMap<ControllerInfo, CommandGroup> mAllowedCommandGroupMap = new ArrayMap<>();
+    @GuardedBy("mLock")
     private final ArrayMap<ControllerInfo, Set<String>> mSubscriptions = new ArrayMap<>();
 
     public MediaSession2Stub(MediaSession2Impl session) {
@@ -92,10 +96,10 @@
         }
     }
 
-    private MediaSession2Impl getSession() throws IllegalStateException {
+    private MediaSession2Impl getSession() {
         final MediaSession2Impl session = mSession.get();
-        if (session == null) {
-            throw new IllegalStateException("Session is died");
+        if (session == null && DEBUG) {
+            Log.d(TAG, "Session is closed", new IllegalStateException());
         }
         return session;
     }
@@ -108,49 +112,145 @@
         return (MediaLibrarySessionImpl) session;
     }
 
-    private ControllerInfo getController(IMediaSession2Callback caller) {
-        // TODO(jaewan): Find a way to return connection-in-progress-controller
-        //               to be included here, because session owner may want to send some datas
-        //               while onConnected() hasn't returned.
+    // Get controller if the command from caller to session is able to be handled.
+    private ControllerInfo getControllerIfAble(IMediaSession2Callback caller) {
         synchronized (mLock) {
-            return mControllers.get(caller.asBinder());
+            final ControllerInfo controllerInfo = mControllers.get(caller.asBinder());
+            if (controllerInfo == null && DEBUG) {
+                Log.d(TAG, "Controller is disconnected", new IllegalStateException());
+            }
+            return controllerInfo;
+        }
+    }
+
+    // Get controller if the command from caller to session is able to be handled.
+    private ControllerInfo getControllerIfAble(IMediaSession2Callback caller, int commandCode) {
+        synchronized (mLock) {
+            final ControllerInfo controllerInfo = getControllerIfAble(caller);
+            if (controllerInfo == null) {
+                return null;
+            }
+            CommandGroup allowedCommands = mAllowedCommandGroupMap.get(controllerInfo);
+            if (allowedCommands == null) {
+                Log.w(TAG, "Controller with null allowed commands. Ignoring",
+                        new IllegalStateException());
+                return null;
+            }
+            if (!allowedCommands.hasCommand(commandCode)) {
+                if (DEBUG) {
+                    Log.d(TAG, "Controller isn't allowed for command " + commandCode);
+                }
+                return null;
+            }
+            return controllerInfo;
+        }
+    }
+
+    // Get controller if the command from caller to session is able to be handled.
+    private ControllerInfo getControllerIfAble(IMediaSession2Callback caller, Command command) {
+        synchronized (mLock) {
+            final ControllerInfo controllerInfo = getControllerIfAble(caller);
+            if (controllerInfo == null) {
+                return null;
+            }
+            CommandGroup allowedCommands = mAllowedCommandGroupMap.get(controllerInfo);
+            if (allowedCommands == null) {
+                Log.w(TAG, "Controller with null allowed commands. Ignoring",
+                        new IllegalStateException());
+                return null;
+            }
+            if (!allowedCommands.hasCommand(command)) {
+                if (DEBUG) {
+                    Log.d(TAG, "Controller isn't allowed for command " + command);
+                }
+                return null;
+            }
+            return controllerInfo;
+        }
+    }
+
+    // Return binder if the session is able to send a command to the controller.
+    private IMediaSession2Callback getControllerBinderIfAble(ControllerInfo controller) {
+        if (getSession() == null) {
+            // getSession() already logged if session is closed.
+            return null;
+        }
+        final ControllerInfoImpl impl = ControllerInfoImpl.from(controller);
+        synchronized (mLock) {
+            if (mControllers.get(impl.getId()) != null
+                    || mConnectingControllers.contains(impl.getId())) {
+                return impl.getControllerBinder();
+            }
+            if (DEBUG) {
+                Log.d(TAG, controller + " isn't connected nor connecting",
+                        new IllegalArgumentException());
+            }
+            return null;
+        }
+    }
+
+    // Return binder if the session is able to send a command to the controller.
+    private IMediaSession2Callback getControllerBinderIfAble(ControllerInfo controller,
+            int commandCode) {
+        synchronized (mLock) {
+            CommandGroup allowedCommands = mAllowedCommandGroupMap.get(controller);
+            if (allowedCommands == null) {
+                Log.w(TAG, "Controller with null allowed commands. Ignoring");
+                return null;
+            }
+            if (!allowedCommands.hasCommand(commandCode)) {
+                if (DEBUG) {
+                    Log.d(TAG, "Controller isn't allowed for command " + commandCode);
+                }
+                return null;
+            }
+            return getControllerBinderIfAble(controller);
         }
     }
 
     //////////////////////////////////////////////////////////////////////////////////////////////
     // AIDL methods for session overrides
     //////////////////////////////////////////////////////////////////////////////////////////////
-
     @Override
-    public void connect(final IMediaSession2Callback caller, String callingPackage)
+    public void connect(final IMediaSession2Callback caller, final String callingPackage)
             throws RuntimeException {
-        final MediaSession2Impl sessionImpl = getSession();
-        final Context context = sessionImpl.getContext();
+        final MediaSession2Impl session = getSession();
+        if (session == null) {
+            return;
+        }
+        final Context context = session.getContext();
         final ControllerInfo controllerInfo = new ControllerInfo(context,
                 Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, caller);
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getSession() == null) {
                 return;
             }
+            synchronized (mLock) {
+                // Keep connecting controllers.
+                // This helps sessions to call APIs in the onConnect() (e.g. setCustomLayout())
+                // instead of pending them.
+                mConnectingControllers.add(ControllerInfoImpl.from(controllerInfo).getId());
+            }
             CommandGroup allowedCommands = session.getCallback().onConnect(controllerInfo);
-            // Don't reject connection for the controllerInfo from trusted app.
+            // Don't reject connection for the request from trusted app.
             // Otherwise server will fail to retrieve session's information to dispatch
             // media keys to.
             boolean accept = allowedCommands != null || controllerInfo.isTrusted();
-            ControllerInfoImpl impl = ControllerInfoImpl.from(controllerInfo);
             if (accept) {
+                ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controllerInfo);
                 if (DEBUG) {
                     Log.d(TAG, "Accepting connection, controllerInfo=" + controllerInfo
                             + " allowedCommands=" + allowedCommands);
                 }
-                synchronized (mLock) {
-                    mControllers.put(impl.getId(), controllerInfo);
-                }
                 if (allowedCommands == null) {
                     // For trusted apps, send non-null allowed commands to keep connection.
                     allowedCommands = new CommandGroup(context);
                 }
+                synchronized (mLock) {
+                    mConnectingControllers.remove(controllerImpl.getId());
+                    mControllers.put(controllerImpl.getId(),  controllerInfo);
+                    mAllowedCommandGroupMap.put(controllerInfo, allowedCommands);
+                }
                 // If connection is accepted, notify the current state to the controller.
                 // It's needed because we cannot call synchronous calls between session/controller.
                 // Note: We're doing this after the onConnectionChanged(), but there's no guarantee
@@ -165,9 +265,12 @@
                 final PlaylistParams params = session.getInstance().getPlaylistParams();
                 final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
                 final PendingIntent sessionActivity = session.getSessionActivity();
-                final List<MediaItem2> playlist = session.getInstance().getPlaylist();
-                final List<Bundle> playlistBundle = new ArrayList<>();
+                final List<MediaItem2> playlist =
+                        allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET)
+                                ? session.getInstance().getPlaylist() : null;
+                final List<Bundle> playlistBundle;
                 if (playlist != null) {
+                    playlistBundle = new ArrayList<>();
                     // TODO(jaewan): Find a way to avoid concurrent modification exception.
                     for (int i = 0; i < playlist.size(); i++) {
                         final MediaItem2 item = playlist.get(i);
@@ -178,11 +281,13 @@
                             }
                         }
                     }
+                } else {
+                    playlistBundle = null;
                 }
 
                 // Double check if session is still there, because close() can be called in another
                 // thread.
-                if (mSession.get() == null) {
+                if (getSession() == null) {
                     return;
                 }
                 try {
@@ -194,6 +299,9 @@
                     // TODO(jaewan): Handle here.
                 }
             } else {
+                synchronized (mLock) {
+                    mConnectingControllers.remove(ControllerInfoImpl.from(controllerInfo).getId());
+                }
                 if (DEBUG) {
                     Log.d(TAG, "Rejecting connection, controllerInfo=" + controllerInfo);
                 }
@@ -208,7 +316,7 @@
     }
 
     @Override
-    public void release(IMediaSession2Callback caller) throws RemoteException {
+    public void release(final IMediaSession2Callback caller) throws RemoteException {
         synchronized (mLock) {
             ControllerInfo controllerInfo = mControllers.remove(caller.asBinder());
             if (DEBUG) {
@@ -219,19 +327,16 @@
     }
 
     @Override
-    public void setVolumeTo(IMediaSession2Callback caller, int value, int flags)
+    public void setVolumeTo(final IMediaSession2Callback caller, final int value, final int flags)
             throws RuntimeException {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_SET_VOLUME);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_SET_VOLUME) == null) {
                 return;
             }
             // TODO(jaewan): Sanity check.
@@ -259,17 +364,14 @@
     @Override
     public void adjustVolume(IMediaSession2Callback caller, int direction, int flags)
             throws RuntimeException {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_SET_VOLUME);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_SET_VOLUME) == null) {
                 return;
             }
             // TODO(jaewan): Sanity check.
@@ -303,17 +405,13 @@
     @Override
     public void sendTransportControlCommand(IMediaSession2Callback caller,
             int commandCode, Bundle args) throws RuntimeException {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(caller, commandCode);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, commandCode) == null) {
                 return;
             }
             // TODO(jaewan): Sanity check.
@@ -377,20 +475,19 @@
     @Override
     public void sendCustomCommand(final IMediaSession2Callback caller, final Bundle commandBundle,
             final Bundle args, final ResultReceiver receiver) {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        if (session == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        final Command command = Command.fromBundle(session.getContext(), commandBundle);
+        final ControllerInfo controller = getControllerIfAble(caller, command);
+        if (controller == null) {
+            return;
+        }
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, command) == null) {
                 return;
             }
-            final Command command = Command.fromBundle(session.getContext(), commandBundle);
             session.getCallback().onCustomCommand(controller, command, args, receiver);
         });
     }
@@ -398,17 +495,15 @@
     @Override
     public void prepareFromUri(final IMediaSession2Callback caller, final Uri uri,
             final Bundle extras) {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_URI);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(
+                    caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_URI) == null) {
                 return;
             }
             session.getCallback().onPrepareFromUri(controller, uri, extras);
@@ -418,17 +513,15 @@
     @Override
     public void prepareFromSearch(final IMediaSession2Callback caller, final String query,
             final Bundle extras) {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(
+                    caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH) == null) {
                 return;
             }
             session.getCallback().onPrepareFromSearch(controller, query, extras);
@@ -438,17 +531,15 @@
     @Override
     public void prepareFromMediaId(final IMediaSession2Callback caller, final String mediaId,
             final Bundle extras) {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(
+                    caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID) == null) {
                 return;
             }
             session.getCallback().onPrepareFromMediaId(controller, mediaId, extras);
@@ -458,17 +549,15 @@
     @Override
     public void playFromUri(final IMediaSession2Callback caller, final Uri uri,
             final Bundle extras) {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_PLAY_FROM_URI);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(
+                    caller, MediaSession2.COMMAND_CODE_PLAY_FROM_URI) == null) {
                 return;
             }
             session.getCallback().onPlayFromUri(controller, uri, extras);
@@ -478,17 +567,15 @@
     @Override
     public void playFromSearch(final IMediaSession2Callback caller, final String query,
             final Bundle extras) {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(
+                    caller, MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH) == null) {
                 return;
             }
             session.getCallback().onPlayFromSearch(controller, query, extras);
@@ -498,16 +585,13 @@
     @Override
     public void playFromMediaId(final IMediaSession2Callback caller, final String mediaId,
             final Bundle extras) {
-        final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
-            }
+        final MediaSession2Impl session = getSession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
+        session.getCallbackExecutor().execute(() -> {
             if (session == null) {
                 return;
             }
@@ -519,7 +603,7 @@
     public void setRating(final IMediaSession2Callback caller, final String mediaId,
             final Bundle ratingBundle) {
         final MediaSession2Impl sessionImpl = getSession();
-        final ControllerInfo controller = getController(caller);
+        final ControllerInfo controller = getControllerIfAble(caller);
         if (controller == null) {
             if (DEBUG) {
                 Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
@@ -541,25 +625,21 @@
     //////////////////////////////////////////////////////////////////////////////////////////////
 
     @Override
-    public void getBrowserRoot(IMediaSession2Callback caller, Bundle rootHints)
+    public void getBrowserRoot(final IMediaSession2Callback caller, final Bundle rootHints)
             throws RuntimeException {
-        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "getBrowerRoot() from a controller that hasn't connected. Ignore");
-            }
+        final MediaLibrarySessionImpl session = getLibrarySession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_BROWSER);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaLibrarySessionImpl session = getLibrarySession();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_BROWSER) == null) {
                 return;
             }
-            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
             LibraryRoot root = session.getCallback().onGetLibraryRoot(controller, rootHints);
             try {
-                controllerImpl.getControllerBinder().onGetLibraryRootDone(rootHints,
+                caller.onGetLibraryRootDone(rootHints,
                         root == null ? null : root.getRootId(),
                         root == null ? null : root.getExtras());
             } catch (RemoteException e) {
@@ -570,31 +650,27 @@
     }
 
     @Override
-    public void getItem(IMediaSession2Callback caller, String mediaId) throws RuntimeException {
-        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "getItem() from a controller that hasn't connected. Ignore");
-            }
-            return;
-        }
+    public void getItem(final IMediaSession2Callback caller, final String mediaId)
+            throws RuntimeException {
         if (mediaId == null) {
             if (DEBUG) {
                 Log.d(TAG, "mediaId shouldn't be null");
             }
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaLibrarySessionImpl session = getLibrarySession();
-            if (session == null) {
+        final MediaLibrarySessionImpl session = getLibrarySession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_BROWSER);
+        if (session == null || controller == null) {
+            return;
+        }
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_BROWSER) == null) {
                 return;
             }
-            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
             MediaItem2 result = session.getCallback().onGetItem(controller, mediaId);
             try {
-                controllerImpl.getControllerBinder().onGetItemDone(
-                        mediaId, result == null ? null : result.toBundle());
+                caller.onGetItemDone(mediaId, result == null ? null : result.toBundle());
             } catch (RemoteException e) {
                 // Controller may be died prematurely.
                 // TODO(jaewan): Handle this.
@@ -603,16 +679,8 @@
     }
 
     @Override
-    public void getChildren(IMediaSession2Callback caller, String parentId, int page,
-            int pageSize, Bundle extras) throws RuntimeException {
-        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "getChildren() from a controller that hasn't connected. Ignore");
-            }
-            return;
-        }
+    public void getChildren(final IMediaSession2Callback caller, final String parentId,
+            final int page, final int pageSize, final Bundle extras) throws RuntimeException {
         if (parentId == null) {
             if (DEBUG) {
                 Log.d(TAG, "parentId shouldn't be null");
@@ -625,13 +693,16 @@
             }
             return;
         }
-
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaLibrarySessionImpl session = getLibrarySession();
-            if (session == null) {
+        final MediaLibrarySessionImpl session = getLibrarySession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_BROWSER);
+        if (session == null || controller == null) {
+            return;
+        }
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_BROWSER) == null) {
                 return;
             }
-            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
             List<MediaItem2> result = session.getCallback().onGetChildren(
                     controller, parentId, page, pageSize, extras);
             if (result != null && result.size() > pageSize) {
@@ -639,7 +710,6 @@
                         + "more than pageSize. result.size()=" + result.size() + " pageSize="
                         + pageSize);
             }
-
             List<Bundle> bundleList = null;
             if (result != null) {
                 bundleList = new ArrayList<>();
@@ -647,10 +717,8 @@
                     bundleList.add(item == null ? null : item.toBundle());
                 }
             }
-
             try {
-                controllerImpl.getControllerBinder().onGetChildrenDone(
-                        parentId, page, pageSize, bundleList, extras);
+                caller.onGetChildrenDone(parentId, page, pageSize, bundleList, extras);
             } catch (RemoteException e) {
                 // Controller may be died prematurely.
                 // TODO(jaewan): Handle this.
@@ -660,42 +728,23 @@
 
     @Override
     public void search(IMediaSession2Callback caller, String query, Bundle extras) {
-        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "search() from a controller that hasn't connected. Ignore");
-            }
+        final MediaLibrarySessionImpl session = getLibrarySession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_BROWSER);
+        if (session == null || controller == null) {
             return;
         }
-        if (TextUtils.isEmpty(query)) {
-            if (DEBUG) {
-                Log.d(TAG, "query shouldn't be empty");
-            }
-            return;
-        }
-
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaLibrarySessionImpl session = getLibrarySession();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_BROWSER) == null) {
                 return;
             }
-            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
             session.getCallback().onSearch(controller, query, extras);
         });
     }
 
     @Override
-    public void getSearchResult(IMediaSession2Callback caller, String query, int page,
-            int pageSize, Bundle extras) {
-        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "getSearchResult() from a controller that hasn't connected. Ignore");
-            }
-            return;
-        }
+    public void getSearchResult(final IMediaSession2Callback caller, final String query,
+            final int page, final int pageSize, final Bundle extras) {
         if (TextUtils.isEmpty(query)) {
             if (DEBUG) {
                 Log.d(TAG, "query shouldn't be empty");
@@ -708,13 +757,16 @@
             }
             return;
         }
-
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaLibrarySessionImpl session = getLibrarySession();
-            if (session == null) {
+        final MediaLibrarySessionImpl session = getLibrarySession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_BROWSER);
+        if (session == null || controller == null) {
+            return;
+        }
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_BROWSER) == null) {
                 return;
             }
-            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
             List<MediaItem2> result = session.getCallback().onGetSearchResult(
                     controller, query, page, pageSize, extras);
             if (result != null && result.size() > pageSize) {
@@ -722,7 +774,6 @@
                         + "items more than pageSize. result.size()=" + result.size() + " pageSize="
                         + pageSize);
             }
-
             List<Bundle> bundleList = null;
             if (result != null) {
                 bundleList = new ArrayList<>();
@@ -732,8 +783,7 @@
             }
 
             try {
-                controllerImpl.getControllerBinder().onGetSearchResultDone(
-                        query, page, pageSize, bundleList, extras);
+                caller.onGetSearchResultDone(query, page, pageSize, bundleList, extras);
             } catch (RemoteException e) {
                 // Controller may be died prematurely.
                 // TODO(jaewan): Handle this.
@@ -744,17 +794,14 @@
     @Override
     public void subscribe(final IMediaSession2Callback caller, final String parentId,
             final Bundle option) {
-        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "subscribe() from a browser that hasn't connected. Ignore");
-            }
+        final MediaLibrarySessionImpl session = getLibrarySession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_BROWSER);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaLibrarySessionImpl session = getLibrarySession();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_BROWSER) == null) {
                 return;
             }
             session.getCallback().onSubscribe(controller, parentId, option);
@@ -771,17 +818,14 @@
 
     @Override
     public void unsubscribe(final IMediaSession2Callback caller, final String parentId) {
-        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
-        final ControllerInfo controller = getController(caller);
-        if (controller == null) {
-            if (DEBUG) {
-                Log.d(TAG, "unsubscribe() from a browser that hasn't connected. Ignore");
-            }
+        final MediaLibrarySessionImpl session = getLibrarySession();
+        final ControllerInfo controller = getControllerIfAble(
+                caller, MediaSession2.COMMAND_CODE_BROWSER);
+        if (session == null || controller == null) {
             return;
         }
-        sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaLibrarySessionImpl session = getLibrarySession();
-            if (session == null) {
+        session.getCallbackExecutor().execute(() -> {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_BROWSER) == null) {
                 return;
             }
             session.getCallback().onUnsubscribe(controller, parentId);
@@ -810,8 +854,10 @@
     public void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
         final List<ControllerInfo> list = getControllers();
         for (int i = 0; i < list.size(); i++) {
-            IMediaSession2Callback controllerBinder =
-                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
+            final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(list.get(i));
+            if (controllerBinder == null) {
+                return;
+            }
             try {
                 final Bundle bundle = state != null ? state.toBundle() : null;
                 controllerBinder.onPlaybackStateChanged(bundle);
@@ -823,10 +869,10 @@
     }
 
     public void notifyCustomLayoutNotLocked(ControllerInfo controller, List<CommandButton> layout) {
-        // TODO(jaewan): It's OK to be called while it's connecting, but not OK if the connection
-        //               is rejected. Handle the case.
-        IMediaSession2Callback controllerBinder =
-                ControllerInfoImpl.from(controller).getControllerBinder();
+        final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(controller);
+        if (controllerBinder == null) {
+            return;
+        }
         try {
             List<Bundle> layoutBundles = new ArrayList<>();
             for (int i = 0; i < layout.size(); i++) {
@@ -843,9 +889,6 @@
     }
 
     public void notifyPlaylistChanged(List<MediaItem2> playlist) {
-        if (playlist == null) {
-            return;
-        }
         final List<Bundle> bundleList = new ArrayList<>();
         for (int i = 0; i < playlist.size(); i++) {
             if (playlist.get(i) != null) {
@@ -857,13 +900,15 @@
         }
         final List<ControllerInfo> list = getControllers();
         for (int i = 0; i < list.size(); i++) {
-            IMediaSession2Callback controllerBinder =
-                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
-            try {
-                controllerBinder.onPlaylistChanged(bundleList);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Controller is gone", e);
-                // TODO(jaewan): What to do when the controller is gone?
+            final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(
+                    list.get(i), MediaSession2.COMMAND_CODE_PLAYLIST_GET);
+            if (controllerBinder != null) {
+                try {
+                    controllerBinder.onPlaylistChanged(bundleList);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Controller is gone", e);
+                    // TODO(jaewan): What to do when the controller is gone?
+                }
             }
         }
     }
@@ -871,8 +916,10 @@
     public void notifyPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
         final List<ControllerInfo> list = getControllers();
         for (int i = 0; i < list.size(); i++) {
-            IMediaSession2Callback controllerBinder =
-                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
+            final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(list.get(i));
+            if (controllerBinder == null) {
+                return;
+            }
             try {
                 controllerBinder.onPlaylistParamsChanged(params.toBundle());
             } catch (RemoteException e) {
@@ -885,8 +932,10 @@
     public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) {
         final List<ControllerInfo> list = getControllers();
         for (int i = 0; i < list.size(); i++) {
-            IMediaSession2Callback controllerBinder =
-                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
+            final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(list.get(i));
+            if (controllerBinder == null) {
+                return;
+            }
             try {
                 controllerBinder.onPlaybackInfoChanged(((MediaController2Impl.PlaybackInfoImpl)
                         playbackInfo.getProvider()).toBundle());
@@ -906,11 +955,6 @@
         if (command == null) {
             throw new IllegalArgumentException("command shouldn't be null");
         }
-        final IMediaSession2Callback controllerBinder =
-                ControllerInfoImpl.from(controller).getControllerBinder();
-        if (getController(controllerBinder) == null) {
-            throw new IllegalArgumentException("Controller is gone");
-        }
         sendCustomCommandInternal(controller, command, args, receiver);
     }
 
@@ -926,8 +970,10 @@
 
     private void sendCustomCommandInternal(ControllerInfo controller, Command command, Bundle args,
             ResultReceiver receiver) {
-        final IMediaSession2Callback controllerBinder =
-                ControllerInfoImpl.from(controller).getControllerBinder();
+        final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(controller);
+        if (controllerBinder == null) {
+            return;
+        }
         try {
             Bundle commandBundle = command.toBundle();
             controllerBinder.sendCustomCommand(commandBundle, args, receiver);
@@ -943,10 +989,12 @@
 
     public void notifySearchResultChanged(ControllerInfo controller, String query, int itemCount,
             Bundle extras) {
-        final IMediaSession2Callback callbackBinder =
-                ControllerInfoImpl.from(controller).getControllerBinder();
+        final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(controller);
+        if (controllerBinder == null) {
+            return;
+        }
         try {
-            callbackBinder.onSearchResultChanged(query, itemCount, extras);
+            controllerBinder.onSearchResultChanged(query, itemCount, extras);
         } catch (RemoteException e) {
             Log.w(TAG, "Controller is gone", e);
             // TODO(jaewan): What to do when the controller is gone?
@@ -955,9 +1003,6 @@
 
     public void notifyChildrenChangedNotLocked(ControllerInfo controller, String parentId,
             int itemCount, Bundle extras) {
-        // TODO(jaewan): Handle when controller is disconnected and no longer valid.
-        //               Note: Commands may be sent while onConnected() is running. Should we also
-        //                     consider it as error?
         notifyChildrenChangedInternalNotLocked(controller, parentId, itemCount, extras);
     }
 
@@ -978,10 +1023,12 @@
                 return;
             }
         }
-        final IMediaSession2Callback callbackBinder =
-                ControllerInfoImpl.from(controller).getControllerBinder();
+        final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(controller);
+        if (controller == null) {
+            return;
+        }
         try {
-            callbackBinder.onChildrenChanged(parentId, itemCount, extras);
+            controllerBinder.onChildrenChanged(parentId, itemCount, extras);
         } catch (RemoteException e) {
             // TODO(jaewan): Handle controller removed?
         }
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index b0d435d..9a01ade 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -226,9 +226,9 @@
     }
 
     @Override
-    public MediaItem2Provider createMediaItem2(Context context, MediaItem2 instance,
-            String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags) {
-        return new MediaItem2Impl(context, instance, mediaId, dsd, metadata, flags);
+    public MediaItem2Provider.BuilderProvider createMediaItem2Builder(
+            Context context, MediaItem2.Builder instance, int flags) {
+        return new MediaItem2Impl.BuilderImpl(context, instance, flags);
     }
 
     @Override
@@ -249,14 +249,14 @@
 
     @Override
     public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
-            Context context, MediaMetadata2.Builder builder) {
-        return new MediaMetadata2Impl.BuilderImpl(context, builder);
+            Context context, MediaMetadata2.Builder instance) {
+        return new MediaMetadata2Impl.BuilderImpl(context, instance);
     }
 
     @Override
     public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
-            Context context, MediaMetadata2.Builder builder, MediaMetadata2 source) {
-        return new MediaMetadata2Impl.BuilderImpl(context, builder, source);
+            Context context, MediaMetadata2.Builder instance, MediaMetadata2 source) {
+        return new MediaMetadata2Impl.BuilderImpl(context, instance, source);
     }
 
     @Override
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 0efb84a..908952c 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -744,13 +744,11 @@
         testControllerAfterSessionIsGone(id);
     }
 
-    @Ignore
     @Test
     public void testClose_sessionService() throws InterruptedException {
         testCloseFromService(MockMediaSessionService2.ID);
     }
 
-    @Ignore
     @Test
     public void testClose_libraryService() throws InterruptedException {
         testCloseFromService(MockMediaLibraryService2.ID);
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index 3c72e7d..16cc07c 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -404,6 +404,10 @@
                 .setId("testSetCustomLayout")
                 .setSessionCallback(sHandlerExecutor, sessionCallback)
                 .build()) {
+            if (mSession != null) {
+                mSession.close();
+                mSession = session;
+            }
             final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
                 @Override
                 public void onCustomLayoutChanged(List<CommandButton> layout) {
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java b/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
new file mode 100644
index 0000000..d1ff9fb
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
@@ -0,0 +1,357 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+import static android.media.MediaSession2.*;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.media.MediaSession2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.SessionCallback;
+import android.net.Uri;
+import android.os.Process;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mockito;
+
+/**
+ * Tests whether {@link MediaSession2} receives commands that hasn't allowed.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class MediaSession2_PermissionTest extends MediaSession2TestBase {
+    private static final String SESSION_ID = "MediaSession2Test_permission";
+
+    private MockPlayer mPlayer;
+    private MediaSession2 mSession;
+    private MediaSession2.SessionCallback mCallback;
+
+    private static ControllerInfo matchesSelf() {
+        return argThat((controllerInfo) -> controllerInfo.getUid() == Process.myUid());
+    }
+
+    private static Command matches(final int commandCode) {
+        return argThat((command) -> command.getCommandCode() == commandCode);
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    @Override
+    public void cleanUp() throws Exception {
+        super.cleanUp();
+        if (mSession != null) {
+            mSession.close();
+            mSession = null;
+        }
+        mPlayer = null;
+        mCallback = null;
+    }
+
+    private MediaSession2 createSessionWithAllowedActions(CommandGroup commands) {
+        mPlayer = new MockPlayer(0);
+        if (commands == null) {
+            commands = new CommandGroup(mContext);
+        }
+        mCallback = mock(SessionCallback.class);
+        when(mCallback.onConnect(any())).thenReturn(commands);
+        if (mSession != null) {
+            mSession.close();
+        }
+        mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer).setId(SESSION_ID)
+                .setSessionCallback(sHandlerExecutor, mCallback).build();
+        return mSession;
+    }
+
+    private CommandGroup createCommandGroupWith(int commandCode) {
+        CommandGroup commands = new CommandGroup(mContext);
+        commands.addCommand(new Command(mContext, commandCode));
+        return commands;
+    }
+
+    private CommandGroup createCommandGroupWithout(int commandCode) {
+        CommandGroup commands = new CommandGroup(mContext);
+        commands.addAllPredefinedCommands();
+        commands.removeCommand(new Command(mContext, commandCode));
+        return commands;
+    }
+
+    @Test
+    public void testPlay() throws InterruptedException {
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_PLAY));
+        createController(mSession.getToken()).play();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_PLAY));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_PLAY));
+        createController(mSession.getToken()).play();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testPause() throws InterruptedException {
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_PAUSE));
+        createController(mSession.getToken()).pause();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_PAUSE));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_PAUSE));
+        createController(mSession.getToken()).pause();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testStop() throws InterruptedException {
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_STOP));
+        createController(mSession.getToken()).stop();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_STOP));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_STOP));
+        createController(mSession.getToken()).stop();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testSkipToNext() throws InterruptedException {
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+        createController(mSession.getToken()).skipToNext();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+        createController(mSession.getToken()).skipToNext();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testSkipToPrevious() throws InterruptedException {
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+        createController(mSession.getToken()).skipToPrevious();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+        createController(mSession.getToken()).skipToPrevious();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testFastForward() throws InterruptedException {
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_FAST_FORWARD));
+        createController(mSession.getToken()).fastForward();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_FAST_FORWARD));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_FAST_FORWARD));
+        createController(mSession.getToken()).fastForward();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testRewind() throws InterruptedException {
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_REWIND));
+        createController(mSession.getToken()).rewind();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_REWIND));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_REWIND));
+        createController(mSession.getToken()).rewind();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testSeekTo() throws InterruptedException {
+        final long position = 10;
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SEEK_TO));
+        createController(mSession.getToken()).seekTo(position);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_SEEK_TO));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SEEK_TO));
+        createController(mSession.getToken()).seekTo(position);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    // TODO(jaewan): Uncomment when we implement skipToPlaylistItem()
+    /*
+    @Test
+    public void testSkipToPlaylistItem() throws InterruptedException {
+        final Uri uri = Uri.parse("set://current.playlist.item");
+        final DataSourceDesc dsd = new DataSourceDesc.Builder()
+                .setDataSource(mContext, uri).build();
+        final MediaItem2 item = new MediaItem2.Builder(mContext, MediaItem2.FLAG_PLAYABLE)
+                .setDataSourceDesc(dsd).build();
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
+        createController(mSession.getToken()).skipToPlaylistItem(item);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
+        createController(mSession.getToken()).skipToPlaylistItem(item);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+    */
+
+    @Test
+    public void testSetPlaylistParams() throws InterruptedException {
+        final PlaylistParams param = new PlaylistParams(mContext,
+                PlaylistParams.REPEAT_MODE_ALL, PlaylistParams.SHUFFLE_MODE_ALL, null);
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS));
+        createController(mSession.getToken()).setPlaylistParams(param);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS));
+        createController(mSession.getToken()).setPlaylistParams(param);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testSetVolume() throws InterruptedException {
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_SET_VOLUME));
+        createController(mSession.getToken()).setVolumeTo(0, 0);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesSelf(),
+                matches(COMMAND_CODE_SET_VOLUME));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_SET_VOLUME));
+        createController(mSession.getToken()).setVolumeTo(0, 0);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+
+    @Test
+    public void testPlayFromMediaId() throws InterruptedException {
+        final String mediaId = "testPlayFromMediaId";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAY_FROM_MEDIA_ID));
+        createController(mSession.getToken()).playFromMediaId(mediaId, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPlayFromMediaId(matchesSelf(),
+                eq(mediaId), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAY_FROM_MEDIA_ID));
+        createController(mSession.getToken()).playFromMediaId(mediaId, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPlayFromMediaId(any(), any(), any());
+    }
+
+    @Test
+    public void testPlayFromUri() throws InterruptedException {
+        final Uri uri = Uri.parse("play://from.uri");
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAY_FROM_URI));
+        createController(mSession.getToken()).playFromUri(uri, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPlayFromUri(matchesSelf(),
+                eq(uri), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAY_FROM_URI));
+        createController(mSession.getToken()).playFromUri(uri, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPlayFromUri(any(), any(), any());
+    }
+
+    @Test
+    public void testPlayFromSearch() throws InterruptedException {
+        final String query = "testPlayFromSearch";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAY_FROM_SEARCH));
+        createController(mSession.getToken()).playFromSearch(query, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPlayFromSearch(matchesSelf(),
+                eq(query), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAY_FROM_SEARCH));
+        createController(mSession.getToken()).playFromSearch(query, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPlayFromSearch(any(), any(), any());
+    }
+
+    @Test
+    public void testPrepareFromMediaId() throws InterruptedException {
+        final String mediaId = "testPrepareFromMediaId";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_MEDIA_ID));
+        createController(mSession.getToken()).prepareFromMediaId(mediaId, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromMediaId(matchesSelf(),
+                eq(mediaId), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_MEDIA_ID));
+        createController(mSession.getToken()).prepareFromMediaId(mediaId, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromMediaId(any(), any(), any());
+    }
+
+    @Test
+    public void testPrepareFromUri() throws InterruptedException {
+        final Uri uri = Uri.parse("prepare://from.uri");
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_URI));
+        createController(mSession.getToken()).prepareFromUri(uri, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromUri(matchesSelf(),
+                eq(uri), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_URI));
+        createController(mSession.getToken()).prepareFromUri(uri, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromUri(any(), any(), any());
+    }
+
+    @Test
+    public void testPrepareFromSearch() throws InterruptedException {
+        final String query = "testPrepareFromSearch";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_SEARCH));
+        createController(mSession.getToken()).prepareFromSearch(query, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromSearch(matchesSelf(),
+                eq(query), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_SEARCH));
+        createController(mSession.getToken()).prepareFromSearch(query, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromSearch(any(), any(), any());
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 4cdd140..4f344d1 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -20,6 +20,7 @@
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.SessionCallback;
 import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.OnSessionTokensChangedListener;
 import android.media.session.PlaybackState;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -30,7 +31,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.UUID;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import static org.junit.Assert.*;
@@ -209,9 +212,128 @@
         assertTrue(foundTestLibraryService);
     }
 
+    @Test
+    public void testAddOnSessionTokensChangedListener() throws InterruptedException {
+        TokensChangedListener listener = new TokensChangedListener();
+        mManager.addOnSessionTokensChangedListener(sHandlerExecutor, listener);
+
+        listener.reset();
+        MediaSession2 session1 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertTrue(listener.await());
+        assertTrue(listener.findToken(session1.getToken()));
+
+        listener.reset();
+        session1.close();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+
+        listener.reset();
+        MediaSession2 session2 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+        assertTrue(listener.findToken(session2.getToken()));
+
+        listener.reset();
+        MediaSession2 session3 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+        assertTrue(listener.findToken(session2.getToken()));
+        assertTrue(listener.findToken(session3.getToken()));
+
+        listener.reset();
+        session2.close();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+        assertFalse(listener.findToken(session2.getToken()));
+        assertTrue(listener.findToken(session3.getToken()));
+
+        listener.reset();
+        session3.close();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+        assertFalse(listener.findToken(session2.getToken()));
+        assertFalse(listener.findToken(session3.getToken()));
+
+        mManager.removeOnSessionTokensChangedListener(listener);
+    }
+
+    @Test
+    public void testRemoveOnSessionTokensChangedListener() throws InterruptedException {
+        TokensChangedListener listener = new TokensChangedListener();
+        mManager.addOnSessionTokensChangedListener(sHandlerExecutor, listener);
+
+        listener.reset();
+        MediaSession2 session1 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertTrue(listener.await());
+
+        mManager.removeOnSessionTokensChangedListener(listener);
+
+        listener.reset();
+        session1.close();
+        assertFalse(listener.await());
+
+        listener.reset();
+        MediaSession2 session2 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertFalse(listener.await());
+
+        listener.reset();
+        MediaSession2 session3 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertFalse(listener.await());
+
+        listener.reset();
+        session2.close();
+        assertFalse(listener.await());
+
+        listener.reset();
+        session3.close();
+        assertFalse(listener.await());
+    }
+
     // Ensures if the session creation/release is notified to the server.
     private void ensureChangeInSession() throws InterruptedException {
         // TODO(jaewan): Wait by listener.
         Thread.sleep(WAIT_TIME_MS);
     }
+
+    private class TokensChangedListener implements OnSessionTokensChangedListener {
+        private CountDownLatch mLatch;
+        private List<SessionToken2> mTokens;
+
+        private void reset() {
+            mLatch = new CountDownLatch(1);
+            mTokens = null;
+        }
+
+        private boolean await() throws InterruptedException {
+            return mLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+        }
+
+        private boolean findToken(SessionToken2 token) {
+            return mTokens.contains(token);
+        }
+
+        @Override
+        public void onSessionTokensChanged(List<SessionToken2> tokens) {
+            mTokens = tokens;
+            mLatch.countDown();
+        }
+    }
 }
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index 20ea3d2..c18d025 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -241,13 +241,12 @@
 
     private MediaItem2 createMediaItem(String mediaId) {
         Context context = MockMediaLibraryService2.this;
-        return new MediaItem2(
-                context,
-                mediaId,
-                DATA_SOURCE_DESC,
-                new MediaMetadata2.Builder(context)
-                        .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId)
-                        .build(),
-                0 /* Flags */);
+        return new MediaItem2.Builder(context, 0 /* Flags */)
+                .setMediaId(mediaId)
+                .setDataSourceDesc(DATA_SOURCE_DESC)
+                .setMetadata(new MediaMetadata2.Builder(context)
+                                .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId)
+                                .build())
+                .build();
     }
 }
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index ac3202b..c708fee 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -32,7 +32,6 @@
 #include "AAudioService.h"
 #include "AAudioServiceStreamMMAP.h"
 #include "AAudioServiceStreamShared.h"
-#include "AAudioServiceStreamMMAP.h"
 #include "binding/IAAudioService.h"
 #include "ServiceUtilities.h"