Load android native libraries (JNI) in a native c program - android

I'm gonna write a c program that loads a native android library (.so) this is my code:
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
typedef void (*target_func)(JNIEnv* env, jobject obj, int x);
int main(int argc, char ** argv) {
char *lib = "/path/to/lib.so"
void *handle = dlopen(lib, RTLD_LAZY);
if (NULL == handle) {
printf("load library error\n");
return 1;
}
void *offset_func = dlsym(handle, "Java_com_example_test_MainActivity_myFunc");
if (NULL == offset_func) {
printf("getprocaddress error\n");
return 1;
}
target_func target = (target_func)((unsigned char *)offset_func);
target(nullptr, nullptr, 10); // Here i need to pass JNIEnv pointer
return 0;
}
The only thing that i need is to pass the JNIEnv (the first parameter), Is there any way ??
My program runs on android emulator and because of that i tried to resolve JNIEnv from libart.so but i wasn't success to disassemble libart.so in IDA Pro.
I know when we launch an apk in our device, the ART (or Dalvik) creates JNIEnv for the app but i don't know how can i emulate and create JNIEnv similar to ART.

Related

How to use ffmpeg .so file in Android application

I have compiled ffmpeg code and generated .so files,
Then I put these .so files in jniLibs/armeabi/ folder.
To use it below code :
Controller.java
public class Controller {
static {
System.loadLibrary("avutil");
System.loadLibrary("swresample");
System.loadLibrary("avcodec");
System.loadLibrary("avformat");
System.loadLibrary("swscale");
System.loadLibrary("avfilter");
System.loadLibrary("avdevice");
}
public static native void runffmpegCommand(String[] argv);
public static void testFFMPEG(String[] strings) {
runffmpegCommand(new String[]);
}
}
ffmpeg_controller.c
#include <android/log.h>
#include "ffmpeg_Controller.h"
#include <stdlib.h>
#include <stdbool.h>
int main(int argc, char **argv);
JavaVM *sVm = NULL;
jint JNI_OnLoad( JavaVM* vm, void* reserved )
{
sVm = vm;
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL Java_com_test_Controller_runffmpegCommand(JNIEnv *env, jobject obj, jobjectArray args)
{
int i = 0;
int argc = 0;
char **argv = NULL;
jstring *strr = NULL;
if (args != NULL) {
argc = (*env)->GetArrayLength(env, args);
argv = (char **) malloc(sizeof(char *) * argc);
strr = (jstring *) malloc(sizeof(jstring) * argc);
for(i=0;i<argc;i++) { strr[i] = (jstring)(*env)->GetObjectArrayElement(env, args, i);
argv[i] = (char *)(*env)->GetStringUTFChars(env, strr[i], 0);
}
}
main(argc, argv);
for(i=0;i<argc;i++) {
(*env)->ReleaseStringUTFChars(env, strr[i], argv[i]);
}
free(argv);
free(strr);
}
ffmpeg_controller.h
#include <jni.h>
#ifndef _Included_com_test_Controller
#define _Included_com_test_Controller
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_com_android_com_test_Controller_runffmpegCommand(JNIEnv *, jobject, jobjectArray);
#ifdef __cplusplus
}
#endif
#endif
When I run this code it through error as below:
Logs:
2018-12-17 14:11:17.850 25598-25598/com.android.test E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.test, PID: 25598
java.lang.UnsatisfiedLinkError: dlopen failed: "/data/app/com.android.test-1/lib/arm/libavutil.so" is 64-bit instead of 32-bit
at java.lang.Runtime.loadLibrary0(Runtime.java:989)
at java.lang.System.loadLibrary(System.java:1530)
I think the problem her because you run application with targetSdkVersion 26 or more so the solution i think it's her look:
https://stackoverflow.com/a/52951886/7055487
The Exception is self-explain: "libavutil.so is 64-bit instead of 32-bit": you have copied a 64 bit library in a "jniLibs" subfolder that expects 32 bit libraries.
"jniLibs/armeabi" subfolder is for 32bit ONLY devices

Android NDK JNI_OnLoad not getting called

I am following a tutorial on NDK development using C++. The app is a basic fibonacci number printing app. I have the appropriate System.loadLibrary and the JNI_OnLoad call. I can see in the logcat that the library is getting loaded. However, post that, the system still looks for methods based on the package names. Heres the error from logcat .
No implementation found for long com.test.fib.FibLib.fibN(long) (tried Java_com_test_fib_FibLib_fibN and Java_com_test_fib_FibLib_fibN__J)
And here is the class where I have the cpp code and related stuff .
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <android/log.h>
#include <jni.h>
#define LOG_TAG "Fibonacci Stuff"
#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
namespace com_test_fib {
static long fib(long n) {
if(n==0) return 0;
if(n==1) return 1;
return fib(n-1) + fib(n-2);
}
/* JNI wrapper */
static jlong fibN(JNIEnv* env, jclass clazz, jlong n) {
return fib(n);
}
static JNINativeMethod method_table[] = {
{"fibN", "(J)J", (void *) fibN }
};
}
using namespace com_test_fib;
extern "C" jint JNI_Onload(JavaVM* vm, void* reserved) {
LOG_D ("JNI_OnLoad");
JNIEnv* env;
if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK ) {
LOG_D("Initial Get env error");
return JNI_ERR;
}
else {
jclass clazz = env->FindClass("com/test/fib/FibLib");
LOG_D ("Find the class");
if(clazz) {
LOG_D ("Class not found");
jint ret = env->RegisterNatives(clazz, method_table, sizeof(method_table)/sizeof(method_table[0]) );
env->DeleteLocalRef(clazz);
return ret ==0 ? JNI_VERSION_1_6 : JNI_ERR;
} else {
LOG_D("Some error it seems");
return JNI_ERR;
}
}
}
Here is the loadLibrary call
package com.test.fib;
import android.util.Log;
public class FibLib {
public static long fibJ(long n) {
if(n==0) {
return 0;
}
if(n==1) {
return 1;
}
return fibJ(n-1) + fibJ(n-2);
}
/* Wrapper to call the JNI code. This is called by the activity */
public static native long fibN(long n);
static {
Log.d("Sys library loading", "This should call the onLoad function");
System.loadLibrary("Fib");
}
}
I can see the above message in Logcat. After that i directly see the exceptions and no messages from the onLoad method. I am using eclipse, min version is 14 and compile version is 5.1.1. Device is nexus 7. The package names are matching com.test.fib) in the app and the namespace.
Can anyone please let me know whats wrong here. THis is driving me mad..
Thanks

Directly call function in another .so file from C++ code in Android NDK

I have a.so which defines void a() and b.so which defines void b(). They are both put in the .apk so they are available to the Android application.
Now suppose that I'm calling a() through JNI. Is it possible to call b() from a() while completely bypassing JNI?
Can I do it this way in Android (the code is only for illustration, so it might have some errors)?
void a() {
void *handle = dlopen("b.so", RTLD_LAZY);
void (*b)() = dlsym(handle, "b");
b();
}
Would I need to add the fully qualified path, or is b.so already in LD_LIBRARY_PATH for the app?
You can do it this way on Android, though take care of where the shared library has been put in Android folders. It can change from a version to another.
On api 17 for example, it remains in /data/app-lib/. You can hardwrite it, but the best is to make calls to Java (through JNI) to know where the libraries should be.
We're doing something like this in our project :
JNIEnv* env;
const char* temp;
jobject oActivity = state->activity->clazz;
jclass cActivity = env->GetObjectClass(oActivity);
// get the path to where android extracts native libraries to
jmethodID midActivityGetApplicationInfo = env->GetMethodID(cActivity, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
jobject oApplicationInfo = env->CallObjectMethod(oActivity, midActivityGetApplicationInfo);
jclass cApplicationInfo = env->GetObjectClass(oApplicationInfo);
jfieldID fidApplicationInfoNativeLibraryDir = env->GetFieldID(cApplicationInfo, "nativeLibraryDir", "Ljava/lang/String;");
jstring sNativeLibraryDir = (jstring)env->GetObjectField(oApplicationInfo, fidApplicationInfoNativeLibraryDir);
temp = env->GetStringUTFChars(sNativeLibraryDir, NULL);
strcpy(libpath, temp);
strcat(libpath, "/");
Then you push your dlopen + dlsym combo in the fight and it should work.
As mentioned here : How do I load a shared object in C++?
There are two ways of loading shared objects in C++
For either of these methods you would always need the header file for the object you want to use. The header will contain the definitions of the classes or objects you want to use in your code.
#include "blah.h"
int main()
{
ClassFromBlah a;
a.DoSomething();
}
gcc yourfile.cpp -lblah
Dynamically (In Linux):
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen ("libm.so", RTLD_LAZY);
if (!handle) {
fprintf (stderr, "%s\n", dlerror());
exit(1);
}
dlerror(); /* Clear any existing error */
cosine = dlsym(handle, "cos");
if ((error = dlerror()) != NULL) {
fprintf (stderr, "%s\n", error);
exit(1);
}
printf ("%f\n", (*cosine)(2.0));
dlclose(handle);
return 0;
}
PS : for the dynamic approach, it depends on platform : on Linux, you use dlopen, on windows, you use LoadLibrary.

JNI, C++ problems

I did an Opencv's application en windows and now I am using JNI to convert this code to Android but I am having some problems.
In concrete my native code not do nothing.
This is my Java class where I define my native methods:
package com.example.telo3;
import org.opencv.core.Mat;
public class Process {
static {
System.loadLibrary("nativo");
}
public Process(){
dir=inicializar_nativo();
}
public void Procesar(Mat framedetect, Mat framedraw){
procesar_nativo(dir,framedetect.getNativeObjAddr(),framedraw.getNativeObjAddr());
}
private long dir;
private static native long inicializar_nativo();
private static native void procesar_nativo(long thiz, long framedetect, long framedraw);
}
This is my JNI code:
#include "nativo.h"
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/video/tracking.hpp"
#include <iostream>
#include <stdio.h>
#include "FaceDetector.h"
#include "Draw.h"
#include "Almacena.h"
#include "Runnable.h"
using namespace std;
using namespace cv;
#include <android/log.h>
#define LOG_TAG "NATIVO"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
struct variables {
Almacena almacena;
Draw draw;
FaceDetector face_detector;
};
JNIEXPORT jlong JNICALL Java_com_example_telo3_Process_inicializar_1nativo(
JNIEnv *, jobject) {
long dir = (long) new variables();
return (dir);
}
JNIEXPORT void JNICALL Java_com_example_telo3_Process_procesar_1nativo(JNIEnv *,
jobject, jlong dir, jlong framedetect, jlong framedraw) {
Mat* telo =(Mat*)framedetect;
Mat* telo2= (Mat*)framedraw;
((variables*)dir)->almacena = ((variables*)dir)->face_detector.Detect(*telo);
//almacena = face_detector.Detect(frame_gray);
((variables*)dir)->draw.Dibujar(*telo2,((variables*)dir)->almacena);
//frame_capturado = draw.Dibujar(frame_capturado, almacena);
if( (((variables*)dir)->almacena.get_faces()).size() ==0){
LOGD("no detecto caras");
}
}
I think that I use the Jni correctly but the function Detect not works correctly because when I uses this if return 0.
Is framedetect 0? I made a test app based on this and it worked fine (that is, the jlong was converted to and from just fine, so that shouldn't be the issue).
Try catching the error with ndk-gdb to better understand the problem. Attaching it to the process might a problem though if it crashes straight away, in that case I like to put breakpoint on the java side before the crash, make the execution pause there using the debugger, attach ndk-gdb and continue let the java process continue.
Also I recommend using reinterpret_cast<jlong> and reinterpret_cast<variables*> instead of the C style casts, and save that cast to a separate variable to avoid casting all the time! cleaner code!

Get "CURLE_COULDNT_RESOLVE_HOST" error in Android

I have compiled static libcurl for android but continuously receiving the CurlRes code 6 i.e. CURLE_COULDNT_RESOLVE_HOST.
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Curl
LOCAL_SRC_FILES := prebuild/libcurl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ccSharedLib
LOCAL_SRC_FILES := main-jni.cpp
LOCAL_STATIC_LIBRARIES := Curl
include $(BUILD_SHARED_LIBRARY)
main-jni.cpp
extern "C" {
size_t write_data(void *ptr, size_t size, size_t count, FILE *stream)
{
size_t written;
written = fwrite(ptr, size, count, stream);
printf("data sent, size = %lu",written);
return written;
}
jint
Java_com_example_testlibcurl_MainActivity_test1( JNIEnv* env,
jobject thiz, jstring downloadDirectoryPath)
{
CURLcode res;
res = curl_global_init(CURL_GLOBAL_ALL);
jint temp = 3;
printf("Method called");
const char *nativeDownloadDirPath = env->GetStringUTFChars(downloadDirectoryPath,0);
// Test code for calling methods of libCURL
CURL *curl;
FILE *fp;
std::string s = "http://travel.paintedstork.com/blog/wp-content/uploads/2012/10/2013-calendar-images-1.jpg";
curl = curl_easy_init();
if(curl)
{
fp = fopen(nativeDownloadDirPath, "wb");
res = curl_easy_setopt(curl, CURLOPT_URL, s.c_str());
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if(fp)
fclose(fp);
}
return res;
}
}
This code is downloading the image from a web source, but every time when "curl_easy_perform" method is called it gives the error code 6. I have checked this with different URL but still unsuccessful :( ...
"android.permission.INTERNET" and "android.permission.WRITE_EXTERNAL_STORAGE" permissions already given in Manifest file.
Any pointer to solve this will be a great help.
Make sure that you pass a valid file name and writable directory path to fopen.
I was getting
Fatal signal 11 (SIGSEGV) at 0x00000010 (code=1) ...
because fopen was trying to open a directory instead of a file.
Check the logs in LogCat from Eclipse or using adb logcat for other possible errors.
I've tested your code with the following modifications and it works.
MainActivity.java:
public class MainActivity
{
private static final String TAG = MainActivity.class.getName();
static
{
try
{
System.loadLibrary("mynativelib");
}
catch (UnsatisfiedLinkError ex)
{
Log.e(TAG, "WARNING: Could not load native library: " + ex.getMessage());
}
}
public static native int DownloadFile(String downloadDirectoryPath);
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int res = DownloadFile(Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + Environment.DIRECTORY_DOWNLOADS + File.separator
+ "test.jpg");
Log.d(TAG, "Result Code: " + res);
}
}
main-jni.cpp
#include <jni.h>
#include <android/log.h>
#include <string>
#include <curl/curl.h>
#define LOG_TAG "native"
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOG_WARN(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOG_DEBUG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#ifdef __cplusplus
extern "C"
{
#endif
// [FIX for Android 4.2.x]
// "WARNING: Could not load native library:
// Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol "__exidx_end" referenced by"
// http://stackoverflow.com/a/14501998/313113
void __exidx_start()
{
}
void __exidx_end()
{
}
size_t write_data(void *ptr, size_t size, size_t count, FILE *stream)
{
size_t written;
written = fwrite(ptr, size, count, stream);
LOG_DEBUG("Writing data to file stream %u", written);
return written;
}
jint Java_com_company_awesomeapp_MainActivity_DownloadFile(JNIEnv* env, jobject thiz,
jstring downloadDirectoryPath)
{
CURLcode res;
res = curl_global_init(CURL_GLOBAL_ALL);
jint temp = 3;
LOG_DEBUG("Downloading file");
const char *nativeDownloadDirPath = env->GetStringUTFChars(downloadDirectoryPath, 0);
LOG_DEBUG(nativeDownloadDirPath);
CURL *curl;
FILE *fp;
std::string url = "http://travel.paintedstork.com/blog/wp-content/uploads/2012/10/2013-calendar-images-1.jpg";
curl = curl_easy_init();
if (curl)
{
fp = fopen(nativeDownloadDirPath, "wb");
res = curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
LOG_DEBUG("Before write function");
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
LOG_DEBUG("After write function");
LOG_DEBUG("Before write data");
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
LOG_DEBUG("After write data");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (fp) fclose(fp);
}
env->ReleaseStringUTFChars(downloadDirectoryPath, nativeDownloadDirPath);
return res;
}
/**
* The VM calls JNI_OnLoad when the native library is loaded (for example, through System.loadLibrary).
* JNI_OnLoad must return the JNI version needed by the native library.
*
* #see http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#JNI_OnLoad
*/JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
{
return -1;
}
// [*] Get jclass with env->FindClass.
// [*] Register methods with env->RegisterNatives.
//jniRegisterNativeMethods(env, "dev/android/sample/AndroidNDKSampleActivity", sMethods, NELEM(sMethods));
return JNI_VERSION_1_6;
}
/**
* The VM calls JNI_OnUnload when the class loader containing the native library is garbage collected.
* This function can be used to perform cleanup operations.
*
* Because this function is called in an unknown context (such as from a finalizer),
* the programmer should be conservative on using Java VM services, and refrain from arbitrary
* Java call-backs.
* #see http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#JNI_OnUnload
*/JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved)
{
}
#ifdef __cplusplus
}
#endif
The file will be saved at: /storage/emulated/0/Download/test.jpg
This can also happen if you compile libcurl without threaded resolver (CMake option ENABLE_THREADED_RESOLVER) while targeting Android API level less than 23.
The problem is because implementation of CMake/CurlTests.c contains this line:
#ifndef gethostbyaddr_r
(void)gethostbyaddr_r;
#endif
However, if you check the netdb.h header in Android NDK, you will see
#if __ANDROID_API__ >= 23
int gethostbyaddr_r(const void* __addr, socklen_t __length, int __type, struct hostent* __ret, char* __buf, size_t __buf_size, struct hostent** __result, int* __h_errno_ptr) __INTRODUCED_IN(23);
#endif /* __ANDROID_API__ >= 23 */
This means that this function is not available when targeting Android older than Marshmallow. This is problematic, as testing for HAVE_GETHOSTBYNAME_R succeeds, while testing for HAVE_GETHOSTBYNAME_R_6 fails (but should succeed) with following error (observable in CMakeError.log):
/path/to/curl/CMake/CurlTests.c:128:9: error: use of undeclared identifier 'gethostbyaddr_r'
(void)gethostbyaddr_r;
^
1 error generated.
This then results with no-op behaviour in function Curl_ipv4_resolve_r in file hostip4.c, as it expects that after HAVE_GETHOSTBYNAME_R is defined, that at least one of HAVE_GETHOSTBYNAME_R_5, HAVE_GETHOSTBYNAME_R_6 or HAVE_GETHOSTBYNAME_R_3. Therefore, curl doesn't even try to resolve the host.
To fix this problem, simply remove those problematic lines from CMake/CurlTest.c.
I didn't try building curl with configure script, as in official documentation, but even if you do, it clearly states that you should target Android M or above if you plan on supporting SSL via OpenSSL, but AFAIK for simple HTTP support it should work.
The problem is gone if you use threaded resolver, as it uses different system API for performing the DNS resolution (getaddrinfo in function Curl_getaddrinfo_ex in curl_addrinfo.c). getaddrinfo is more modern API, but may not be available on all platform curl targets, such as embedded systems, which may also lack thread support. Therefore, curl uses the traditional approach in DNS resolution when threads are disabled.
P.S. Sorry for resurrecting this question after more than 7 years, but after facing the exact same problem as you, this StackOverflow question was the only similar Google search result 😛

Categories

Resources