I'm using the following bootstrapping code to load my native activity (jngl-test):
#include <android/native_activity.h>
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdexcept>
const std::string LIB_PATH = "/data/data/com.bixense.jngl_test/lib/";
void* load_lib(const std::string& l) {
void* handle = dlopen(l.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
throw std::runtime_error(std::string("dlopen(") + l + "): " + strerror(errno));
}
return handle;
}
void ANativeActivity_onCreate(ANativeActivity* app, void* ud, size_t udsize) {
try {
load_lib(LIB_PATH + "libogg.so");
load_lib(LIB_PATH + "libvorbis.so");
auto main = reinterpret_cast<void (*)(ANativeActivity*, void*, size_t)>(
dlsym(load_lib(LIB_PATH + "libjngl-test.so"), "ANativeActivity_onCreate")
);
if (!main) {
throw std::runtime_error("undefined symbol ANativeActivity_onCreate");
}
main(app, ud, udsize);
} catch(std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "bootstrap", e.what());
ANativeActivity_finish(app);
}
}
I get the following error message:
dlopen(/data/data/com.bixense.jngl_test/lib/libjngl-test.so): Invalid argument
This doesn't tell me at all whats going wrong. Is there a way to get more debug output? What could "Invalid argument" mean?
I fixed it:
dlerror()
gives a far better error message.
Here's the bootstrap code if someone is interested:
#include <android/native_activity.h>
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdexcept>
void* load_lib(const std::string& l) {
auto handle = dlopen(std::string("/data/data/com.bixense.jngl_test/lib/" + l).c_str(),
RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
throw std::runtime_error(std::string("dlopen(") + l + "): " + dlerror());
}
return handle;
}
void ANativeActivity_onCreate(ANativeActivity* app, void* ud, size_t udsize) {
try {
load_lib("libogg.so");
load_lib("libvorbis.so");
auto main = reinterpret_cast<void (*)(ANativeActivity*, void*, size_t)>(
dlsym(load_lib("libjngl-test.so"), "ANativeActivity_onCreate")
);
if (!main) {
throw std::runtime_error("undefined symbol ANativeActivity_onCreate");
}
main(app, ud, udsize);
} catch(std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "bootstrap", e.what());
ANativeActivity_finish(app);
}
}
You could do this..
put that lib in raw directory and load it
For raw files, you should consider creating a raw folder inside res directory and then call
getResources().openRawResource(resourceName)
from your activity.
then you can use it the way you like.
Related
I don't know if I'm wrong but I found this solution to bypass Android restriction on private shared library like OpenCL. It copies required libs from /system/vendor/lib64 to /data/data/package.name/ and then loads them by calling dlopen.
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <errno.h>
#include <unistd.h>
#define LOG_TAG "JNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
void copy_file(const char* in_path, const char* out_path) {
FILE* fin = fopen(in_path, "rb");
check(fin, "fopen()");
FILE* fout = fopen(out_path, "wb");
check(fout, "fopen()");
unsigned char buffer[255];
size_t ret;
while ((ret = fread(buffer, sizeof(*buffer), ARRAY_SIZE(buffer), fin)) == ARRAY_SIZE(buffer)) {
fwrite(buffer, sizeof(*buffer), ret, fout);
}
if (ferror(fin) != 0) {
LOGE("Error on read");
exit(1);
}
fwrite(buffer, sizeof(*buffer), ret, fout);
fclose(fin);
fclose(fout);
}
void* sdlopen(const char* path){
void *handle;
handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
LOGE("#1 %s", dlerror());
exit(1);
}
return handle;
}
void load(){
void *handle, *handle1, *handle2, *handle3, *handle4, *handle5, *handle6;
cl_int (*_clGetPlatformIDs)(cl_uint, cl_platform_id*, cl_uint*);
char *error;
copy_file("/system/vendor/lib64/libOpenCL.so", "/data/data/com.app.opencl/libOpenCL.so");
copy_file("/system/lib64/libcutils.so", "/data/data/com.app.opencl/libcutils.so");
copy_file("/system/lib64/libbase.so", "/data/data/com.app.opencl/libbase.so");
copy_file("/system/lib64/libc++.so", "/data/data/com.app.opencl/libc++.so");
copy_file("/system/lib64/libvndksupport.so", "/data/data/com.app.opencl/libvndksupport.so");
copy_file("/system/lib64/libdl_android.so", "/data/data/com.app.opencl/libdl_android.so");
copy_file("/system/lib64/ld-android.so", "/data/data/com.app.opencl/ld-android.so");
handle1 = sdlopen("/data/data/com.app.opencl/libc++.so");
handle2 = sdlopen("/data/data/com.app.opencl/ld-android.so");
handle3 = sdlopen("/data/data/com.app.opencl/libdl_android.so");
handle4 = sdlopen("/data/data/com.app.opencl/libvndksupport.so");
handle5 = sdlopen("/data/data/com.app.opencl/libbase.so");
handle6 = sdlopen("/data/data/com.app.opencl/libcutils.so");
handle = sdlopen("/data/data/com.app.opencl/libOpenCL.so");
_clGetPlatformIDs = dlsym(handle, "clGetPlatformIDs");
if ((error = dlerror()) != NULL) {
LOGE("#2 %s", error);
exit(1);
}
dlclose(handle);
dlclose(handle1);
dlclose(handle2);
dlclose(handle3);
dlclose(handle4);
dlclose(handle5);
dlclose(handle6);
}
With the code above I can dlopen libOpenCL.so but
dlsym(handle, "clGetPlatformIDs");
returns undefined symbol: JNI_OnLoad. I think it shouldn't look for JNI_OnLoad function. What is wrong?
I am trying out Parcel with native code:
#include <stdio.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IBinder.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
using namespace android;
int main()
{
int32_t i = 123, j = 456;
Parcel data;
status_t t = data.writeInt32(j);
if(t == NO_ERROR)
printf("Status: %d\n", t);
else if(t == BAD_VALUE)
printf("Bad Value\n");
int32_t jj = 0;
t = data.readInt32(&jj);
printf("t: %d\n", t);
printf("ParcelTest: %d\n", jj);
return 0;
}
To compile this code, Android's source tree is needed. Put it under external/ParcelTest. And the Android.mk is here. Run mmma external/ParcelTest to compile.
Output of the program is:
generic_x86:/ # /system/bin/ParcelTest
Status: 0
t: -61
ParcelTest: 0
The Status: 0 indicates that writing value into the Parcel works out. But reading doesn't. So Parcel is the thing that if I read data as the order I write, I would get correct result. Any idea why this code sample fails?
The correct usage is below:
#include <stdio.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IBinder.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
using namespace android;
int main()
{
int32_t i = 123, j = 456;
Parcel data;
status_t t = data.writeInt32(j);
if(t == NO_ERROR)
printf("Status: %d\n", t);
else if(t == BAD_VALUE)
printf("Bad Value\n");
int32_t jj = 0;
data.setDataPosition(0);
t = data.readInt32(&jj);
printf("t: %d\n", t);
printf("ParcelTest: %d\n", jj);
return 0;
}
The read position has to be set manually.
I'm trying to compile an application using C++11 and OpenGL ES for Android using NativeActivity. I'm using
APP_STL := gnustl_shared
and everything compiles just fine. But when running my app I get:
dlopen(libjngl-test.so): Cannot load library: soinfo_relocate(linker.cpp:976): cannot locate symbol "_ZSt11_Hash_bytesPKvjj" referenced by "libjngl-test.so"...
jngl-test ist my activity. This is how my loading code looks like:
#include <android/native_activity.h>
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdexcept>
void* load_lib(const std::string& l) {
auto handle = dlopen(std::string("/data/data/com.bixense.jngl_test/lib/" + l).c_str(),
RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
throw std::runtime_error(std::string("dlopen(") + l + "): " + dlerror());
}
__android_log_print(ANDROID_LOG_INFO, "bootstrap", "Loaded %s", l.c_str());
return handle;
}
void ANativeActivity_onCreate(ANativeActivity* app, void* ud, size_t udsize) {
try {
load_lib("libogg.so");
load_lib("libvorbis.so");
auto main = reinterpret_cast<void (*)(ANativeActivity*, void*, size_t)>(
dlsym(load_lib("libjngl-test.so"), "ANativeActivity_onCreate")
);
if (!main) {
throw std::runtime_error("undefined symbol ANativeActivity_onCreate");
}
main(app, ud, udsize);
} catch(std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "bootstrap", "%s", e.what());
ANativeActivity_finish(app);
}
}
Does anybody have an idea what I'm doing wrong? Where could _ZSt11_Hash_bytesPKvjj come from?
I'm trying to write an app that gets all the frames of a video and manipulating them, I found oout that the best way to extract frames on Android is using OpenCv lib.
I saw in the sample code that uses VideoCapture object that receives the video path and can grab frames out of it, so I wrote the following code but the capture.open() doen't really open the video file, the capture.isOpen() is always false.
Source code:
#include <jni.h>
//opencv
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/background_segm.hpp>
#include <opencv2/core/mat.hpp>
//C
#include <stdio.h>
#include <string.h>
//C++
#include <iostream>
#include <sstream>
//Android
#include <android/log.h>
//#define LOGI(TAG,INFO) __android_log_print(ANDROID_LOG_INFO,INFO,__VA_ARGS__)
using namespace cv;
extern "C" {
JNIEXPORT void JNICALL Java_com_example_nativeopencvcheck_MainActivity_processVideo(JNIEnv* env,
jobject thsObj,jstring fileName) {
const char * fileNameNative;
jboolean isCopy;
fileNameNative = env->GetStringUTFChars(fileName, &isCopy);
//create the capture object
__android_log_print(ANDROID_LOG_ERROR, "From_Native",
"trying to open file: %s", fileNameNative);
VideoCapture capture(fileNameNative);
capture.open(fileNameNative);
if (!capture.isOpened()) { //!!!!!! ALWAYS CLOSED !!!!!
__android_log_write(ANDROID_LOG_ERROR, "From_Native",
"capture isn't open. closing..");
exit( EXIT_FAILURE);
}
Mat iplimage;
capture.retrieve(iplimage,0);
if (iplimage.size > 0) {
jclass cls = env->FindClass( "com/example/opencvframesext/MainActivity");
if (cls == 0) {
return;
}
jmethodID javamethod = env->GetMethodID(cls, "getCurrentFrameFromNative", "()V");
if (javamethod == 0) {
// LOGI("From_Native","GetMethodID error");
return;
}
jobject obj; // TODO
env->CallVoidMethod(obj, javamethod);
return;
}
/*bool gotFrame = capture.read(mat);
while (gotFrame) {
mats.addref(mat);
capture.read(mat);
}*/
//delete capture object
capture.release();
}
}
yea, that wont work, unfortunately.
there's no ffmpeg backend for VideoCapture on android.
Original code that prints to stderr:
extern "C" {
/* error: output error message */
void Error(const int error, char *message, ...)
{
va_list arg;
fflush(stdout);
fflush(stderr);
if (error > 0)
fprintf(stderr, "\nError: ");
else
fprintf(stderr, "\nWarning: ");
va_start(arg, message);
vfprintf(stderr, message, arg);
va_end(arg);
fflush(stderr);
if (error > 0)
exit(error);
}
void main(){
Error(0,"Problem %s in file", "sometext");
}
}//extern "C"
I modified my code to look like that. It should print to logcat.
extern "C" {
#include <android/log.h>
#include <jni.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
/* error: output error message */
void Error(const int error, char *message, ...)
{
va_list arg;
va_start(arg, message);
if (error > 0)
__android_log_print(ANDROID_LOG_ERROR, "HTS_API", message, arg);
else
__android_log_print(ANDROID_LOG_WARN, "HTS_API", message, arg);
va_end(arg);
if (error > 0)
exit(error);
}
void main(){
Error(0,"Problem %s in file", "sometext");
}
}//extern "C"
Problem is that my code outputs: 'Problem |�;A.|�;A. in file'
Calling the logger function directly, I will get the expected output.
__android_log_print(ANDROID_LOG_WARN, "HTS_API","Problem %s in file", "sometext");
The expected output is: 'Problem sometext in file'
What am I doing wrong?
__android_log_print does not accept a va_list as a parameter. It accepts a variable parameter list.
It looks like in the newest NDK they've added
int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap);
which is what you want. Formerly you'd have to fix this problem by either re-implementing Error as a macro with variable argument list, or using vsprintf to format the error message in a buffer and then say
__android_log_print(ANDROID_LOG_WARN, "HTS_API", "%s", buf);