From 6579a9d6fe2302fa149452f66c4062ebc60c2523 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 23 Sep 2011 21:17:56 -0700 Subject: Transfer large bitmaps using ashmem. Bug: 5224703 Change-Id: If385a66adf4c6179a0bb49c0e6d09a9567e23808 --- core/jni/android/graphics/Bitmap.cpp | 29 ++++++++-- include/binder/Parcel.h | 49 +++++++++++++++- libs/binder/Parcel.cpp | 108 +++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 9 deletions(-) diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 18bd754470f0..da055fcf84fb 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -392,10 +392,20 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { SkSafeUnref(ctable); size_t size = bitmap->getSize(); + + android::Parcel::ReadableBlob blob; + android::status_t status = p->readBlob(size, &blob); + if (status) { + doThrowRE(env, "Could not read bitmap from parcel blob."); + delete bitmap; + return NULL; + } + bitmap->lockPixels(); - memcpy(bitmap->getPixels(), p->readInplace(size), size); + memcpy(bitmap->getPixels(), blob.data(), size); bitmap->unlockPixels(); + blob.release(); return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, density); } @@ -431,17 +441,24 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, } size_t size = bitmap->getSize(); - bitmap->lockPixels(); - void* pDst = p->writeInplace(size); - const void* pSrc = bitmap->getPixels(); + android::Parcel::WritableBlob blob; + android::status_t status = p->writeBlob(size, &blob); + if (status) { + doThrowRE(env, "Could not write bitmap to parcel blob."); + return false; + } + bitmap->lockPixels(); + const void* pSrc = bitmap->getPixels(); if (pSrc == NULL) { - memset(pDst, 0, size); + memset(blob.data(), 0, size); } else { - memcpy(pDst, pSrc, size); + memcpy(blob.data(), pSrc, size); } bitmap->unlockPixels(); + + blob.release(); return true; } diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index 57f5dd22e34b..d9737852d9fb 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -38,6 +38,9 @@ struct flat_binder_object; // defined in support_p/binder_module.h class Parcel { public: + class ReadableBlob; + class WritableBlob; + Parcel(); ~Parcel(); @@ -111,7 +114,13 @@ public: // Place a file descriptor into the parcel. A dup of the fd is made, which // will be closed once the parcel is destroyed. status_t writeDupFileDescriptor(int fd); - + + // Writes a blob to the parcel. + // If the blob is small, then it is stored in-place, otherwise it is + // transferred by way of an anonymous shared memory region. + // The caller should call release() on the blob after writing its contents. + status_t writeBlob(size_t len, WritableBlob* outBlob); + status_t writeObject(const flat_binder_object& val, bool nullMetaData); // Like Parcel.java's writeNoException(). Just writes a zero int32. @@ -159,7 +168,11 @@ public: // Retrieve a file descriptor from the parcel. This returns the raw fd // in the parcel, which you do not own -- use dup() to get your own copy. int readFileDescriptor() const; - + + // Reads a blob from the parcel. + // The caller should call release() on the blob after reading its contents. + status_t readBlob(size_t len, ReadableBlob* outBlob) const; + const flat_binder_object* readObject(bool nullMetaData) const; // Explicitly close all file descriptors in the parcel. @@ -179,7 +192,7 @@ public: release_func relFunc, void* relCookie); void print(TextOutput& to, uint32_t flags = 0) const; - + private: Parcel(const Parcel& o); Parcel& operator=(const Parcel& o); @@ -218,6 +231,36 @@ private: release_func mOwner; void* mOwnerCookie; + + class Blob { + public: + Blob(); + ~Blob(); + + void release(); + inline size_t size() const { return mSize; } + + protected: + void init(bool mapped, void* data, size_t size); + void clear(); + + bool mMapped; + void* mData; + size_t mSize; + }; + +public: + class ReadableBlob : public Blob { + friend class Parcel; + public: + inline const void* data() const { return mData; } + }; + + class WritableBlob : public Blob { + friend class Parcel; + public: + inline void* data() { return mData; } + }; }; // --------------------------------------------------------------------------- diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 8eeab7aef6fc..9552c1cfc5c4 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -30,12 +30,14 @@ #include #include #include +#include #include #include #include #include +#include #ifndef INT32_MAX #define INT32_MAX ((int32_t)(2147483647)) @@ -54,6 +56,9 @@ // Note: must be kept in sync with android/os/Parcel.java's EX_HAS_REPLY_HEADER #define EX_HAS_REPLY_HEADER -128 +// Maximum size of a blob to transfer in-place. +static const size_t IN_PLACE_BLOB_LIMIT = 40 * 1024; + // XXX This can be made public if we want to provide // support for typed data. struct small_flat_data @@ -718,6 +723,54 @@ status_t Parcel::writeDupFileDescriptor(int fd) return writeObject(obj, true); } +status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) +{ + status_t status; + + if (!mAllowFds || len <= IN_PLACE_BLOB_LIMIT) { + LOGV("writeBlob: write in place"); + status = writeInt32(0); + if (status) return status; + + void* ptr = writeInplace(len); + if (!ptr) return NO_MEMORY; + + outBlob->init(false /*mapped*/, ptr, len); + return NO_ERROR; + } + + LOGV("writeBlob: write to ashmem"); + int fd = ashmem_create_region("Parcel Blob", len); + if (fd < 0) return NO_MEMORY; + + int result = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); + if (result < 0) { + status = -result; + } else { + void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + status = -errno; + } else { + result = ashmem_set_prot_region(fd, PROT_READ); + if (result < 0) { + status = -result; + } else { + status = writeInt32(1); + if (!status) { + status = writeFileDescriptor(fd); + if (!status) { + outBlob->init(true /*mapped*/, ptr, len); + return NO_ERROR; + } + } + } + } + ::munmap(ptr, len); + } + ::close(fd); + return status; +} + status_t Parcel::write(const Flattenable& val) { status_t err; @@ -1040,6 +1093,32 @@ int Parcel::readFileDescriptor() const return BAD_TYPE; } +status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const +{ + int32_t useAshmem; + status_t status = readInt32(&useAshmem); + if (status) return status; + + if (!useAshmem) { + LOGV("readBlob: read in place"); + const void* ptr = readInplace(len); + if (!ptr) return BAD_VALUE; + + outBlob->init(false /*mapped*/, const_cast(ptr), len); + return NO_ERROR; + } + + LOGV("readBlob: read from ashmem"); + int fd = readFileDescriptor(); + if (fd == int(BAD_TYPE)) return BAD_VALUE; + + void* ptr = ::mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); + if (!ptr) return NO_MEMORY; + + outBlob->init(true /*mapped*/, ptr, len); + return NO_ERROR; +} + status_t Parcel::read(Flattenable& val) const { // size @@ -1469,4 +1548,33 @@ void Parcel::scanForFds() const mFdsKnown = true; } +// --- Parcel::Blob --- + +Parcel::Blob::Blob() : + mMapped(false), mData(NULL), mSize(0) { +} + +Parcel::Blob::~Blob() { + release(); +} + +void Parcel::Blob::release() { + if (mMapped && mData) { + ::munmap(mData, mSize); + } + clear(); +} + +void Parcel::Blob::init(bool mapped, void* data, size_t size) { + mMapped = mapped; + mData = data; + mSize = size; +} + +void Parcel::Blob::clear() { + mMapped = false; + mData = NULL; + mSize = 0; +} + }; // namespace android -- cgit v1.2.3-59-g8ed1b