Move Codec2-related code from hardware/google/av

Test: None
Bug: 112362730
Change-Id: Ie2f8ff431d65c40333f267ab9877d47089adeea4
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
new file mode 100644
index 0000000..22e8d84
--- /dev/null
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -0,0 +1,743 @@
+/*
+ * Copyright (C) 2016 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 "C2AllocatorGralloc"
+#include <utils/Log.h>
+
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2Buffer.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+namespace {
+    enum : uint64_t {
+        /**
+         * Usage mask that is passed through from gralloc to Codec 2.0 usage.
+         */
+        PASSTHROUGH_USAGE_MASK =
+            ~(GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_PROTECTED)
+    };
+
+    // verify that passthrough mask is within the platform mask
+    static_assert((~C2MemoryUsage::PLATFORM_MASK & PASSTHROUGH_USAGE_MASK) == 0, "");
+}
+
+C2MemoryUsage C2AndroidMemoryUsage::FromGrallocUsage(uint64_t usage) {
+    // gralloc does not support WRITE_PROTECTED
+    return C2MemoryUsage(
+            ((usage & GRALLOC_USAGE_SW_READ_MASK) ? C2MemoryUsage::CPU_READ : 0) |
+            ((usage & GRALLOC_USAGE_SW_WRITE_MASK) ? C2MemoryUsage::CPU_WRITE : 0) |
+            ((usage & GRALLOC_USAGE_PROTECTED) ? C2MemoryUsage::READ_PROTECTED : 0) |
+            (usage & PASSTHROUGH_USAGE_MASK));
+}
+
+uint64_t C2AndroidMemoryUsage::asGrallocUsage() const {
+    // gralloc does not support WRITE_PROTECTED
+    return (((expected & C2MemoryUsage::CPU_READ) ? GRALLOC_USAGE_SW_READ_OFTEN : 0) |
+            ((expected & C2MemoryUsage::CPU_WRITE) ? GRALLOC_USAGE_SW_WRITE_OFTEN : 0) |
+            ((expected & C2MemoryUsage::READ_PROTECTED) ? GRALLOC_USAGE_PROTECTED : 0) |
+            (expected & PASSTHROUGH_USAGE_MASK));
+}
+
+using ::android::hardware::graphics::allocator::V2_0::IAllocator;
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::graphics::mapper::V2_0::BufferDescriptor;
+using ::android::hardware::graphics::mapper::V2_0::Error;
+using ::android::hardware::graphics::mapper::V2_0::IMapper;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_vec;
+
+namespace {
+
+struct BufferDescriptorInfo {
+    IMapper::BufferDescriptorInfo mapperInfo;
+    uint32_t stride;
+};
+
+}
+
+/* ===================================== GRALLOC ALLOCATION ==================================== */
+static c2_status_t maperr2error(Error maperr) {
+    switch (maperr) {
+        case Error::NONE:           return C2_OK;
+        case Error::BAD_DESCRIPTOR: return C2_BAD_VALUE;
+        case Error::BAD_BUFFER:     return C2_BAD_VALUE;
+        case Error::BAD_VALUE:      return C2_BAD_VALUE;
+        case Error::NO_RESOURCES:   return C2_NO_MEMORY;
+        case Error::UNSUPPORTED:    return C2_CANNOT_DO;
+    }
+    return C2_CORRUPTED;
+}
+
+static
+bool native_handle_is_invalid(const native_handle_t *const handle) {
+    // perform basic validation of a native handle
+    if (handle == nullptr) {
+        // null handle is considered valid
+        return false;
+    }
+    return ((size_t)handle->version != sizeof(native_handle_t) ||
+            handle->numFds < 0 ||
+            handle->numInts < 0 ||
+            // for sanity assume handles must occupy less memory than INT_MAX bytes
+            handle->numFds > int((INT_MAX - handle->version) / sizeof(int)) - handle->numInts);
+}
+
+class C2HandleGralloc : public C2Handle {
+private:
+    struct ExtraData {
+        uint32_t width;
+        uint32_t height;
+        uint32_t format;
+        uint32_t usage_lo;
+        uint32_t usage_hi;
+        uint32_t stride;
+        uint32_t generation;
+        uint32_t igbp_id_lo;
+        uint32_t igbp_id_hi;
+        uint32_t igbp_slot;
+        uint32_t magic;
+    };
+
+    enum {
+        NUM_INTS = sizeof(ExtraData) / sizeof(int),
+    };
+    const static uint32_t MAGIC = '\xc2gr\x00';
+
+    static
+    const ExtraData* getExtraData(const C2Handle *const handle) {
+        if (handle == nullptr
+                || native_handle_is_invalid(handle)
+                || handle->numInts < NUM_INTS) {
+            return nullptr;
+        }
+        return reinterpret_cast<const ExtraData*>(
+                &handle->data[handle->numFds + handle->numInts - NUM_INTS]);
+    }
+
+    static
+    ExtraData *getExtraData(C2Handle *const handle) {
+        return const_cast<ExtraData *>(getExtraData(const_cast<const C2Handle *const>(handle)));
+    }
+
+public:
+    void getIgbpData(uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) const {
+        const ExtraData *ed = getExtraData(this);
+        *generation = ed->generation;
+        *igbp_id = unsigned(ed->igbp_id_lo) | uint64_t(unsigned(ed->igbp_id_hi)) << 32;
+        *igbp_slot = ed->igbp_slot;
+    }
+
+    static bool isValid(const C2Handle *const o) {
+        if (o == nullptr) { // null handle is always valid
+            return true;
+        }
+        const ExtraData *xd = getExtraData(o);
+        // we cannot validate width/height/format/usage without accessing gralloc driver
+        return xd != nullptr && xd->magic == MAGIC;
+    }
+
+    static C2HandleGralloc* WrapNativeHandle(
+            const native_handle_t *const handle,
+            uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+            uint32_t stride, uint32_t generation, uint64_t igbp_id = 0, uint32_t igbp_slot = 0) {
+        //CHECK(handle != nullptr);
+        if (native_handle_is_invalid(handle) ||
+            handle->numInts > int((INT_MAX - handle->version) / sizeof(int)) - NUM_INTS - handle->numFds) {
+            return nullptr;
+        }
+        ExtraData xd = {
+            width, height, format, uint32_t(usage & 0xFFFFFFFF), uint32_t(usage >> 32),
+            stride, generation, uint32_t(igbp_id & 0xFFFFFFFF), uint32_t(igbp_id >> 32),
+            igbp_slot, MAGIC
+        };
+        native_handle_t *res = native_handle_create(handle->numFds, handle->numInts + NUM_INTS);
+        if (res != nullptr) {
+            memcpy(&res->data, &handle->data, sizeof(int) * (handle->numFds + handle->numInts));
+            *getExtraData(res) = xd;
+        }
+        return reinterpret_cast<C2HandleGralloc *>(res);
+    }
+
+    static native_handle_t* UnwrapNativeHandle(
+            const C2Handle *const handle) {
+        const ExtraData *xd = getExtraData(handle);
+        if (xd == nullptr || xd->magic != MAGIC) {
+            return nullptr;
+        }
+        native_handle_t *res = native_handle_create(handle->numFds, handle->numInts - NUM_INTS);
+        if (res != nullptr) {
+            memcpy(&res->data, &handle->data, sizeof(int) * (res->numFds + res->numInts));
+        }
+        return res;
+    }
+
+    static native_handle_t* UnwrapNativeHandle(
+            const C2Handle *const handle,
+            uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) {
+        const ExtraData *xd = getExtraData(handle);
+        if (xd == nullptr || xd->magic != MAGIC) {
+            return nullptr;
+        }
+        *generation = xd->generation;
+        *igbp_id = unsigned(xd->igbp_id_lo) | uint64_t(unsigned(xd->igbp_id_hi)) << 32;
+        *igbp_slot = xd->igbp_slot;
+        native_handle_t *res = native_handle_create(handle->numFds, handle->numInts - NUM_INTS);
+        if (res != nullptr) {
+            memcpy(&res->data, &handle->data, sizeof(int) * (res->numFds + res->numInts));
+        }
+        return res;
+    }
+
+    static const C2HandleGralloc* Import(
+            const C2Handle *const handle,
+            uint32_t *width, uint32_t *height, uint32_t *format,
+            uint64_t *usage, uint32_t *stride,
+            uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) {
+        const ExtraData *xd = getExtraData(handle);
+        if (xd == nullptr) {
+            return nullptr;
+        }
+        *width = xd->width;
+        *height = xd->height;
+        *format = xd->format;
+        *usage = xd->usage_lo | (uint64_t(xd->usage_hi) << 32);
+        *stride = xd->stride;
+        *generation = xd->generation;
+        *igbp_id = xd->igbp_id_lo | (uint64_t(xd->igbp_id_hi) << 32);
+        *igbp_slot = xd->igbp_slot;
+        return reinterpret_cast<const C2HandleGralloc *>(handle);
+    }
+};
+
+native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) {
+    return C2HandleGralloc::UnwrapNativeHandle(handle);
+}
+
+native_handle_t *UnwrapNativeCodec2GrallocHandle(
+        const C2Handle *const handle,
+        uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) {
+    return C2HandleGralloc::UnwrapNativeHandle(handle, generation, igbp_id, igbp_slot);
+}
+
+C2Handle *WrapNativeCodec2GrallocHandle(
+        const native_handle_t *const handle,
+        uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride,
+        uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) {
+    return C2HandleGralloc::WrapNativeHandle(handle, width, height, format, usage, stride,
+                                             generation, igbp_id, igbp_slot);
+}
+
+class C2AllocationGralloc : public C2GraphicAllocation {
+public:
+    virtual ~C2AllocationGralloc() override;
+
+    virtual c2_status_t map(
+            C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
+            C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) override;
+    virtual c2_status_t unmap(
+            uint8_t **addr /* nonnull */, C2Rect rect, C2Fence *fence /* nullable */) override;
+    virtual C2Allocator::id_t getAllocatorId() const override { return mAllocatorId; }
+    virtual const C2Handle *handle() const override { return mLockedHandle ? : mHandle; }
+    virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const override;
+
+    // internal methods
+    // |handle| will be moved.
+    C2AllocationGralloc(
+              const BufferDescriptorInfo &info,
+              const sp<IMapper> &mapper,
+              hidl_handle &hidlHandle,
+              const C2HandleGralloc *const handle,
+              C2Allocator::id_t allocatorId);
+    int dup() const;
+    c2_status_t status() const;
+
+private:
+    const BufferDescriptorInfo mInfo;
+    const sp<IMapper> mMapper;
+    const hidl_handle mHidlHandle;
+    const C2HandleGralloc *mHandle;
+    buffer_handle_t mBuffer;
+    const C2HandleGralloc *mLockedHandle;
+    bool mLocked;
+    C2Allocator::id_t mAllocatorId;
+    std::mutex mMappedLock;
+};
+
+C2AllocationGralloc::C2AllocationGralloc(
+          const BufferDescriptorInfo &info,
+          const sp<IMapper> &mapper,
+          hidl_handle &hidlHandle,
+          const C2HandleGralloc *const handle,
+          C2Allocator::id_t allocatorId)
+    : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
+      mInfo(info),
+      mMapper(mapper),
+      mHidlHandle(std::move(hidlHandle)),
+      mHandle(handle),
+      mBuffer(nullptr),
+      mLockedHandle(nullptr),
+      mLocked(false),
+      mAllocatorId(allocatorId) {
+}
+
+C2AllocationGralloc::~C2AllocationGralloc() {
+    if (!mBuffer) {
+        return;
+    }
+    if (mLocked) {
+        // implementation ignores addresss and rect
+        uint8_t* addr[C2PlanarLayout::MAX_NUM_PLANES] = {};
+        unmap(addr, C2Rect(), nullptr);
+    }
+    mMapper->freeBuffer(const_cast<native_handle_t *>(mBuffer));
+    native_handle_delete(const_cast<native_handle_t*>(
+            reinterpret_cast<const native_handle_t*>(mHandle)));
+}
+
+c2_status_t C2AllocationGralloc::map(
+        C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
+        C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) {
+    uint64_t grallocUsage = static_cast<C2AndroidMemoryUsage>(usage).asGrallocUsage();
+    ALOGV("mapping buffer with usage %#llx => %#llx",
+          (long long)usage.expected, (long long)grallocUsage);
+
+    // TODO
+    (void) fence;
+
+    std::lock_guard<std::mutex> lock(mMappedLock);
+    if (mBuffer && mLocked) {
+        ALOGD("already mapped");
+        return C2_DUPLICATE;
+    }
+    if (!layout || !addr) {
+        ALOGD("wrong param");
+        return C2_BAD_VALUE;
+    }
+
+    c2_status_t err = C2_OK;
+    if (!mBuffer) {
+        mMapper->importBuffer(
+                mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) {
+                    err = maperr2error(maperr);
+                    if (err == C2_OK) {
+                        mBuffer = static_cast<buffer_handle_t>(buffer);
+                    }
+                });
+        if (err != C2_OK) {
+            ALOGD("importBuffer failed: %d", err);
+            return err;
+        }
+        if (mBuffer == nullptr) {
+            ALOGD("importBuffer returned null buffer");
+            return C2_CORRUPTED;
+        }
+        uint32_t generation = 0;
+        uint64_t igbp_id = 0;
+        uint32_t igbp_slot = 0;
+        if (mHandle) {
+            mHandle->getIgbpData(&generation, &igbp_id, &igbp_slot);
+        }
+        mLockedHandle = C2HandleGralloc::WrapNativeHandle(
+                mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height,
+                (uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride,
+                generation, igbp_id, igbp_slot);
+    }
+
+    // UGLY HACK: assume YCbCr 4:2:0 8-bit format (and lockable via lockYCbCr) if we don't
+    // recognize the format
+    PixelFormat format = mInfo.mapperInfo.format;
+    if (format != PixelFormat::RGBA_8888 && format != PixelFormat::RGBX_8888) {
+        format = PixelFormat::YCBCR_420_888;
+    }
+
+    switch (format) {
+        case PixelFormat::YCBCR_420_888:
+        case PixelFormat::YV12: {
+            YCbCrLayout ycbcrLayout;
+            mMapper->lockYCbCr(
+                    const_cast<native_handle_t *>(mBuffer), grallocUsage,
+                    { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
+                    // TODO: fence
+                    hidl_handle(),
+                    [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
+                        err = maperr2error(maperr);
+                        if (err == C2_OK) {
+                            ycbcrLayout = mapLayout;
+                        }
+                    });
+            if (err != C2_OK) {
+                ALOGD("lockYCbCr failed: %d", err);
+                return err;
+            }
+            addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y;
+            addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb;
+            addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
+            layout->type = C2PlanarLayout::TYPE_YUV;
+            layout->numPlanes = 3;
+            layout->rootPlanes = 3;
+            layout->planes[C2PlanarLayout::PLANE_Y] = {
+                C2PlaneInfo::CHANNEL_Y,         // channel
+                1,                              // colInc
+                (int32_t)ycbcrLayout.yStride,   // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+                C2PlanarLayout::PLANE_Y,        // rootIx
+                0,                              // offset
+            };
+            layout->planes[C2PlanarLayout::PLANE_U] = {
+                C2PlaneInfo::CHANNEL_CB,          // channel
+                (int32_t)ycbcrLayout.chromaStep,  // colInc
+                (int32_t)ycbcrLayout.cStride,     // rowInc
+                2,                                // mColSampling
+                2,                                // mRowSampling
+                8,                                // allocatedDepth
+                8,                                // bitDepth
+                0,                                // rightShift
+                C2PlaneInfo::NATIVE,              // endianness
+                C2PlanarLayout::PLANE_U,          // rootIx
+                0,                                // offset
+            };
+            layout->planes[C2PlanarLayout::PLANE_V] = {
+                C2PlaneInfo::CHANNEL_CR,          // channel
+                (int32_t)ycbcrLayout.chromaStep,  // colInc
+                (int32_t)ycbcrLayout.cStride,     // rowInc
+                2,                                // mColSampling
+                2,                                // mRowSampling
+                8,                                // allocatedDepth
+                8,                                // bitDepth
+                0,                                // rightShift
+                C2PlaneInfo::NATIVE,              // endianness
+                C2PlanarLayout::PLANE_V,          // rootIx
+                0,                                // offset
+            };
+            // handle interleaved formats
+            intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U];
+            if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chromaStep) {
+                layout->rootPlanes = 2;
+                layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U;
+                layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset;
+            } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chromaStep) {
+                layout->rootPlanes = 2;
+                layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V;
+                layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset;
+            }
+            break;
+        }
+
+        case PixelFormat::RGBA_8888:
+            // TODO: alpha channel
+            // fall-through
+        case PixelFormat::RGBX_8888: {
+            void *pointer = nullptr;
+            mMapper->lock(
+                    const_cast<native_handle_t *>(mBuffer),
+                    grallocUsage,
+                    { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
+                    // TODO: fence
+                    hidl_handle(),
+                    [&err, &pointer](const auto &maperr, const auto &mapPointer) {
+                        err = maperr2error(maperr);
+                        if (err == C2_OK) {
+                            pointer = mapPointer;
+                        }
+                    });
+            if (err != C2_OK) {
+                ALOGD("lock failed: %d", err);
+                return err;
+            }
+            addr[C2PlanarLayout::PLANE_R] = (uint8_t *)pointer;
+            addr[C2PlanarLayout::PLANE_G] = (uint8_t *)pointer + 1;
+            addr[C2PlanarLayout::PLANE_B] = (uint8_t *)pointer + 2;
+            layout->type = C2PlanarLayout::TYPE_RGB;
+            layout->numPlanes = 3;
+            layout->rootPlanes = 1;
+            layout->planes[C2PlanarLayout::PLANE_R] = {
+                C2PlaneInfo::CHANNEL_R,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+                C2PlanarLayout::PLANE_R,        // rootIx
+                0,                              // offset
+            };
+            layout->planes[C2PlanarLayout::PLANE_G] = {
+                C2PlaneInfo::CHANNEL_G,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+                C2PlanarLayout::PLANE_R,        // rootIx
+                1,                              // offset
+            };
+            layout->planes[C2PlanarLayout::PLANE_B] = {
+                C2PlaneInfo::CHANNEL_B,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+                C2PlanarLayout::PLANE_R,        // rootIx
+                2,                              // offset
+            };
+            break;
+        }
+        default: {
+            ALOGD("unsupported pixel format: %d", mInfo.mapperInfo.format);
+            return C2_OMITTED;
+        }
+    }
+    mLocked = true;
+
+    return C2_OK;
+}
+
+c2_status_t C2AllocationGralloc::unmap(
+        uint8_t **addr, C2Rect rect, C2Fence *fence /* nullable */) {
+    // TODO: check addr and size, use fence
+    (void)addr;
+    (void)rect;
+
+    std::lock_guard<std::mutex> lock(mMappedLock);
+    c2_status_t err = C2_OK;
+    mMapper->unlock(
+            const_cast<native_handle_t *>(mBuffer),
+            [&err, &fence](const auto &maperr, const auto &releaseFence) {
+                // TODO
+                (void) fence;
+                (void) releaseFence;
+                err = maperr2error(maperr);
+                if (err == C2_OK) {
+                    // TODO: fence
+                }
+            });
+    if (err == C2_OK) {
+        mLocked = false;
+    }
+    return err;
+}
+
+bool C2AllocationGralloc::equals(const std::shared_ptr<const C2GraphicAllocation> &other) const {
+    return other && other->handle() == handle();
+}
+
+/* ===================================== GRALLOC ALLOCATOR ==================================== */
+class C2AllocatorGralloc::Impl {
+public:
+    Impl(id_t id, bool bufferQueue);
+
+    id_t getId() const {
+        return mTraits->id;
+    }
+
+    C2String getName() const {
+        return mTraits->name;
+    }
+
+    std::shared_ptr<const C2Allocator::Traits> getTraits() const {
+        return mTraits;
+    }
+
+    c2_status_t newGraphicAllocation(
+            uint32_t width, uint32_t height, uint32_t format, const C2MemoryUsage &usage,
+            std::shared_ptr<C2GraphicAllocation> *allocation);
+
+    c2_status_t priorGraphicAllocation(
+            const C2Handle *handle,
+            std::shared_ptr<C2GraphicAllocation> *allocation);
+
+    c2_status_t status() const { return mInit; }
+
+private:
+    std::shared_ptr<C2Allocator::Traits> mTraits;
+    c2_status_t mInit;
+    sp<IAllocator> mAllocator;
+    sp<IMapper> mMapper;
+    const bool mBufferQueue;
+};
+
+void _UnwrapNativeCodec2GrallocMetadata(
+        const C2Handle *const handle,
+        uint32_t *width, uint32_t *height, uint32_t *format,uint64_t *usage, uint32_t *stride,
+        uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) {
+    (void)C2HandleGralloc::Import(handle, width, height, format, usage, stride,
+                                  generation, igbp_id, igbp_slot);
+}
+
+C2AllocatorGralloc::Impl::Impl(id_t id, bool bufferQueue)
+    : mInit(C2_OK), mBufferQueue(bufferQueue) {
+    // TODO: get this from allocator
+    C2MemoryUsage minUsage = { 0, 0 }, maxUsage = { ~(uint64_t)0, ~(uint64_t)0 };
+    Traits traits = { "android.allocator.gralloc", id, C2Allocator::GRAPHIC, minUsage, maxUsage };
+    mTraits = std::make_shared<C2Allocator::Traits>(traits);
+
+    // gralloc allocator is a singleton, so all objects share a global service
+    mAllocator = IAllocator::getService();
+    mMapper = IMapper::getService();
+    if (mAllocator == nullptr || mMapper == nullptr) {
+        mInit = C2_CORRUPTED;
+    }
+}
+
+c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
+        uint32_t width, uint32_t height, uint32_t format, const C2MemoryUsage &usage,
+        std::shared_ptr<C2GraphicAllocation> *allocation) {
+    uint64_t grallocUsage = static_cast<C2AndroidMemoryUsage>(usage).asGrallocUsage();
+    ALOGV("allocating buffer with usage %#llx => %#llx",
+          (long long)usage.expected, (long long)grallocUsage);
+
+    BufferDescriptorInfo info = {
+        {
+            width,
+            height,
+            1u,  // layerCount
+            (PixelFormat)format,
+            grallocUsage,
+        },
+        0u,  // stride placeholder
+    };
+    c2_status_t err = C2_OK;
+    BufferDescriptor desc;
+    mMapper->createDescriptor(
+            info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
+                err = maperr2error(maperr);
+                if (err == C2_OK) {
+                    desc = descriptor;
+                }
+            });
+    if (err != C2_OK) {
+        return err;
+    }
+
+    // IAllocator shares IMapper error codes.
+    hidl_handle buffer;
+    mAllocator->allocate(
+            desc,
+            1u,
+            [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
+                err = maperr2error(maperr);
+                if (err != C2_OK) {
+                    return;
+                }
+                if (buffers.size() != 1u) {
+                    err = C2_CORRUPTED;
+                    return;
+                }
+                info.stride = stride;
+                buffer = std::move(buffers[0]);
+            });
+    if (err != C2_OK) {
+        return err;
+    }
+
+
+    allocation->reset(new C2AllocationGralloc(
+            info, mMapper, buffer,
+            C2HandleGralloc::WrapNativeHandle(
+                    buffer.getNativeHandle(),
+                    info.mapperInfo.width, info.mapperInfo.height,
+                    (uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride,
+                    0, 0, mBufferQueue ? ~0 : 0),
+            mTraits->id));
+    return C2_OK;
+}
+
+c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation(
+        const C2Handle *handle,
+        std::shared_ptr<C2GraphicAllocation> *allocation) {
+    BufferDescriptorInfo info;
+    info.mapperInfo.layerCount = 1u;
+    uint32_t generation;
+    uint64_t igbp_id;
+    uint32_t igbp_slot;
+    const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
+            handle,
+            &info.mapperInfo.width, &info.mapperInfo.height,
+            (uint32_t *)&info.mapperInfo.format, (uint64_t *)&info.mapperInfo.usage, &info.stride,
+            &generation, &igbp_id, &igbp_slot);
+    if (grallocHandle == nullptr) {
+        return C2_BAD_VALUE;
+    }
+
+    hidl_handle hidlHandle;
+    hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
+
+    allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle, mTraits->id));
+    return C2_OK;
+}
+
+C2AllocatorGralloc::C2AllocatorGralloc(id_t id, bool bufferQueue)
+        : mImpl(new Impl(id, bufferQueue)) {}
+
+C2AllocatorGralloc::~C2AllocatorGralloc() { delete mImpl; }
+
+C2Allocator::id_t C2AllocatorGralloc::getId() const {
+    return mImpl->getId();
+}
+
+C2String C2AllocatorGralloc::getName() const {
+    return mImpl->getName();
+}
+
+std::shared_ptr<const C2Allocator::Traits> C2AllocatorGralloc::getTraits() const {
+    return mImpl->getTraits();
+}
+
+c2_status_t C2AllocatorGralloc::newGraphicAllocation(
+        uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+        std::shared_ptr<C2GraphicAllocation> *allocation) {
+    return mImpl->newGraphicAllocation(width, height, format, usage, allocation);
+}
+
+c2_status_t C2AllocatorGralloc::priorGraphicAllocation(
+        const C2Handle *handle,
+        std::shared_ptr<C2GraphicAllocation> *allocation) {
+    return mImpl->priorGraphicAllocation(handle, allocation);
+}
+
+c2_status_t C2AllocatorGralloc::status() const {
+    return mImpl->status();
+}
+
+bool C2AllocatorGralloc::isValid(const C2Handle* const o) {
+    return C2HandleGralloc::isValid(o);
+}
+
+} // namespace android