diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 18bd754470f0b..da055fcf84fbc 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(); + + 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(); - void* pDst = p->writeInplace(size); - 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/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 79acd552b5832..ca24d7fff3b66 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -1061,7 +1061,7 @@ public final class Bitmap implements Parcelable { * No special parcel contents. */ public int describeContents() { - return 0; + return Parcelable.CONTENTS_FILE_DESCRIPTOR; // uses parcel blobs } /** diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index bfe13f01bc7fe..53a53c422fc54 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(); @@ -109,7 +112,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. @@ -157,7 +166,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. @@ -177,7 +190,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); @@ -215,6 +228,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 a0fc4d05bc8f1..126d3f57a5693 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 @@ -706,6 +711,47 @@ status_t Parcel::writeDupFileDescriptor(int fd) return writeObject(obj, true); } +status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) +{ + if (len <= IN_PLACE_BLOB_LIMIT) { + LOGV("writeBlob: write in place"); + 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; + + status_t status; + 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 = 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; @@ -1025,6 +1071,28 @@ int Parcel::readFileDescriptor() const return BAD_TYPE; } +status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const +{ + if (len <= IN_PLACE_BLOB_LIMIT) { + 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 @@ -1452,4 +1520,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