I'm writing an Adroid app with some C++ code behind the UI using Eclipse + NDK (r8d). I have some code that I thought was fool proof but the compiler just gives me weird errors like "Invalid arguments" without specifics. Here is what my C++ code looks like:
#include <jni.h>
#include <string>
using namespace std;
#include "../../Evaluator.Engine/Evaluator.Engine.h"
Evaluator evaluator;
extern "C" {
JNIEXPORT jstring JNICALL Java_haskellevaluator_android_MainActivity_evaluateNative(JNIEnv *env, jobject, jstring jInput)
{
...
string sInput(L"Hello world");
string sResult = evaluator.evaluate(sInput);
jstring jResult = env->NewStringUTF(sResult.data());
return jResult;
}
}
Evaluator.Engine.h is nothing fancy, but just a declaration of the class Evaluator.
#include <string>
using namespace std;
class Evaluator
{
public:
string evaluate(string input);
};
However, the compiler complains:
Invalid arguments '
Candidates are:
? evaluate(?)
'
as if string is not defined. But if I put a copy of the header file under the same folder, the error goes away. This is a Windows box. I have tried using \ and escaped \\ as path separators and it didn't work.
Does this sound like a NDK (or whatever the preprocessor it uses) bug? I don't want to move the header file because it'll be shared by other projects. I also hate to keep 2 copies of the same file.
Any ideas? Thanks.
Sorry I don't have windows OS, but I've tried you code on a MacOS, but it doesn't work because of:
string sInput(L"Hello world");
Saying that wchar_t cannot be put on std::string. Is it possible to be the same problem ?
Related
I'm integrating a native c++ library (sqlite3) into android, in order to save the database file I need a path to put it somewhere, on iOS I used this code to get the documents directory:
char *home = getenv("HOME");
However this environment variable not available on Android, is there anyway to get the applications documents path from the c++ or is it necessary to always get it from some higher java call?
Thanks a lot for the help!
Edit
After looking around, I found this small snippet that I already had in my codebase:
SequelModule.initialize(
this.getReactApplicationContext().getJavaScriptContextHolder().get(),
this.getReactApplicationContext().getFilesDir().getAbsolutePath()
);
which is being passed to my cpp-adapter.cpp, how do I convert the second value (the absolute path) JNI/Java string into a C++ std::string?
#include <jni.h>
#include "react-native-sequel.h"
extern "C"
JNIEXPORT void JNICALL
Java_com_reactnativesequel_SequelModule_initialize(JNIEnv* env, jclass clazz, jlong jsiPtr) {
installSequel(*reinterpret_cast<facebook::jsi::Runtime*>(jsiPtr));
}
extern "C"
JNIEXPORT void JNICALL
Java_com_reactnativesequel_SequelModule_destruct(JNIEnv* env, jclass clazz) {
cleanUpSequel();
}
I am trying to load a file in NDK using ifstream but it fails to read it. I've double checked the path of the file and it is not wrong. The same program works in normal C++(without the JNI stuff ofcourse).
#include <jni.h>
#include <string>
#include <iostream>
#include <istream>
#include <sstream>
#include <fstream>
using namespace std;
extern "C"
{
JNIEXPORT jstring JNICALL Java_com_example_aaaaatrytest_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {
string file_path = "/home/moe/Desktop/blah.txt";
std::ifstream fim(file_path);
if(fim.is_open())
{
string pass = "File Loaded";
return env->NewStringUTF(pass.c_str());
}
else{
std::string fail = "Failed to load file";
return env->NewStringUTF(fail.c_str());
}
}
}
After removing if-else and debugging, this is what debugger displays:
SIGTRAP (signal SIGTRAP)
env = {JNIEnv * | 0x55bc7ccc00} 0x00000055bc7ccc00
{jobject | 0x7fcefb1af4} 0x0000007fcefb1af4
I have tried to use fstream instead of ifstream but same error. I've also provided external storage write and read permission in manifest.xml but it didn't help.
This problem is format independent as I've tried to put different files in the path. Why is it failing to read the file?
l have copied the file to my device and gave android path but it still fails to read. My android's path to file looks like this "/storage/emulated/0/abc/abc.txt".
Your app needs READ_EXTERNAL_STORAGE permission.
Here is a small snippet that helps to request this permission at runtime: see READ_EXTERNAL_STORAGE permission is in manifest but still doesn't work.
I'm trying to add MP3 read and write capabilities to my Android app. I'm using the lame4android app as a starting point. Encoding a file works for me, but I'm having a problem with the decode functions -- I'm getting undefined references to the decode functions.
Here are excerpts from my wrapper.c:
#include "libmp3lame/lame.h"
#include "jni.h"
lame_t lame;
jint Java_com_intonia_dandy_WavStream_initEncoder(JNIEnv *env,
jobject jobj,
jint in_num_channels,
jint in_samplerate)
{
lame = lame_init();
...
return lame_init_params(lame);
}
hip_t hip;
jint Java_com_intonia_dandy_WavStream_initDecoder(JNIEnv *env, jobject jobj)
{
hip = hip_decode_init();
return hip != 0;
}
And here are the declarations from lame.h:
lame_global_flags * CDECL lame_init(void);
typedef hip_global_flags *hip_t;
hip_t CDECL hip_decode_init(void);
I'm getting an error message:
C:/ACode/dandy/src/main/jni/./wrapper.c:62: undefined reference to `hip_decode_init`
I'm also getting undefined references to hip_decode and and hip_decode_exit. But lame_init, lame_init_params, lame_encode_buffer, and lame_encode_flush do not generate any errors. I get these errors using the command line to run ndk-build, and I get the same errors when I let Android Studio manage the compilation.
How are the lame_* functions different from the hip_decode_* functions? Should I be using the deprecated lame_decode_*?
EDIT: I'm looking at the output of the ndk-build command. The .c files are listed on the console as they are compiled. hip_decode_init is defined in jni/libmp3lame/mpglib_interface.c, but mpglib_interface is not getting compiled, even though it's listed in jni/Android.mk. Why not???
It turns out that the LAME library as distributed does not have decoding enabled. To get it working, I had to do the following:
Add #define HAVE_MPGLIB 1 to mpglib_interface.c
Copy all .c and .h files from the mpglib directory of the LAME distribution.
Edit Android.mk to include the .c files from mpglib.
EDIT: instead of modifying mpglib_interface.c to define HAVE_MPGLIB,
it's better to set compilation flags.
Working with Android Studio 2+, build.gradle should contain
android {
defaultConfig {
ndk {
moduleName "libmp3lame"
cFlags "-DSTDC_HEADERS -DHAVE_MPGLIB"
}
}
}
Or in Android.mk:
LOCAL_CFLAGS = -DSTDC_HEADERS -DHAVE_MPGLIB
I searched online and on this site and found this link which asks the same question I am about to, but the reply does not seem address the question. Plus one of the referenced links are missing. Basically the question is how do you efficiently and intelligently decide when to use a jni-compatible function to what you have in your C source file. I am familiar with what javah command does, but that command converts JAVA methods into a C header file to be used. What about the methods that are already implemented in a C source file? How can you know if you have to convert them to a JNI version of the method? I am using Android Studio and put in the following code in the .c source file that is in my jni folder of the project:
#include "com_example_sansari_usetbt_MainActivity.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <android/log.h>
#define TAG "native-log-tag"
#define LOGI(LOG_TAG, ...) __android_log_print (ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGV(LOG_TAG, ...) __android_log_print (ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGE(LOG_TAG, ...) __android_log_print (ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
/*
* Class: com_example_sansari_usetbt_MainActivity
* Method: usetbt
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_sansari_useqsee_MainActivity_Usetbt
(JNIEnv *env, jobject obj)
{
int fd;
int rc = 0;
char *rd_buf[16];
//(*env)->printf("<1>%s: entered\n", argv[0]);
printf("<1>: entered\n");
fd = open("/dev/tbt", O_RDWR);
LOGI(LOG_TAG,"This is a log test");
LOGV(LOG_TAG,"This is a log test");
LOGE(LOG_TAG,"This is a log test");
//(*env)->fd;
//return;
if ( fd == -1 ) {
perror("<1>open failed");
rc = fd;
}
printf("<1>: open: successful\n");
/* Issue a read */
rc = read(fd, rd_buf, 0);
//I need to find what fd is and then use command completion to pick a jni finction. rd-buf seems to be jstring, and 0 seems to be int
rc = (*env)->GetString
if ( rc == -1 ) {
perror("<1>read failed");
close(fd);
}
printf("<1>: read: returning %d bytes!\n",rc);
close(fd);
(*env)->NewStringUTF(env,"Hi From Usetbt version 2");
}
And ndk-build compiles the project, but I do not get the result I am looking for. That is, the normal C version of the code which I compiled with NDK opens the driver and is able to interface with it, but this does not do the same. I do not see the result of the print statement in the kernel logs that is. I am not asking for someone to convert the above code; rather show me the way to know which of the above lines need to be converted, and what is the best way to do it please. I have read a number of items about this, and I am quickly coming up to speed, but if you can describe at a high level how
this conversion is done, please advise. I do have a copy of Java Native Interface and a number of Android texts and am going through them as fast as possible.
Thanks
I believe you need to call fflush(stdout); after your printf. As per this answer.
I have a native android library (.so) I am bundling with some application. In the native code I want to verify the signer/package name of the calling application.
The reason is, currently anyone can open up the .apk file take my .so file and use it to built their own applications.
Is there a way to securely identify the calling application from Java side? This could be package name, signature or anything else that can identify the Android application in a unique way.
JNI code is coupled with Java package name, and can be called only from the same package and class. To improve security further, you can check some Java private static final field from the JNI code.
In the native code I want to verify the signer/package name of the
calling application.
how about this?
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring JNICALL
Java_com_x_y_TestActivity_stringFromJNI(JNIEnv *env, jobject thiz)
{
jclass jActivity_class = env->GetObjectClass(thiz);
jmethodID jMethod_id_pn = env->GetMethodID(jActivity_class,"getPackageName","()Ljava/lang/String;");
jstring package_name = (jstring) env->CallObjectMethod(thiz,jMethod_id_pn);
return package_name;
}