Add HidlMemoryCache and mapMemory(memory_block)

The HidlMemoryCache is a singleton class which provides cache for
sp<IMemory>. It works as an abstraction layer on top of the
IMapper. It supports, but is not limited to, the Ashmem type
HidlMemory.

Bug: 69640640
Test: hidl_test/internal master/sailfish
Change-Id: I69cca18dbdda532eaae409ee9372b3080f7d58dd
diff --git a/libhidlcache/HidlMemoryCache.cpp b/libhidlcache/HidlMemoryCache.cpp
new file mode 100644
index 0000000..6f9c25c
--- /dev/null
+++ b/libhidlcache/HidlMemoryCache.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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_TAG "HidlMemoryCache"
+#include "HidlMemoryCache.h"
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <android/hidl/memory/token/1.0/IMemoryToken.h>
+#include <hidlmemory/mapping.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+
+using IMemoryToken = ::android::hidl::memory::token::V1_0::IMemoryToken;
+using IMemory = ::android::hidl::memory::V1_0::IMemory;
+
+class IMemoryDecorator : public virtual IMemory {
+   public:
+    IMemoryDecorator(sp<IMemory> heap) : mHeap(heap) {}
+    virtual ~IMemoryDecorator(){};
+    Return<void> update() override { return mHeap->update(); }
+    Return<void> read() override { return mHeap->read(); };
+    Return<void> updateRange(uint64_t start, uint64_t length) override {
+        return mHeap->updateRange(start, length);
+    }
+    Return<void> readRange(uint64_t start, uint64_t length) override {
+        return mHeap->readRange(start, length);
+    }
+    Return<void> commit() override { return mHeap->commit(); }
+
+    Return<void*> getPointer() override { return mHeap->getPointer(); }
+    Return<uint64_t> getSize() override { return mHeap->getSize(); }
+
+   protected:
+    sp<IMemory> mHeap;
+};
+
+class IMemoryCacheable : public virtual IMemoryDecorator {
+   public:
+    IMemoryCacheable(sp<IMemory> heap, sp<IMemoryToken> key) : IMemoryDecorator(heap), mKey(key) {}
+    virtual ~IMemoryCacheable() { HidlMemoryCache::getInstance()->flush(mKey); }
+
+   protected:
+    sp<IMemoryToken> mKey;
+};
+
+class IMemoryBlock : public virtual IMemoryDecorator {
+   public:
+    IMemoryBlock(sp<IMemory> heap, uint64_t size, uint64_t offset)
+        : IMemoryDecorator(heap), mSize(size), mOffset(offset), mHeapSize(heap->getSize()) {}
+    bool validRange(uint64_t start, uint64_t length) {
+        return (start + length < mSize) && (start + length >= start) &&
+               (mOffset + mSize < mHeapSize);
+    }
+    Return<void> readRange(uint64_t start, uint64_t length) {
+        if (!validRange(start, length)) {
+            ALOGE("IMemoryBlock::readRange: out of range");
+            Status status;
+            status.setException(Status::EX_ILLEGAL_ARGUMENT, "out of range");
+            return Return<void>(status);
+        }
+        return mHeap->readRange(mOffset + start, length);
+    }
+    Return<void> updateRange(uint64_t start, uint64_t length) override {
+        if (!validRange(start, length)) {
+            ALOGE("IMemoryBlock::updateRange: out of range");
+            return Void();
+        }
+        return mHeap->updateRange(mOffset + start, length);
+    }
+    Return<uint64_t> getSize() override { return mSize; }
+    Return<void*> getPointer() override {
+        void* p = mHeap->getPointer();
+        return (static_cast<char*>(p) + mOffset);
+    }
+
+   protected:
+    uint64_t mSize;
+    uint64_t mOffset;
+    uint64_t mHeapSize;
+};
+
+sp<HidlMemoryCache> HidlMemoryCache::getInstance() {
+    static sp<HidlMemoryCache> instance = new HidlMemoryCache();
+    return instance;
+}
+
+sp<IMemory> HidlMemoryCache::fillLocked(const sp<IMemoryToken>& key) {
+    sp<IMemory> memory = nullptr;
+    Return<void> ret = key->get(
+        [&](const hidl_memory& mem) { memory = new IMemoryCacheable(mapMemory(mem), key); });
+    if (!ret.isOk()) {
+        ALOGE("HidlMemoryCache::fill: cannot IMemoryToken::get.");
+        return nullptr;
+    }
+    mCached[key] = memory;
+    return memory;
+}
+
+sp<IMemory> HidlMemoryCache::map(const MemoryBlock& memblk) {
+    sp<IMemoryToken> token = memblk.token;
+    sp<IMemory> heap = fetch(token);
+    if (heap == nullptr) {
+        return nullptr;
+    }
+    return new IMemoryBlock(heap, memblk.size, memblk.offset);
+}
+
+}  // namespace hardware
+}  // namespace android