1diff --git a/third_party/ashmem/ashmem-dev.c b/third_party/ashmem/ashmem-dev.c
2index 52b3f47eeae0..25a33cdcd0c8 100644
3--- a/third_party/ashmem/ashmem-dev.c
4+++ b/third_party/ashmem/ashmem-dev.c
5@@ -14,23 +14,115 @@
6  * limitations under the License.
7  */
8
9-/*
10- * Implementation of the user-space ashmem API for devices, which have our
11- * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
12- * used by the simulator.
13- */
14+#include "ashmem.h"
15
16+#include <dlfcn.h>
17+#include <errno.h>
18 #include <unistd.h>
19+#include <stdlib.h>
20 #include <string.h>
21+#include <sys/mman.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/ioctl.h>
25+#include <sys/stat.h>  /* for fdstat() */
26 #include <fcntl.h>
27
28 #include <linux/ashmem.h>
29-#include "ashmem.h"
30+#include <sys/system_properties.h>
31
32-#define ASHMEM_DEVICE	"/dev/ashmem"
33+#define ASHMEM_DEVICE  "/dev/ashmem"
34+
35+/* Technical note regarding reading system properties.
36+ *
37+ * Try to use the new __system_property_read_callback API that appeared in
38+ * Android O / API level 26 when available. Otherwise use the deprecated
39+ * __system_property_get function.
40+ *
41+ * For more technical details from an NDK maintainer, see:
42+ * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17
43+ */
44+
45+/* Weak symbol import */
46+void __system_property_read_callback(
47+    const prop_info* info,
48+    void (*callback)(
49+        void* cookie, const char* name, const char* value, uint32_t serial),
50+    void* cookie) __attribute__((weak));
51+
52+/* Callback used with __system_property_read_callback. */
53+static void prop_read_int(void* cookie,
54+                          const char* name,
55+                          const char* value,
56+                          uint32_t serial) {
57+  *(int *)cookie = atoi(value);
58+  (void)name;
59+  (void)serial;
60+}
61+
62+static int system_property_get_int(const char* name) {
63+  int result = 0;
64+  if (__system_property_read_callback) {
65+    const prop_info* info = __system_property_find(name);
66+    if (info)
67+      __system_property_read_callback(info, &prop_read_int, &result);
68+  } else {
69+    char value[PROP_VALUE_MAX] = {};
70+    if (__system_property_get(name, value) >= 1)
71+      result = atoi(value);
72+  }
73+  return result;
74+}
75+
76+static int device_api_level() {
77+  static int s_api_level = -1;
78+  if (s_api_level < 0)
79+    s_api_level = system_property_get_int("ro.build.version.sdk");
80+  return s_api_level;
81+}
82+
83+typedef enum {
84+  ASHMEM_STATUS_INIT,
85+  ASHMEM_STATUS_NOT_SUPPORTED,
86+  ASHMEM_STATUS_SUPPORTED,
87+} AshmemStatus;
88+
89+static AshmemStatus s_ashmem_status = ASHMEM_STATUS_INIT;
90+static dev_t s_ashmem_dev;
91+
92+/* Return the dev_t of a given file path, or 0 if not available, */
93+static dev_t ashmem_find_dev(const char* path) {
94+  struct stat st;
95+  dev_t result = 0;
96+  if (stat(path, &st) == 0 && S_ISCHR(st.st_mode))
97+    result = st.st_dev;
98+  return result;
99+}
100+
101+static AshmemStatus ashmem_get_status(void) {
102+  /* NOTE: No need to make this thread-safe, assuming that
103+   * all threads will find the same value. */
104+  if (s_ashmem_status != ASHMEM_STATUS_INIT)
105+    return s_ashmem_status;
106+
107+  s_ashmem_dev = ashmem_find_dev(ASHMEM_DEVICE);
108+  s_ashmem_status = (s_ashmem_dev == 0) ? ASHMEM_STATUS_NOT_SUPPORTED
109+                                        : ASHMEM_STATUS_SUPPORTED;
110+  return s_ashmem_status;
111+}
112+
113+/* Returns true iff the ashmem device ioctl should be used for a given fd.
114+ * NOTE: Try not to use fstat() when possible to avoid performance issues. */
115+static int ashmem_dev_fd_check(int fd) {
116+  if (device_api_level() <= __ANDROID_API_O_MR1__)
117+    return 1;
118+  if (ashmem_get_status() == ASHMEM_STATUS_SUPPORTED) {
119+    struct stat st;
120+    return (fstat(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
121+            st.st_dev != 0 && st.st_dev == s_ashmem_dev);
122+  }
123+  return 0;
124+}
125
126 /*
127  * ashmem_create_region - creates a new ashmem region and returns the file
128@@ -39,67 +131,133 @@
129  * `name' is an optional label to give the region (visible in /proc/pid/maps)
130  * `size' is the size of the region, in page-aligned bytes
131  */
132-int ashmem_create_region(const char *name, size_t size)
133-{
134-	int fd, ret;
135+static int ashmem_dev_create_region(const char *name, size_t size) {
136+  int fd = open(ASHMEM_DEVICE, O_RDWR);
137+  if (fd < 0)
138+    return fd;
139
140-	fd = open(ASHMEM_DEVICE, O_RDWR);
141-	if (fd < 0)
142-		return fd;
143+  int ret;
144+  if (name) {
145+    char buf[ASHMEM_NAME_LEN];
146+    strlcpy(buf, name, sizeof(buf));
147+    ret = ioctl(fd, ASHMEM_SET_NAME, buf);
148+    if (ret < 0)
149+      goto error;
150+  }
151+  ret = ioctl(fd, ASHMEM_SET_SIZE, size);
152+  if (ret < 0)
153+    goto error;
154
155-	if (name) {
156-		char buf[ASHMEM_NAME_LEN];
157+  return fd;
158
159-		strlcpy(buf, name, sizeof(buf));
160-		ret = ioctl(fd, ASHMEM_SET_NAME, buf);
161-		if (ret < 0)
162-			goto error;
163-	}
164+error:
165+  close(fd);
166+  return ret;
167+}
168
169-	ret = ioctl(fd, ASHMEM_SET_SIZE, size);
170-	if (ret < 0)
171-		goto error;
172+static int ashmem_dev_set_prot_region(int fd, int prot) {
173+  return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
174+}
175
176-	return fd;
177+static int ashmem_dev_get_prot_region(int fd) {
178+  return ioctl(fd, ASHMEM_GET_PROT_MASK);
179+}
180
181-error:
182-	close(fd);
183-	return ret;
184+static int ashmem_dev_pin_region(int fd, size_t offset, size_t len) {
185+  struct ashmem_pin pin = { offset, len };
186+  return ioctl(fd, ASHMEM_PIN, &pin);
187 }
188
189-int ashmem_set_prot_region(int fd, int prot)
190-{
191-	return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
192+static int ashmem_dev_unpin_region(int fd, size_t offset, size_t len) {
193+  struct ashmem_pin pin = { offset, len };
194+  return ioctl(fd, ASHMEM_UNPIN, &pin);
195 }
196
197-int ashmem_get_prot_region(int fd)
198-{
199-	return ioctl(fd, ASHMEM_GET_PROT_MASK);
200+static size_t ashmem_dev_get_size_region(int fd) {
201+  return ioctl(fd, ASHMEM_GET_SIZE, NULL);
202 }
203
204-int ashmem_pin_region(int fd, size_t offset, size_t len)
205-{
206-	struct ashmem_pin pin = { offset, len };
207-	return ioctl(fd, ASHMEM_PIN, &pin);
208+// Starting with API level 26, the following functions from
209+// libandroid.so should be used to create shared memory regions.
210+typedef int(*ASharedMemory_createFunc)(const char*, size_t);
211+typedef size_t(*ASharedMemory_getSizeFunc)(int fd);
212+typedef int(*ASharedMemory_setProtFunc)(int fd, int prot);
213+
214+// Function pointers to shared memory functions.
215+typedef struct {
216+  ASharedMemory_createFunc create;
217+  ASharedMemory_getSizeFunc getSize;
218+  ASharedMemory_setProtFunc setProt;
219+} ASharedMemoryFuncs;
220+
221+const ASharedMemoryFuncs* ashmem_get_funcs() {
222+  static ASharedMemoryFuncs s_ashmem_funcs = {};
223+  ASharedMemoryFuncs* funcs = &s_ashmem_funcs;
224+  if (funcs->create == NULL) {
225+    if (device_api_level() >= __ANDROID_API_O__) {
226+      /* Leaked intentionally! */
227+      void* lib = dlopen("libandroid.so", RTLD_NOW);
228+      funcs->create = (ASharedMemory_createFunc)
229+          dlsym(lib, "ASharedMemory_create");
230+      funcs->getSize = (ASharedMemory_getSizeFunc)
231+          dlsym(lib, "ASharedMemory_getSize");
232+      funcs->setProt = (ASharedMemory_setProtFunc)
233+          dlsym(lib, "ASharedMemory_setProt");
234+    } else {
235+      funcs->create = &ashmem_dev_create_region;
236+      funcs->getSize = &ashmem_dev_get_size_region;
237+      funcs->setProt = &ashmem_dev_set_prot_region;
238+    }
239+  }
240+  return funcs;
241 }
242
243-int ashmem_unpin_region(int fd, size_t offset, size_t len)
244-{
245-	struct ashmem_pin pin = { offset, len };
246-	return ioctl(fd, ASHMEM_UNPIN, &pin);
247+int ashmem_create_region(const char* name, size_t size) {
248+  return ashmem_get_funcs()->create(name, size);
249 }
250
251-int ashmem_get_size_region(int fd)
252-{
253-  return ioctl(fd, ASHMEM_GET_SIZE, NULL);
254+int ashmem_set_prot_region(int fd, int prot) {
255+  return ashmem_get_funcs()->setProt(fd, prot);
256 }
257
258-int ashmem_purge_all(void)
259-{
260-  const int fd = open(ASHMEM_DEVICE, O_RDWR);
261-  if (fd < 0)
262-    return fd;
263-  const int ret = ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0);
264-  close(fd);
265-  return ret;
266+int ashmem_get_prot_region(int fd) {
267+  if (ashmem_dev_fd_check(fd))
268+    return ashmem_dev_get_prot_region(fd);
269+  /* There are only two practical values to return here: either
270+   * PROT_READ|PROT_WRITE or just PROT_READ, so try to determine
271+   * the flags by trying to mmap() the region read-write first.
272+   */
273+  int result = PROT_READ;
274+  const size_t page_size = (size_t)sysconf(_SC_PAGESIZE);
275+  void* m = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
276+                 MAP_PRIVATE, fd, 0);
277+  if (m != MAP_FAILED) {
278+    munmap(m, page_size);
279+    result = PROT_READ|PROT_WRITE;
280+  }
281+  return result;
282+}
283+
284+int ashmem_pin_region(int fd, size_t offset, size_t len) {
285+ if (ashmem_dev_fd_check(fd))
286+   return ashmem_dev_pin_region(fd, offset, len);
287+  return ASHMEM_NOT_PURGED;
288+}
289+
290+int ashmem_unpin_region(int fd, size_t offset, size_t len) {
291+  if (ashmem_dev_fd_check(fd))
292+    return ashmem_dev_unpin_region(fd, offset, len);
293+  /* NOTE: It is not possible to use madvise() here because it requires a
294+   * memory address. This could be done in the caller though, instead of
295+   * this function. */
296+  return 0;
297+}
298+
299+int ashmem_get_size_region(int fd) {
300+  /* NOTE: Original API returns an int. Avoid breaking it. */
301+  return (int)ashmem_get_funcs()->getSize(fd);
302+}
303+
304+int ashmem_device_is_supported(void) {
305+  return ashmem_get_status() == ASHMEM_STATUS_SUPPORTED;
306 }
307diff --git a/third_party/ashmem/ashmem.h b/third_party/ashmem/ashmem.h
308index d8afccbd2a6e..f3675c98b19a 100644
309--- a/third_party/ashmem/ashmem.h
310+++ b/third_party/ashmem/ashmem.h
311@@ -16,13 +16,20 @@
312 extern "C" {
313 #endif
314
315+/* Returns true if the ashmem device is supported on this device.
316+ * Not that even if the device is not supported,
317+ * ashmem_{create,set_prot,get_prot,get_size}_region() will still work
318+ * because they will use the ASharedMemory functions from libandroid.so
319+ * instead. But ashmem_{pin,unpin}_region() will be no-ops.
320+ */
321+int ashmem_device_is_supported(void);
322+
323 int ashmem_create_region(const char *name, size_t size);
324 int ashmem_set_prot_region(int fd, int prot);
325 int ashmem_get_prot_region(int fd);
326 int ashmem_pin_region(int fd, size_t offset, size_t len);
327 int ashmem_unpin_region(int fd, size_t offset, size_t len);
328 int ashmem_get_size_region(int fd);
329-int ashmem_purge_all(void);
330
331 #ifdef __cplusplus
332 }
333