| Index: src/ports/SkPurgeableMemoryBlock_android.cpp | 
| diff --git a/src/ports/SkPurgeableMemoryBlock_android.cpp b/src/ports/SkPurgeableMemoryBlock_android.cpp | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..7c06b933edecd345f2b5cf83f71ae97b2b1ba3e5 | 
| --- /dev/null | 
| +++ b/src/ports/SkPurgeableMemoryBlock_android.cpp | 
| @@ -0,0 +1,117 @@ | 
| +/* | 
| + * Copyright 2013 Google Inc. | 
| + * | 
| + * Use of this source code is governed by a BSD-style license that can be | 
| + * found in the LICENSE file. | 
| + */ | 
| + | 
| +#include "SkPurgeableMemoryBlock.h" | 
| + | 
| +#include "android/ashmem.h" | 
| +#include <sys/mman.h> | 
| +#include <unistd.h> | 
| + | 
| +bool SkPurgeableMemoryBlock::PlatformSupportsPurgeableMemory() { | 
| +    return true; | 
| +} | 
| + | 
| +#ifdef SK_DEBUG | 
| +bool SkPurgeableMemoryBlock::PlatformSupportsPurgingAllUnpinnedBlocks() { | 
| +    return false; | 
| +} | 
| + | 
| +bool SkPurgeableMemoryBlock::PurgeAllUnpinnedBlocks() { | 
| +    return false; | 
| +} | 
| + | 
| +bool SkPurgeableMemoryBlock::purge() { | 
| +    SkASSERT(!fPinned); | 
| +    if (-1 != fFD) { | 
| +        munmap(fAddr, fSize); | 
| +        close(fFD); | 
| +        fFD = -1; | 
| +        fAddr = NULL; | 
| +        fSize = 0; | 
| +        return true; | 
| +    } else { | 
| +        return false; | 
| +    } | 
| +} | 
| +#endif | 
| + | 
| +// ashmem likes lengths on page boundaries. | 
| +static size_t round_to_page_size(size_t size) { | 
| +    const size_t mask = getpagesize() - 1; | 
| +    size_t newSize = (size + mask) & ~mask; | 
| +    return newSize; | 
| +} | 
| + | 
| +SkPurgeableMemoryBlock::SkPurgeableMemoryBlock(size_t size) | 
| +    : fAddr(NULL) | 
| +    , fSize(0) | 
| +    , fPinned(false) | 
| +    , fFD(-1) { | 
| +    size = round_to_page_size(size); | 
| +    int fd = ashmem_create_region(NULL, size); | 
| +    if (-1 == fd) { | 
| +        SkDebugf("ashmem_create_region failed\n"); | 
| +        return; | 
| +    } | 
| + | 
| +    int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); | 
| +    if (err != 0) { | 
| +        SkDebugf("ashmem_set_prot_region failed\n"); | 
| +        close(fd); | 
| +        return; | 
| +    } | 
| + | 
| +    void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); | 
| +    if (-1 == (long) addr) { | 
| +        SkDebugf("mmap failed\n"); | 
| +        close(fd); | 
| +        return; | 
| +    } | 
| +    fAddr = addr; | 
| +    fSize = size; | 
| +    fFD = fd; | 
| +    (void) ashmem_pin_region(fd, 0, 0); | 
| +    fPinned = true; | 
| +} | 
| + | 
| +SkPurgeableMemoryBlock::~SkPurgeableMemoryBlock() { | 
| +    if (-1 != fFD) { | 
| +        munmap(fAddr, fSize); | 
| +        close(fFD); | 
| +    } | 
| +} | 
| + | 
| +SkPurgeableMemoryBlock::PinResult SkPurgeableMemoryBlock::pin() { | 
| +    SkASSERT(!fPinned); | 
| +    if (-1 != fFD) { | 
| +        int pin = ashmem_pin_region(fFD, 0, 0); | 
| +        if (ASHMEM_NOT_PURGED == pin) { | 
| +            fPinned = true; | 
| +            return kRetained_PinResult; | 
| +        } else if (ASHMEM_WAS_PURGED == pin) { | 
| +            fPinned = true; | 
| +            return kUninitialized_PinResult; | 
| +        } else { | 
| +            // Failed. | 
| +            munmap(fAddr, fSize); | 
| +            close(fFD); | 
| +            fFD = -1; | 
| +            fAddr = NULL; | 
| +            fSize = 0; | 
| +            // Fall through | 
| +        } | 
| +    } | 
| +    return kFailed_PinResult; | 
| +} | 
| + | 
| +void SkPurgeableMemoryBlock::unpin() { | 
| +    if (-1 != fFD) { | 
| +        ashmem_unpin_region(fFD, 0, 0); | 
| +        fPinned = false; | 
| +    } | 
| +} | 
| + | 
|  |