diff --git a/base/memory/discardable_shared_memory.cc b/base/memory/discardable_shared_memory.cc index 5ad48a97675c56..57494b592bf804 100644 --- a/base/memory/discardable_shared_memory.cc +++ b/base/memory/discardable_shared_memory.cc @@ -487,12 +487,14 @@ DiscardableSharedMemory::LockResult DiscardableSharedMemory::LockPages( size_t length) { #if defined(OS_ANDROID) if (region.IsValid()) { - int pin_result = - ashmem_pin_region(region.GetPlatformHandle(), offset, length); - if (pin_result == ASHMEM_WAS_PURGED) - return PURGED; - if (pin_result < 0) - return FAILED; + if (ashmem_device_is_supported()) { + int pin_result = + ashmem_pin_region(region.GetPlatformHandle(), offset, length); + if (pin_result == ASHMEM_WAS_PURGED) + return PURGED; + if (pin_result < 0) + return FAILED; + } } #endif return SUCCESS; @@ -505,9 +507,11 @@ void DiscardableSharedMemory::UnlockPages( size_t length) { #if defined(OS_ANDROID) if (region.IsValid()) { - int unpin_result = - ashmem_unpin_region(region.GetPlatformHandle(), offset, length); - DCHECK_EQ(0, unpin_result); + if (ashmem_device_is_supported()) { + int unpin_result = + ashmem_unpin_region(region.GetPlatformHandle(), offset, length); + DCHECK_EQ(0, unpin_result); + } } #endif } @@ -516,4 +520,11 @@ Time DiscardableSharedMemory::Now() const { return Time::Now(); } +#if defined(OS_ANDROID) +// static +bool DiscardableSharedMemory::IsAshmemDeviceSupportedForTesting() { + return ashmem_device_is_supported(); +} +#endif + } // namespace base diff --git a/base/memory/discardable_shared_memory.h b/base/memory/discardable_shared_memory.h index 52a78b131eac3b..44d4cab02a52f2 100644 --- a/base/memory/discardable_shared_memory.h +++ b/base/memory/discardable_shared_memory.h @@ -150,6 +150,12 @@ class BASE_EXPORT DiscardableSharedMemory { trace_event::ProcessMemoryDump* pmd, bool is_owned) const; +#if defined(OS_ANDROID) + // Returns true if the Ashmem device is supported on this system. + // Only use this for unit-testing. + static bool IsAshmemDeviceSupportedForTesting(); +#endif + private: // LockPages/UnlockPages are platform-native discardable page management // helper functions. Both expect |offset| to be specified relative to the diff --git a/base/memory/discardable_shared_memory_unittest.cc b/base/memory/discardable_shared_memory_unittest.cc index 78712d8a720d78..538f5c69ef08de 100644 --- a/base/memory/discardable_shared_memory_unittest.cc +++ b/base/memory/discardable_shared_memory_unittest.cc @@ -245,6 +245,11 @@ TEST(DiscardableSharedMemoryTest, LockShouldAlwaysFailAfterSuccessfulPurge) { TEST(DiscardableSharedMemoryTest, LockShouldFailIfPlatformLockPagesFails) { const uint32_t kDataSize = 1024; + // This test cannot succeed on devices without a proper ashmem device + // because Lock() will always succeed. + if (!DiscardableSharedMemory::IsAshmemDeviceSupportedForTesting()) + return; + DiscardableSharedMemory memory1; bool rv1 = memory1.CreateAndMap(kDataSize); ASSERT_TRUE(rv1); diff --git a/third_party/ashmem/README.chromium b/third_party/ashmem/README.chromium index 36f8f77b8b5b45..ee9d9a7427fcbb 100644 --- a/third_party/ashmem/README.chromium +++ b/third_party/ashmem/README.chromium @@ -8,3 +8,6 @@ Patches: 0001-Add-ashmem-get-prot-region.path: Add ashmem_get_prot_region() declaration and implementation to return the current protection mask of a given Ashmem region. + 0002-Use-AShareMemory-functions-when-possible.patch: + Use ASharedMemory_xxx() functions from libandroid.so when possible + in order to prepare for future devices without ashmem support. diff --git a/third_party/ashmem/ashmem-dev.c b/third_party/ashmem/ashmem-dev.c index 52b3f47eeae0ab..25a33cdcd0c809 100644 --- a/third_party/ashmem/ashmem-dev.c +++ b/third_party/ashmem/ashmem-dev.c @@ -14,23 +14,115 @@ * limitations under the License. */ -/* - * Implementation of the user-space ashmem API for devices, which have our - * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version, - * used by the simulator. - */ +#include "ashmem.h" +#include +#include #include +#include #include +#include #include #include #include +#include /* for fdstat() */ #include #include -#include "ashmem.h" +#include -#define ASHMEM_DEVICE "/dev/ashmem" +#define ASHMEM_DEVICE "/dev/ashmem" + +/* Technical note regarding reading system properties. + * + * Try to use the new __system_property_read_callback API that appeared in + * Android O / API level 26 when available. Otherwise use the deprecated + * __system_property_get function. + * + * For more technical details from an NDK maintainer, see: + * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17 + */ + +/* Weak symbol import */ +void __system_property_read_callback( + const prop_info* info, + void (*callback)( + void* cookie, const char* name, const char* value, uint32_t serial), + void* cookie) __attribute__((weak)); + +/* Callback used with __system_property_read_callback. */ +static void prop_read_int(void* cookie, + const char* name, + const char* value, + uint32_t serial) { + *(int *)cookie = atoi(value); + (void)name; + (void)serial; +} + +static int system_property_get_int(const char* name) { + int result = 0; + if (__system_property_read_callback) { + const prop_info* info = __system_property_find(name); + if (info) + __system_property_read_callback(info, &prop_read_int, &result); + } else { + char value[PROP_VALUE_MAX] = {}; + if (__system_property_get(name, value) >= 1) + result = atoi(value); + } + return result; +} + +static int device_api_level() { + static int s_api_level = -1; + if (s_api_level < 0) + s_api_level = system_property_get_int("ro.build.version.sdk"); + return s_api_level; +} + +typedef enum { + ASHMEM_STATUS_INIT, + ASHMEM_STATUS_NOT_SUPPORTED, + ASHMEM_STATUS_SUPPORTED, +} AshmemStatus; + +static AshmemStatus s_ashmem_status = ASHMEM_STATUS_INIT; +static dev_t s_ashmem_dev; + +/* Return the dev_t of a given file path, or 0 if not available, */ +static dev_t ashmem_find_dev(const char* path) { + struct stat st; + dev_t result = 0; + if (stat(path, &st) == 0 && S_ISCHR(st.st_mode)) + result = st.st_dev; + return result; +} + +static AshmemStatus ashmem_get_status(void) { + /* NOTE: No need to make this thread-safe, assuming that + * all threads will find the same value. */ + if (s_ashmem_status != ASHMEM_STATUS_INIT) + return s_ashmem_status; + + s_ashmem_dev = ashmem_find_dev(ASHMEM_DEVICE); + s_ashmem_status = (s_ashmem_dev == 0) ? ASHMEM_STATUS_NOT_SUPPORTED + : ASHMEM_STATUS_SUPPORTED; + return s_ashmem_status; +} + +/* Returns true iff the ashmem device ioctl should be used for a given fd. + * NOTE: Try not to use fstat() when possible to avoid performance issues. */ +static int ashmem_dev_fd_check(int fd) { + if (device_api_level() <= __ANDROID_API_O_MR1__) + return 1; + if (ashmem_get_status() == ASHMEM_STATUS_SUPPORTED) { + struct stat st; + return (fstat(fd, &st) == 0 && S_ISCHR(st.st_mode) && + st.st_dev != 0 && st.st_dev == s_ashmem_dev); + } + return 0; +} /* * ashmem_create_region - creates a new ashmem region and returns the file @@ -39,67 +131,133 @@ * `name' is an optional label to give the region (visible in /proc/pid/maps) * `size' is the size of the region, in page-aligned bytes */ -int ashmem_create_region(const char *name, size_t size) -{ - int fd, ret; +static int ashmem_dev_create_region(const char *name, size_t size) { + int fd = open(ASHMEM_DEVICE, O_RDWR); + if (fd < 0) + return fd; - fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) - return fd; + int ret; + if (name) { + char buf[ASHMEM_NAME_LEN]; + strlcpy(buf, name, sizeof(buf)); + ret = ioctl(fd, ASHMEM_SET_NAME, buf); + if (ret < 0) + goto error; + } + ret = ioctl(fd, ASHMEM_SET_SIZE, size); + if (ret < 0) + goto error; - if (name) { - char buf[ASHMEM_NAME_LEN]; + return fd; - strlcpy(buf, name, sizeof(buf)); - ret = ioctl(fd, ASHMEM_SET_NAME, buf); - if (ret < 0) - goto error; - } +error: + close(fd); + return ret; +} - ret = ioctl(fd, ASHMEM_SET_SIZE, size); - if (ret < 0) - goto error; +static int ashmem_dev_set_prot_region(int fd, int prot) { + return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); +} - return fd; +static int ashmem_dev_get_prot_region(int fd) { + return ioctl(fd, ASHMEM_GET_PROT_MASK); +} -error: - close(fd); - return ret; +static int ashmem_dev_pin_region(int fd, size_t offset, size_t len) { + struct ashmem_pin pin = { offset, len }; + return ioctl(fd, ASHMEM_PIN, &pin); } -int ashmem_set_prot_region(int fd, int prot) -{ - return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); +static int ashmem_dev_unpin_region(int fd, size_t offset, size_t len) { + struct ashmem_pin pin = { offset, len }; + return ioctl(fd, ASHMEM_UNPIN, &pin); } -int ashmem_get_prot_region(int fd) -{ - return ioctl(fd, ASHMEM_GET_PROT_MASK); +static size_t ashmem_dev_get_size_region(int fd) { + return ioctl(fd, ASHMEM_GET_SIZE, NULL); } -int ashmem_pin_region(int fd, size_t offset, size_t len) -{ - struct ashmem_pin pin = { offset, len }; - return ioctl(fd, ASHMEM_PIN, &pin); +// Starting with API level 26, the following functions from +// libandroid.so should be used to create shared memory regions. +typedef int(*ASharedMemory_createFunc)(const char*, size_t); +typedef size_t(*ASharedMemory_getSizeFunc)(int fd); +typedef int(*ASharedMemory_setProtFunc)(int fd, int prot); + +// Function pointers to shared memory functions. +typedef struct { + ASharedMemory_createFunc create; + ASharedMemory_getSizeFunc getSize; + ASharedMemory_setProtFunc setProt; +} ASharedMemoryFuncs; + +const ASharedMemoryFuncs* ashmem_get_funcs() { + static ASharedMemoryFuncs s_ashmem_funcs = {}; + ASharedMemoryFuncs* funcs = &s_ashmem_funcs; + if (funcs->create == NULL) { + if (device_api_level() >= __ANDROID_API_O__) { + /* Leaked intentionally! */ + void* lib = dlopen("libandroid.so", RTLD_NOW); + funcs->create = (ASharedMemory_createFunc) + dlsym(lib, "ASharedMemory_create"); + funcs->getSize = (ASharedMemory_getSizeFunc) + dlsym(lib, "ASharedMemory_getSize"); + funcs->setProt = (ASharedMemory_setProtFunc) + dlsym(lib, "ASharedMemory_setProt"); + } else { + funcs->create = &ashmem_dev_create_region; + funcs->getSize = &ashmem_dev_get_size_region; + funcs->setProt = &ashmem_dev_set_prot_region; + } + } + return funcs; } -int ashmem_unpin_region(int fd, size_t offset, size_t len) -{ - struct ashmem_pin pin = { offset, len }; - return ioctl(fd, ASHMEM_UNPIN, &pin); +int ashmem_create_region(const char* name, size_t size) { + return ashmem_get_funcs()->create(name, size); } -int ashmem_get_size_region(int fd) -{ - return ioctl(fd, ASHMEM_GET_SIZE, NULL); +int ashmem_set_prot_region(int fd, int prot) { + return ashmem_get_funcs()->setProt(fd, prot); } -int ashmem_purge_all(void) -{ - const int fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) - return fd; - const int ret = ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0); - close(fd); - return ret; +int ashmem_get_prot_region(int fd) { + if (ashmem_dev_fd_check(fd)) + return ashmem_dev_get_prot_region(fd); + /* There are only two practical values to return here: either + * PROT_READ|PROT_WRITE or just PROT_READ, so try to determine + * the flags by trying to mmap() the region read-write first. + */ + int result = PROT_READ; + const size_t page_size = (size_t)sysconf(_SC_PAGESIZE); + void* m = mmap(NULL, page_size, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fd, 0); + if (m != MAP_FAILED) { + munmap(m, page_size); + result = PROT_READ|PROT_WRITE; + } + return result; +} + +int ashmem_pin_region(int fd, size_t offset, size_t len) { + if (ashmem_dev_fd_check(fd)) + return ashmem_dev_pin_region(fd, offset, len); + return ASHMEM_NOT_PURGED; +} + +int ashmem_unpin_region(int fd, size_t offset, size_t len) { + if (ashmem_dev_fd_check(fd)) + return ashmem_dev_unpin_region(fd, offset, len); + /* NOTE: It is not possible to use madvise() here because it requires a + * memory address. This could be done in the caller though, instead of + * this function. */ + return 0; +} + +int ashmem_get_size_region(int fd) { + /* NOTE: Original API returns an int. Avoid breaking it. */ + return (int)ashmem_get_funcs()->getSize(fd); +} + +int ashmem_device_is_supported(void) { + return ashmem_get_status() == ASHMEM_STATUS_SUPPORTED; } diff --git a/third_party/ashmem/ashmem.h b/third_party/ashmem/ashmem.h index d8afccbd2a6e0f..f3675c98b19af9 100644 --- a/third_party/ashmem/ashmem.h +++ b/third_party/ashmem/ashmem.h @@ -16,13 +16,20 @@ extern "C" { #endif +/* Returns true if the ashmem device is supported on this device. + * Not that even if the device is not supported, + * ashmem_{create,set_prot,get_prot,get_size}_region() will still work + * because they will use the ASharedMemory functions from libandroid.so + * instead. But ashmem_{pin,unpin}_region() will be no-ops. + */ +int ashmem_device_is_supported(void); + int ashmem_create_region(const char *name, size_t size); int ashmem_set_prot_region(int fd, int prot); int ashmem_get_prot_region(int fd); int ashmem_pin_region(int fd, size_t offset, size_t len); int ashmem_unpin_region(int fd, size_t offset, size_t len); int ashmem_get_size_region(int fd); -int ashmem_purge_all(void); #ifdef __cplusplus } diff --git a/third_party/ashmem/patches/0002-Use-ASharedMemory-functions-when-possible.patch b/third_party/ashmem/patches/0002-Use-ASharedMemory-functions-when-possible.patch new file mode 100644 index 00000000000000..d9580659150a9e --- /dev/null +++ b/third_party/ashmem/patches/0002-Use-ASharedMemory-functions-when-possible.patch @@ -0,0 +1,332 @@ +diff --git a/third_party/ashmem/ashmem-dev.c b/third_party/ashmem/ashmem-dev.c +index 52b3f47eeae0..25a33cdcd0c8 100644 +--- a/third_party/ashmem/ashmem-dev.c ++++ b/third_party/ashmem/ashmem-dev.c +@@ -14,23 +14,115 @@ + * limitations under the License. + */ + +-/* +- * Implementation of the user-space ashmem API for devices, which have our +- * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version, +- * used by the simulator. +- */ ++#include "ashmem.h" + ++#include ++#include + #include ++#include + #include ++#include + #include + #include + #include ++#include /* for fdstat() */ + #include + + #include +-#include "ashmem.h" ++#include + +-#define ASHMEM_DEVICE "/dev/ashmem" ++#define ASHMEM_DEVICE "/dev/ashmem" ++ ++/* Technical note regarding reading system properties. ++ * ++ * Try to use the new __system_property_read_callback API that appeared in ++ * Android O / API level 26 when available. Otherwise use the deprecated ++ * __system_property_get function. ++ * ++ * For more technical details from an NDK maintainer, see: ++ * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17 ++ */ ++ ++/* Weak symbol import */ ++void __system_property_read_callback( ++ const prop_info* info, ++ void (*callback)( ++ void* cookie, const char* name, const char* value, uint32_t serial), ++ void* cookie) __attribute__((weak)); ++ ++/* Callback used with __system_property_read_callback. */ ++static void prop_read_int(void* cookie, ++ const char* name, ++ const char* value, ++ uint32_t serial) { ++ *(int *)cookie = atoi(value); ++ (void)name; ++ (void)serial; ++} ++ ++static int system_property_get_int(const char* name) { ++ int result = 0; ++ if (__system_property_read_callback) { ++ const prop_info* info = __system_property_find(name); ++ if (info) ++ __system_property_read_callback(info, &prop_read_int, &result); ++ } else { ++ char value[PROP_VALUE_MAX] = {}; ++ if (__system_property_get(name, value) >= 1) ++ result = atoi(value); ++ } ++ return result; ++} ++ ++static int device_api_level() { ++ static int s_api_level = -1; ++ if (s_api_level < 0) ++ s_api_level = system_property_get_int("ro.build.version.sdk"); ++ return s_api_level; ++} ++ ++typedef enum { ++ ASHMEM_STATUS_INIT, ++ ASHMEM_STATUS_NOT_SUPPORTED, ++ ASHMEM_STATUS_SUPPORTED, ++} AshmemStatus; ++ ++static AshmemStatus s_ashmem_status = ASHMEM_STATUS_INIT; ++static dev_t s_ashmem_dev; ++ ++/* Return the dev_t of a given file path, or 0 if not available, */ ++static dev_t ashmem_find_dev(const char* path) { ++ struct stat st; ++ dev_t result = 0; ++ if (stat(path, &st) == 0 && S_ISCHR(st.st_mode)) ++ result = st.st_dev; ++ return result; ++} ++ ++static AshmemStatus ashmem_get_status(void) { ++ /* NOTE: No need to make this thread-safe, assuming that ++ * all threads will find the same value. */ ++ if (s_ashmem_status != ASHMEM_STATUS_INIT) ++ return s_ashmem_status; ++ ++ s_ashmem_dev = ashmem_find_dev(ASHMEM_DEVICE); ++ s_ashmem_status = (s_ashmem_dev == 0) ? ASHMEM_STATUS_NOT_SUPPORTED ++ : ASHMEM_STATUS_SUPPORTED; ++ return s_ashmem_status; ++} ++ ++/* Returns true iff the ashmem device ioctl should be used for a given fd. ++ * NOTE: Try not to use fstat() when possible to avoid performance issues. */ ++static int ashmem_dev_fd_check(int fd) { ++ if (device_api_level() <= __ANDROID_API_O_MR1__) ++ return 1; ++ if (ashmem_get_status() == ASHMEM_STATUS_SUPPORTED) { ++ struct stat st; ++ return (fstat(fd, &st) == 0 && S_ISCHR(st.st_mode) && ++ st.st_dev != 0 && st.st_dev == s_ashmem_dev); ++ } ++ return 0; ++} + + /* + * ashmem_create_region - creates a new ashmem region and returns the file +@@ -39,67 +131,133 @@ + * `name' is an optional label to give the region (visible in /proc/pid/maps) + * `size' is the size of the region, in page-aligned bytes + */ +-int ashmem_create_region(const char *name, size_t size) +-{ +- int fd, ret; ++static int ashmem_dev_create_region(const char *name, size_t size) { ++ int fd = open(ASHMEM_DEVICE, O_RDWR); ++ if (fd < 0) ++ return fd; + +- fd = open(ASHMEM_DEVICE, O_RDWR); +- if (fd < 0) +- return fd; ++ int ret; ++ if (name) { ++ char buf[ASHMEM_NAME_LEN]; ++ strlcpy(buf, name, sizeof(buf)); ++ ret = ioctl(fd, ASHMEM_SET_NAME, buf); ++ if (ret < 0) ++ goto error; ++ } ++ ret = ioctl(fd, ASHMEM_SET_SIZE, size); ++ if (ret < 0) ++ goto error; + +- if (name) { +- char buf[ASHMEM_NAME_LEN]; ++ return fd; + +- strlcpy(buf, name, sizeof(buf)); +- ret = ioctl(fd, ASHMEM_SET_NAME, buf); +- if (ret < 0) +- goto error; +- } ++error: ++ close(fd); ++ return ret; ++} + +- ret = ioctl(fd, ASHMEM_SET_SIZE, size); +- if (ret < 0) +- goto error; ++static int ashmem_dev_set_prot_region(int fd, int prot) { ++ return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); ++} + +- return fd; ++static int ashmem_dev_get_prot_region(int fd) { ++ return ioctl(fd, ASHMEM_GET_PROT_MASK); ++} + +-error: +- close(fd); +- return ret; ++static int ashmem_dev_pin_region(int fd, size_t offset, size_t len) { ++ struct ashmem_pin pin = { offset, len }; ++ return ioctl(fd, ASHMEM_PIN, &pin); + } + +-int ashmem_set_prot_region(int fd, int prot) +-{ +- return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); ++static int ashmem_dev_unpin_region(int fd, size_t offset, size_t len) { ++ struct ashmem_pin pin = { offset, len }; ++ return ioctl(fd, ASHMEM_UNPIN, &pin); + } + +-int ashmem_get_prot_region(int fd) +-{ +- return ioctl(fd, ASHMEM_GET_PROT_MASK); ++static size_t ashmem_dev_get_size_region(int fd) { ++ return ioctl(fd, ASHMEM_GET_SIZE, NULL); + } + +-int ashmem_pin_region(int fd, size_t offset, size_t len) +-{ +- struct ashmem_pin pin = { offset, len }; +- return ioctl(fd, ASHMEM_PIN, &pin); ++// Starting with API level 26, the following functions from ++// libandroid.so should be used to create shared memory regions. ++typedef int(*ASharedMemory_createFunc)(const char*, size_t); ++typedef size_t(*ASharedMemory_getSizeFunc)(int fd); ++typedef int(*ASharedMemory_setProtFunc)(int fd, int prot); ++ ++// Function pointers to shared memory functions. ++typedef struct { ++ ASharedMemory_createFunc create; ++ ASharedMemory_getSizeFunc getSize; ++ ASharedMemory_setProtFunc setProt; ++} ASharedMemoryFuncs; ++ ++const ASharedMemoryFuncs* ashmem_get_funcs() { ++ static ASharedMemoryFuncs s_ashmem_funcs = {}; ++ ASharedMemoryFuncs* funcs = &s_ashmem_funcs; ++ if (funcs->create == NULL) { ++ if (device_api_level() >= __ANDROID_API_O__) { ++ /* Leaked intentionally! */ ++ void* lib = dlopen("libandroid.so", RTLD_NOW); ++ funcs->create = (ASharedMemory_createFunc) ++ dlsym(lib, "ASharedMemory_create"); ++ funcs->getSize = (ASharedMemory_getSizeFunc) ++ dlsym(lib, "ASharedMemory_getSize"); ++ funcs->setProt = (ASharedMemory_setProtFunc) ++ dlsym(lib, "ASharedMemory_setProt"); ++ } else { ++ funcs->create = &ashmem_dev_create_region; ++ funcs->getSize = &ashmem_dev_get_size_region; ++ funcs->setProt = &ashmem_dev_set_prot_region; ++ } ++ } ++ return funcs; + } + +-int ashmem_unpin_region(int fd, size_t offset, size_t len) +-{ +- struct ashmem_pin pin = { offset, len }; +- return ioctl(fd, ASHMEM_UNPIN, &pin); ++int ashmem_create_region(const char* name, size_t size) { ++ return ashmem_get_funcs()->create(name, size); + } + +-int ashmem_get_size_region(int fd) +-{ +- return ioctl(fd, ASHMEM_GET_SIZE, NULL); ++int ashmem_set_prot_region(int fd, int prot) { ++ return ashmem_get_funcs()->setProt(fd, prot); + } + +-int ashmem_purge_all(void) +-{ +- const int fd = open(ASHMEM_DEVICE, O_RDWR); +- if (fd < 0) +- return fd; +- const int ret = ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0); +- close(fd); +- return ret; ++int ashmem_get_prot_region(int fd) { ++ if (ashmem_dev_fd_check(fd)) ++ return ashmem_dev_get_prot_region(fd); ++ /* There are only two practical values to return here: either ++ * PROT_READ|PROT_WRITE or just PROT_READ, so try to determine ++ * the flags by trying to mmap() the region read-write first. ++ */ ++ int result = PROT_READ; ++ const size_t page_size = (size_t)sysconf(_SC_PAGESIZE); ++ void* m = mmap(NULL, page_size, PROT_READ|PROT_WRITE, ++ MAP_PRIVATE, fd, 0); ++ if (m != MAP_FAILED) { ++ munmap(m, page_size); ++ result = PROT_READ|PROT_WRITE; ++ } ++ return result; ++} ++ ++int ashmem_pin_region(int fd, size_t offset, size_t len) { ++ if (ashmem_dev_fd_check(fd)) ++ return ashmem_dev_pin_region(fd, offset, len); ++ return ASHMEM_NOT_PURGED; ++} ++ ++int ashmem_unpin_region(int fd, size_t offset, size_t len) { ++ if (ashmem_dev_fd_check(fd)) ++ return ashmem_dev_unpin_region(fd, offset, len); ++ /* NOTE: It is not possible to use madvise() here because it requires a ++ * memory address. This could be done in the caller though, instead of ++ * this function. */ ++ return 0; ++} ++ ++int ashmem_get_size_region(int fd) { ++ /* NOTE: Original API returns an int. Avoid breaking it. */ ++ return (int)ashmem_get_funcs()->getSize(fd); ++} ++ ++int ashmem_device_is_supported(void) { ++ return ashmem_get_status() == ASHMEM_STATUS_SUPPORTED; + } +diff --git a/third_party/ashmem/ashmem.h b/third_party/ashmem/ashmem.h +index d8afccbd2a6e..f3675c98b19a 100644 +--- a/third_party/ashmem/ashmem.h ++++ b/third_party/ashmem/ashmem.h +@@ -16,13 +16,20 @@ + extern "C" { + #endif + ++/* Returns true if the ashmem device is supported on this device. ++ * Not that even if the device is not supported, ++ * ashmem_{create,set_prot,get_prot,get_size}_region() will still work ++ * because they will use the ASharedMemory functions from libandroid.so ++ * instead. But ashmem_{pin,unpin}_region() will be no-ops. ++ */ ++int ashmem_device_is_supported(void); ++ + int ashmem_create_region(const char *name, size_t size); + int ashmem_set_prot_region(int fd, int prot); + int ashmem_get_prot_region(int fd); + int ashmem_pin_region(int fd, size_t offset, size_t len); + int ashmem_unpin_region(int fd, size_t offset, size_t len); + int ashmem_get_size_region(int fd); +-int ashmem_purge_all(void); + + #ifdef __cplusplus + }