Merge "Transfer large bitmaps using ashmem. Bug: 5224703"

This commit is contained in:
Jeff Brown
2011-09-24 20:49:20 -07:00
committed by Android (Google) Code Review
4 changed files with 167 additions and 10 deletions

View File

@@ -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;
}

View File

@@ -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
}
/**

View File

@@ -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; }
};
};
// ---------------------------------------------------------------------------

View File

@@ -30,12 +30,14 @@
#include <utils/TextOutput.h>
#include <utils/misc.h>
#include <utils/Flattenable.h>
#include <cutils/ashmem.h>
#include <private/binder/binder_module.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#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<void*>(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