문제

I want to have a shared memory block (an ashmem region) that's mapped and accessed from native code. I also want this block to be used by several applications. I also want it to work on SDK level 7 (Android 2.1)

There are two routes. I can create an ashmem region in native code; but then the question is - how do I pass an integer file descriptor to another process? You can marshal FileDescriptor objects via a Parcel, but there's no way to construct one around a raw FD. There's also ParcelFileDescriptor which supports constructing around and retrieving integer FD's, but the relevant methods are only supported in SDK level 12 or even higher.

Alternatively, I can create a MemoryFile. There's a fugly way to pass it around in Parcels. But how do I retrieve a file descriptor from it so that native code has something to mmap()?

도움이 되었습니까?

해결책

On all versions of Android since 1.5 to 4.1, FileDescriptor has an int data member called descriptor. It's package-private on earlier versions of Android, private on recent ones. With a bit of deliberate access control subversion, you can access it - either via reflection, or via JNI. Each can bypass access control - in case of reflection, via Field.setAccessible(), in case of JNI - by default.

With that in mind, you can construct a FileDescriptor around a native FD just fine. Construct a blank one, then set descriptor. That's what bits and pieces of Android code do when constructing those.

Whether this dirty hack will break eventually, who knows. Fortunately, it's not a core piece of functionality in my case - there's some graceful degradation.

One may conditionally employ supported ParcelFileDescriptor methods, if the platform allows, using the field access hack as a fallback. This way, it'll be relatively future proof.

다른 팁

There is a method in helper library libnativehelper.so for this jniCreateFileDescriptor() https://android.googlesource.com/platform/libnativehelper/+/jb-dev/include/nativehelper/JNIHelp.h. It basically does the same thing as stated in previous answer but you might find this approach a bit cleaner.

This is how it worked for me while working with a similar problem:

Instead of using shmfd = open(SHM_PATH, O_RDWR) for creating and getting file descriptor I replaced it with

int fd = ashmem_create_region("SharedRegionName", size); 

and used the file descriptor to get base address:

int base_address = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

// write data You can pass the base_address to your java code from the native code using a native function that returns the descriptor.

Then I create a Service with aidl interface and used this interface to bind this service from another process. From the Service I have used a ParcelFileDescriptor object to return to another process. You can create ParcelFileDescriptor by:

ParcelFileDescriptor desc = ParcelFileDescriptor.fromFd(fd);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top