Search code examples
androidandroid-ndkshared-memoryparcelashmem

Shared memory region in NDK


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()?


Solution

  • 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.