123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- 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 <dlfcn.h>
- +#include <errno.h>
- #include <unistd.h>
- +#include <stdlib.h>
- #include <string.h>
- +#include <sys/mman.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/ioctl.h>
- +#include <sys/stat.h> /* for fdstat() */
- #include <fcntl.h>
-
- #include <linux/ashmem.h>
- -#include "ashmem.h"
- +#include <sys/system_properties.h>
-
- -#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
- }
|