incubator-weex icon indicating copy to clipboard operation
incubator-weex copied to clipboard

[Android] Weex failed on Android Q failed with targetSdkVersion=29

Open LiGe888 opened this issue 5 years ago • 5 comments

Describe the bug

Android Q when I set targetSdkVersion=29 weex can not work well but targetSdkVersion=28 weex works OK

To Reproduce

The same source code behave defferent,I only changed targetSdkVersion

Environment

  • Device: OPPO Reno
  • OS: Android Q
  • Version 0.26.0
  • Build from source [no]

Expected behavior

May be Permission denied

Screenshots

Additional context

LiGe888 avatar Jul 15 '19 07:07 LiGe888

If you remain the targetSdkVersion as it is(26) and run it on Android Q device, you will see everything is fine.

Maybe Android Q change their C++ memory permission mechanism, maybe this is a bug of Android Q itself. Either way, Weex won't change targetAPI(neither do you, I think) or any code for this issue before Android Q release. After all, it works well on Android Q emulator.

YorkShen avatar Jul 16 '19 07:07 YorkShen

fix ashmem.c bug on android Q ,if build targetSdkVerson=29.

/**

  • Licensed to the Apache Software Foundation (ASF) under one
  • or more contributor license agreements. See the NOTICE file
  • distributed with this work for additional information
  • regarding copyright ownership. The ASF licenses this file
  • to you under the Apache License, Version 2.0 (the
  • "License"); you may not use this file except in compliance
  • with the License. You may obtain a copy of the License at
  • http://www.apache.org/licenses/LICENSE-2.0
  • Unless required by applicable law or agreed to in writing,
  • software distributed under the License is distributed on an
  • "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  • KIND, either express or implied. See the License for the
  • specific language governing permissions and 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 <dlfcn.h> #include <errno.h> #include <pthread.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/mman.h> #include "ashmem.h" #include <linux/ashmem.h> #include <sys/system_properties.h>

#define ASHMEM_DEVICE "/dev/ashmem"

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

  • descriptor, or <0 on error

  • `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 */ static int ashmem_dev_create_region(const char *name, size_t size) { int 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;

    return fd;

    error: close(fd); return ret; }

static int ashmem_dev_set_prot_region(int fd, int prot) { return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); }

static int ashmem_dev_get_prot_region(int fd) { return ioctl(fd, ASHMEM_GET_PROT_MASK); }

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

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

static size_t ashmem_dev_get_size_region(int fd) { return ioctl(fd, ASHMEM_GET_SIZE, NULL); }

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

static ASharedMemoryFuncs s_ashmem_funcs = {}; static pthread_once_t s_ashmem_funcs_once = PTHREAD_ONCE_INIT;

static void ashmem_init_funcs() { ASharedMemoryFuncs* funcs = &s_ashmem_funcs; 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; } }

static const ASharedMemoryFuncs* ashmem_get_funcs() { pthread_once(&s_ashmem_funcs_once, ashmem_init_funcs); return &s_ashmem_funcs; }

/*

  • ashmem_create_region - creates a new ashmem region and returns the file
  • descriptor, or <0 on error
  • `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) { return ashmem_get_funcs()->create(name, size); }

int ashmem_set_prot_region(int fd, int prot) { return ashmem_get_funcs()->setProt(fd, prot); }

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

nm-cmd avatar Mar 13 '20 13:03 nm-cmd

@nm-cmd Could you please give us a PR? I'd be very happy to review and merge it.

FYI: I just try to upgrade to Android 29, and stuck by this ASHMEM issue.

YorkShen avatar Mar 14 '20 02:03 YorkShen

基于 @nm-cmd 修改的代码,在Android Q上可以正常工作。我去掉了一些没有调用的函数。

weex_core/Source/third_party/IPC/ashmem.h
@@ -33,10 +33,7 @@ extern "C" {
 
 int ashmem_create_region(const char* name, size_t size);
 int ashmem_set_prot_region(int fd, int prot);
-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
 }
weex_core/Source/third_party/IPC/ashmem.c
@@ -31,9 +31,44 @@
 
 #include "ashmem.h"
 #include <linux/ashmem.h>
+#include <stdlib.h>
+#include <sys/system_properties.h>
+#include <dlfcn.h>
+#include <pthread.h>
 
 #define ASHMEM_DEVICE "/dev/ashmem"
 
+static int system_property_get_int(const char* name) {
+    int result = 0;
+    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 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;
+
+static ASharedMemoryFuncs s_ashmem_funcs = {
+        NULL, NULL, NULL
+};
+static pthread_once_t s_ashmem_funcs_once = PTHREAD_ONCE_INIT;
+
 /*
  * ashmem_create_region - creates a new ashmem region and returns the file
  * descriptor, or <0 on error
@@ -41,7 +76,7 @@
  * `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)
+static int ashmem_dev_create_region(const char* name, size_t size)
 {
     int fd, ret;
 
@@ -69,34 +104,56 @@ error:
     return ret;
 }
 
-int ashmem_set_prot_region(int fd, int prot)
+static int ashmem_dev_set_prot_region(int fd, int prot)
 {
     return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
 }
 
-int ashmem_pin_region(int fd, size_t offset, size_t len)
+static size_t ashmem_dev_get_size_region(int fd)
 {
-    struct ashmem_pin pin = { offset, len };
-    return ioctl(fd, ASHMEM_PIN, &pin);
+    return (size_t)ioctl(fd, ASHMEM_GET_SIZE, NULL);
 }
 
-int ashmem_unpin_region(int fd, size_t offset, size_t len)
-{
-    struct ashmem_pin pin = { offset, len };
-    return ioctl(fd, ASHMEM_UNPIN, &pin);
+static void ashmem_init_funcs() {
+    ASharedMemoryFuncs* funcs = &s_ashmem_funcs;
+    // ANDROID_API_Q
+    if (device_api_level() >= 29) {
+        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");
+    }
+
+    if(funcs->create == NULL) {
+        funcs->create = &ashmem_dev_create_region;
+    }
+    if(funcs->getSize == NULL) {
+        funcs->getSize = &ashmem_dev_get_size_region;
+    }
+    if(funcs->setProt == NULL) {
+        funcs->setProt = &ashmem_dev_set_prot_region;
+    }
 }
 
-int ashmem_get_size_region(int fd)
+static const ASharedMemoryFuncs* ashmem_get_funcs() {
+    pthread_once(&s_ashmem_funcs_once, ashmem_init_funcs);
+    return &s_ashmem_funcs;
+}
+
+int ashmem_create_region(const char *name, size_t size)
 {
-    return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+    return ashmem_get_funcs()->create(name, size);
 }
 
-int ashmem_purge_all(void)
+int ashmem_set_prot_region(int fd, int prot)
 {
-    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;
+    return ashmem_get_funcs()->setProt(fd, prot);
 }
+
+int ashmem_get_size_region(int fd)
+{
+    return (int)ashmem_get_funcs()->getSize(fd);
+}

ikantech avatar May 19 '20 11:05 ikantech

When this ASHMEM issue fix?

neuyu avatar Jul 24 '20 12:07 neuyu