I keep trying Multi Processing (using Shared Memory) in Android Studio (With NDK). I installed NDK, LLDB, CMake. Also I use API Level 26 and min SDK is also 26 (OREO, 8.0).
I created native_lib.cpp, and making some files for testing FD.
I made simple small class for testing .
Class has int FileDescriptor, char* buffer.
I checked variables and it seems like made succesfully. ASharedMemory_Create() returns fd, and i can get size of memory from ASharedMemory_getSize(int fd).
But how can i access shared memory from another process? Am i need to use java for IPC? If i can i want to use native only. Currently java is only for UI.
https://developer.android.com/ndk/reference/group/memory
I checekd here. If there are something that worth i can refer please let me know. Thank you.
Edited---------------------------------------------------
I want to make shared memory in both of parent and child. Also want to access shared memory freely. Child to child, child to parents, parents to child.
Making File Descriptor and buffer in parents works smoothly, but when i try to make buffer or fd in child, i cant access it.
I used same name and size for ASharedMemory_create, but other process making different File descriptor in my opinion. Don't know whats wrong.
Below is my native_lib.cpp for testing. Functions matched with Buttons except Init. Init called onCreate.
int fd[4];
char* buffer[4];
int myFd;
int* cBuf;
const char* names[4] = {"Test", "Inter", "Process", "Mech"};
const int size = 512;
extern "C" JNIEXPORT void JNICALL
Java_org_techtwon_multipro_MainActivity_SyncBuffer(
JNIEnv *env,
jobject /* this */) {
int cVal;
memcpy(&cVal, cBuf, sizeof(int));
for(int i = 0 ; i < cVal; ++i)
{
if(fd[i] != NULL) {
buffer[i] = (char *) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0);
}
}
}
extern "C" JNIEXPORT void JNICALL
Java_org_techtwon_multipro_MainActivity_MakeFileDesc(
JNIEnv *env,
jobject /* this */) {
pid_t pid = fork();
int cVal;
if(pid < 0) {
return;
} else if(pid == 0) {
memcpy(&cVal, cBuf, sizeof(int));
fd[cVal] = ASharedMemory_create(names[cVal], size);
buffer[cVal] = (char*) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[cVal], 0);
memset(buffer[cVal], 0, size);
memcpy(buffer[cVal], names[cVal], strlen(names[cVal]));
cVal++;
memcpy(cBuf, &cVal, sizeof(int));
sleep(1);
exit(1);
}
}
extern "C" JNIEXPORT void JNICALL
Java_org_techtwon_multipro_MainActivity_Init(
JNIEnv *env,
jobject /* this */) {
myFd = ASharedMemory_create("Num", sizeof(int));
cBuf = (int*) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, myFd, 0);
for(int i = 0 ; i < 4; ++i) fd[i] = ASharedMemory_create(names[i], size);
buffer[0] = (char*) mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd[0], 0);
memcpy(buffer[0], names[0], strlen(names[0]));
memset(cBuf, 0, sizeof(int));
}
extern "C" JNIEXPORT jint JNICALL
Java_org_techtwon_multipro_MainActivity_GetFd(
JNIEnv *env,
jobject /* this */, jint idx) {
return fd[idx];
}
extern "C" JNIEXPORT jbyteArray JNICALL
Java_org_techtwon_multipro_MainActivity_GetBuffer(
JNIEnv *env,
jobject /* this */, jint idx) {
jbyteArray tmp = (*env).NewByteArray(strlen(buffer[idx]));
env->SetByteArrayRegion(tmp, 0, strlen(buffer[idx]), (jbyte*) buffer[idx]);
return tmp;
}
extern "C" JNIEXPORT jint JNICALL
Java_org_techtwon_multipro_MainActivity_GetcVal(
JNIEnv *env,
jobject /* this */, jint idx) {
int cVal;
memcpy(&cVal, cBuf, sizeof(int));
return cVal;
}
The code snippet in official docs has it quite clear:
int fd = ASharedMemory_create("memory", 128);
// By default it has PROT_READ | PROT_WRITE | PROT_EXEC.
char *buffer = (char *) mmap(NULL, 128, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
strcpy(buffer, "This is an example."); // trivially initialize content
// limit access to read only
ASharedMemory_setProt(fd, PROT_READ);
// share fd with another process here and the other process can only map with PROT_READ.
The name has no meaning, only helpful for debugging. The size should match.
This is the API you should use for API 29 and higher, the old ways (below) don't work anymore.
If you need also to cover devices below API 26, you need a fallback that makes direct IOCTLs to /dev/ashmem file descriptors. This was available since Android early days:
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/ashmem.h>
int fd = open("/dev/ashmem", O_RDWR);
ioctl(fd, ASHMEM_SET_NAME, "memory");
ioctl(fd, ASHMEM_SET_SIZE, 128);
char *buffer = (char * ) mmap(NULL, 128, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
There even is a nice example of wrapping this shared memory for use in Java: ANDROID – CREATING SHARED MEMORY USING ASHMEM.
Related
Hi everyone I have a problem on certain android devices (This device is running android 5.1)
I have the following code
extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_peachss_sadldecoder_Decoder_getDecodedPhoto(JNIEnv *env, jclass clazz,
jbyteArray photo_data, jint size) {
jbyte* input= env->GetByteArrayElements(photo_data, 0);
WiResultImage decode = DecodeImage((unsigned char*)input, size + 1);
env->ReleaseByteArrayElements(photo_data, input, JNI_ABORT);
jbyteArray output = env->NewByteArray(decode.size);
env->SetByteArrayRegion(output, 0, decode.size, (jbyte*)decode.raw);
return output;
}
The moment it reaches 'NewByteArray' I get the following error
Signal: SIGABRT (signal SIGABRT)
I'm trying the use the OpenSSL Rand library with DRBG in an android app, this library is implemented in native code of NDK. At starting, the OpenSSL works fine but in many times the app crashed and don't show any throw message. Here is the only error message that showwing:
A/libc: Fatal signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xb1aedca6f2d64adf in tid 22101 (RenderThread), pid 22075 (android.example)
My code is the follow:
libnative.cpp
#include <jni.h>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <openssl/rand.h>
#include <openssl/rand_drbg.h>
#include <android/log.h>
#include <future>
#ifndef TAG
#define TAG "OpenSslApi"
#endif
#ifndef DEFAULT_VALUE_ERROR
#define DEFAULT_VALUE_ERROR 0
#endif
void thread_handler(union sigval sv) {}
extern "C"
JNIEXPORT void JNICALL
Java_com_android_random_OpenSslApi_initDrbgRandom(
JNIEnv * env,
jclass clazz) {
RAND_DRBG * randDrbgInstance = RAND_DRBG_new(NID_aes_256_ctr, RAND_DRBG_FLAG_CTR_NO_DF, nullptr);
RAND_DRBG_instantiate(randDrbgInstance, nullptr, 0);
RAND_DRBG_set_reseed_time_interval(randDrbgInstance, 0);
RAND_DRBG_set_reseed_interval(randDrbgInstance, 0);
}
std::pair < jint * , jint > generateRandomIntDrbg(jint * secureRandom, jint sizeKey) {
jint myStatus = RAND_DRBG_bytes(
RAND_DRBG_get0_public(),
(unsigned char * ) secureRandom,
sizeKey * sizeof(int)
);
return std::make_pair(secureRandom, myStatus);
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_android_random_OpenSslApi_intDrbgGenerateSecureRandom(
JNIEnv * env,
jclass clazz,
jintArray empty_array,
jint size_key) {
struct sigevent sev {};
timer_t timerid;
memset( & sev, 0, sizeof(sev));
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = & thread_handler;
sev.sigev_value.sival_ptr = & timerid;
timer_create(CLOCK_MONOTONIC, & sev, & timerid);
JavaVM * javaVm = nullptr;
env -> GetJavaVM( & javaVm);
javaVm -> AttachCurrentThread( & env, (void ** ) & env);
jintArray array = env -> NewIntArray(size_key);
if (array == nullptr) {
return DEFAULT_VALUE_ERROR;
}
jint * secureRandomIntArray = env -> GetIntArrayElements(array, nullptr);
if (secureRandomIntArray == nullptr) {
return DEFAULT_VALUE_ERROR;
}
std::future < std::pair < jint * , jint >> futureIntRandom = std::async (generateRandomIntDrbg, secureRandomIntArray, size_key);
std::pair < jint * , jint > result = futureIntRandom.get();
jint * resultSecureRandom = std::get < jint * > (result);
if (resultSecureRandom == nullptr) {
return DEFAULT_VALUE_ERROR;
}
memcpy(secureRandomIntArray, empty_array, size_key);
env -> ReleaseIntArrayElements(empty_array, secureRandomIntArray, 0);
return std::get < jint > (result);
}
OpenSslApi.java
static {
System.loadLibrary("libnative");
}
public OpenSslApi() {
initDrbgRandom();
}
public static native void initDrbgRandom();
public static native int intDrbgGenerateSecureRandom(
int[] emptyArray,
final int sizeKey
);
Thanks for either suggestions about the solution of this error.
I had to use NDK in my java code to call some audio processing functions, but i have never tried JNI or C/C++ coding. I tried to start from the scratch but i really have no time. so i found this library here and tried to use it. I always get a fatal signal 11 (sigsegv) error. I really don't know where to start. here's what i have done so far:
in the Android Activity:
public native int transform(double[] real,double[]imag, int n);
public native int convolve(double[]xreal,double[]ximag,double[]yreal,double[]yimag,double[]outreal,double[]outim, int n);
and they are called in the same file like this :
transform(raw1, imag1,raw1.length);//result back into each vector
transform(raw2, imag2,raw2.length);
convolve(raw1,imag1,raw2,imag2,outre,outim,raw1.length);
in the C file:
JNIEXPORT jint JNICALL Java_com_example_ffttest_FFTActivity_transform
(JNIEnv *env, jobject obj, jdoubleArray real, jdoubleArray imag, jint n)
{
if (n == 0)
return 1;
else if ((n & (n - 1)) == 0) // Is power of 2
return transform_radix2(real, imag, n);
else // More complicated algorithm for arbitrary sizes
return transform_bluestein(real, imag, n);
}
and made some changes to the convolve function:
JNIEXPORT jint JNICALL Java_com_example_ffttest_FFTActivity_convolve
(JNIEnv *env, jobject obj, jdoubleArray xreal, jdoubleArray ximag, jdoubleArray yreal, jdoubleArray yimag, jdoubleArray outreal, jdoubleArray outimag, jint n)
{
//size = n * sizeof(double);
n = (*env)->GetArrayLength(env, xreal);
jdouble *a=(*env)->GetDoubleArrayElements(env,xreal,0);
jdouble *b=(*env)->GetDoubleArrayElements(env,ximag,0);
jdouble *c=(*env)->GetDoubleArrayElements(env,yreal,0);
jdouble *d=(*env)->GetDoubleArrayElements(env,yimag,0);
jdouble *e=(*env)->GetDoubleArrayElements(env,outreal,0);
jdouble *f=(*env)->GetDoubleArrayElements(env,outimag,0);
...
Is there some changes i need to know from C to JNI?
I have two functions and I'm getting a ReferenceTable overflow.
The summary of consumed array entries is:
1 of byte[] (3 elements)
446 of byte[] (75 elements) (2 unique instances)
576 of byte[] (147 elements) (2 unique instances)
1 of int[] (25 elements)
I really checked the code to find any mistake, but didn't found it. Im releasing arrays after getting them. The only thing is that these functions are called thousands of times, can this be the cause?
Here is all my native code:
called once:
JNIEXPORT void JNICALL Java_ar_com_teasoft_Image_nativeUnlock(
JNIEnv *env, jclass clazz, jobject bitmap) {
AndroidBitmap_unlockPixels(env, bitmap);
}
called once:
JNIEXPORT jlong JNICALL Java_ar_com_teasoft_Image_nativeLock(
JNIEnv *env, jclass clazz, jobject bitmap) {
int ret;
AndroidBitmapInfo info;
if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
return 0;
}
if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
LOGE("Bitmap format is not RGBA_8888!");
return 0;
}
void* bitmapPixels;
if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0) {
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return 0;
}
return (jlong) bitmapPixels;
}
called lots of times:
JNIEXPORT void JNICALL Java_ar_com_teasoft_Image_nativeCopyPixels(
JNIEnv *env, jclass clazz, jlong dataRef, jintArray sourceIndexes,
jintArray targetIndexes, jint count) {
argb* sourcePixels;
argb* targetPixels;
jint *sourceArray = env->GetIntArrayElements(sourceIndexes, NULL);
jint *targetArray = env->GetIntArrayElements(targetIndexes, NULL);
for (int i = 0; i < count; i++) {
sourcePixels = (argb*)((char*) dataRef + sourceArray[i] * 4);
targetPixels = (argb*)((char*) dataRef + targetArray[i] * 4);
(*targetPixels) = (*sourcePixels);
}
env->ReleaseIntArrayElements(sourceIndexes, sourceArray, JNI_ABORT);
env->ReleaseIntArrayElements(targetIndexes, targetArray, JNI_ABORT);
}
called lots of times:
JNIEXPORT void JNICALL Java_ar_com_teasoft_Image_nativeGetRGB(
JNIEnv *env, jclass clazz, jlong dataRef, jintArray indexes,
jbyteArray destRgb) {
jint *array = env->GetIntArrayElements(indexes, NULL);
jbyte *dstarray = env->GetByteArrayElements(destRgb, NULL);
int size = env->GetArrayLength(indexes);
char* sourcePixels;
int dstCount = 0;
for (int i = 0; i < size; i++) {
sourcePixels = (char*) dataRef + array[i] * 4;
dstarray[dstCount++] = (*(sourcePixels + 1));
dstarray[dstCount++] = (*(sourcePixels + 2));
dstarray[dstCount++] = (*(sourcePixels + 3));
}
env->ReleaseIntArrayElements(indexes, array, JNI_ABORT);
env->ReleaseByteArrayElements(destRgb, dstarray, JNI_COMMIT);
}
Based on the summary, it looks like the one that is not released, is of byte[], so it has to be the one in function nativeGetRGB. But i cannot find where the mistake is.
Please Help!
Regards,
Juan Ignacio
Java_ar_com_teasoft_Image_nativeGetRGB():
As far as I can see, you'd need to commit and free any temporary array copy by passing 0 instead of JNI_COMMIT to ReleaseByteArrayElements(). The second argument of Get*ArrayElements() is a pointer to a boolean, which will be set to true, if the returned array is a copy, instead of pinned memory.
Java_ar_com_teasoft_Image_nativeCopyPixels():
You might also want to pass 0 instead of JNI_ABORT, which discards everything, here:
env->ReleaseIntArrayElements(targetIndexes, targetArray, JNI_ABORT);
The tricky thing with arrays is, that the release mode applies to copied arrays only, since pinned memory gets modified directly. There's no way to force either array copy or pinning.
I use native c to read data from an audio file to jbyte pointer. Now i want to send it to java as an jbyteArray.
jbyteArray Java_com_app_audio_player_readData(JNIEnv * env, jobject jobj,jstring readPath)
{
FILE *fin;
const char *inFile= (*env)->GetStringUTFChars(env,readPath,0);
fin = fopen(inFile, "r");
fseek(fin, 0, SEEK_END); // seek to end of file
int size = ftell(fin); // get current file pointer
fseek(fin, 0, SEEK_SET);
jbyte *data=(jbyte *)malloc(size*sizeof(jbyte));
int charCnt = 0;
charCnt=fread(data, 1, size, fin);
jbyteArray result=(*env)->NewByteArray(env, size);
//-- I want to convert data to jbyteArray and return it to java
fclose(fin);
return result;
}
How it is done?
use SetByteArrayRegion
charCnt=fread(data, 1, size, fin);
jbyteArray result=(*env)->NewByteArray(env, size);
(*env)->SetByteArrayRegion(env, result, 0, size, data);
one could also use GetByteArrayElements
eg:
jboolean isCopy;
jbyte* rawjBytes = (*env)->GetByteArrayElements(env, result, &isCopy);
//do stuff to raw bytes
memcpy(rawjBytes, data, size*sizeof(jbyte));
(*env)->ReleaseByteArrayElements(env, result, rawjBytes, 0);
see here for more details on SetByteArrayRegion, GetByteArrayElements and ReleaseByteArrayElements.
NB: this question is probably a special case of this question