Android development: updating code to more recent NDK? - android

I have a problem with some code that has been written for Android NDK 10d. It's not my code, but I'd like to fork and recompile this project. But I run into problems with the current NDK (19c). I have seen the code snippet in other projects as well, but I couldn't find a proper way to update this section. Maybe someone here has an idea or could explain what the problem is exactly?
Code:
JNIEXPORT jint JNICALL Java_jackpal_androidterm_TermExec_createSubprocessInternal(JNIEnv *env, jclass clazz,
jstring cmd, jobjectArray args, jobjectArray envVars, jint masterFd)
{
const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0;
String8 cmd_8;
if (str) {
cmd_8.set(str, env->GetStringLength(cmd));
env->ReleaseStringCritical(cmd, str);
}
...
int ptm = create_subprocess(env, cmd_8.string(), argv, envp, masterFd);
return ptm;
}
I get the following error (referring to cmd_8.set(str, env->GetStringLength(cmd));):
process.cpp:210:19: error: cannot initialize a parameter of type 'const char16_t *' with an lvalue of type 'const jchar *' (aka 'const unsigned short *')
cmd_8.set(str, env->GetStringLength(cmd));
^~~
So I guess parameter types changed with newer versions of NDK? Do I need a type conversion somewhere? I guess there are just updated functions/calls to this in newer versions, but I couldn't find any documentation (also didn't know what to look for).
Any ideas?

The compiler in r19 is less forgiving than the one from r10. The parameter types did not change, but the recent clang requires that you add explicit cast:
cmd_8.set((const char16_t*)str, env->GetStringLength(cmd));
This said, your project may have a worse problem, as explained #Richard Critten. If it relies upon libutils and other Android private libraries, it will not work on Android Marshmallow and higher.

Related

A simple way to build with the Android NDK

I started out writing a simple C++ program that tried to get the handle of a service using
defaultServiceManager()->getService(String16('ServiceName'));
Which has now snowballed into this terrible dependency recursion. Ultimately what I need is:
libbinder for defaultServiceManager and getService
libutils for String16
Neither of these are available in the NDK. Their sources are in the SDK along with all the appropriate headers. I can get everything to behave nicely until link time:
undefined reference to `android::defaultServiceManager()'
undefined reference to `android::String16::String16(char const*)'
undefined reference to `android::String16::~String16()'
undefined reference to `android::String16::~String16()'
Is it required to build this as a part of AOSP? Perhaps through something like ndk-build? What I am building is more of a toy application that only needs access to those (and related, e.g. service->transact APIs) APIs along with Parcel.
Perhaps building AOSP once and using -L to include a search path to the generated libs.
libbinder and libutils are not part of the app API surface. https://developer.android.com/ndk/reference/group/ndk-binder is the NDK binder API.
This (dirty hack) is working fine for me:
#include <android/binder_ibinder.h>
typedef AIBinder* (*AServiceManager_getService_func)(const char* instance);
extern "C"
JNIEXPORT void JNICALL
Java_com_irsl_greedybinder_MainActivity_testService(JNIEnv *env, jclass clazz) {
void* binder_ndk = dlopen("/system/lib/libbinder_ndk.so", RTLD_LAZY);
if (binder_ndk == nullptr) {
ALOGI("Unable to load libbinder_ndk.so");
return;
}
AServiceManager_getService_func AServiceManager_getService;
AServiceManager_getService = (AServiceManager_getService_func) dlsym(binder_ndk, "AServiceManager_getService");
if(AServiceManager_getService == nullptr) {
ALOGI("Failed to look up AServiceManager_getService");
return;
}
ALOGI("AServiceManager_getService symbol found at: %p", AServiceManager_getService);
AIBinder* binder = AServiceManager_getService("activity");
if (binder == nullptr) {
ALOGI("Unable to obtain Activity Manager service");
return;
}
ALOGI("We got the binder to the Activity Manager!: %p", binder);
}
Disclaimer by Dan Albert applies: They exist for vendor and APEX users. Those domains do not carry the same guarantees that are needed for apps to use them reliably, so they are not exposed to apps.

Pass jobjectArray from JNI to Java crash on Android API 21/22 but work on API 19

I use follow code to handle JNI array in Android and JNI.
However I found return "jobjectArray" cannot complete on API 21/22 (Android 5.0) but works on API 19. (Android 4.4)
(cannot complete mean it return on JNI part but it hang and no response on Java)
Here is the pseudo code I try to implement in my Android App.
jobjectArray Java_com_test_Simplejni(JNIEnv* env, jobject thisObj)
jclass localClass = env->FindClass("java/lang/Object");
jclass objClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
args = env->NewObjectArray(len, objClass, 0);
return args;
}
The java part function as follow:
String[] Simplejni();
The error message as follow:
JNI DETECTED ERROR IN APPLICATION: attempt to return an instance of java.lang.Object[] from com.test.Simplejni
Please advise any suggestion how to investigate this issue, thank you.
Update: 20150427
I try to simpreturn empty jobjectArray which works in Android 4.4 but failed in Android 5.0 (with the same code)
My IDE is Android Studio
I found a way to solve this:
you just replace java/lang/Object with your java object class on API 21+, for example,
jclass localClass = env->FindClass("com/example/YourLocalClass");,
From the logs, we can know the object class is not the instance of yourJavaLocalObject class.

PJSip Account Registration

I am trying to add PJSip to a project I am working on. I have this method for registering my account but a 'Fatal signal 11' error occurs everytime.
Here is the method
public int setRegistration() {
int status = pjsuaConstants.PJ_FALSE;
/* Register to SIP server by creating SIP account. */
int[] accId = new int[1];
accId[0] = 1;
String uName = getUserName();
String passwd = getPassword();
String server = getSIPServer();
pjsua_acc_config acc_cfg = new pjsua_acc_config();
pjsua.acc_config_default(acc_cfg);
acc_cfg.setId(pjsua.pj_str_copy("sip:" + uName + "#" + server));
acc_cfg.setReg_uri(pjsua.pj_str_copy("sip:" + server));
acc_cfg.setCred_count(1);
acc_cfg.getCred_info().setRealm(pjsua.pj_str_copy(server));
acc_cfg.getCred_info().setScheme(pjsua.pj_str_copy("digest"));
acc_cfg.getCred_info().setUsername(pjsua.pj_str_copy(uName));
acc_cfg.getCred_info().setData_type(pjsip_cred_data_type.PJSIP_CRED_DATA_PLAIN_PASSWD.swigValue());
acc_cfg.getCred_info().setData(pjsua.pj_str_copy(passwd));
Log.d("status", "acc is adding..");
status = pjsua.acc_add(acc_cfg, pjsuaConstants.PJ_TRUE, accId);
Log.d("status", "acc is added");
if (status == pjsuaConstants.PJ_SUCCESS) {
status = pjsua.acc_set_online_status(accId[0], 1);
Log.d("acc_set_online_status returned stauts=", String.valueOf(status));
} else {
Log.d("Error status=", String.valueOf(status));
}
return status;
}
I receive the error on the status = pjsua.acc_add(acc_cfg, pjsuaConstants.PJ_TRUE, accId); line. I know that the username, server, and password are not null. I have looked at multiple questions relating to this and no use.
How can I register my account?
Thanks
*****EDIT******
After tracking down this through blogs and forums I got passed this error but received another. The reason this error occurred was because pjsua_init was never successful. It was successful because it gave me this error
11-04 10:19:20.973: E/AndroidRuntime(2961): FATAL EXCEPTION: main
11-04 10:19:20.973: E/AndroidRuntime(2961): java.lang.UnsatisfiedLinkError: Native method not found: org.pjsip.pjsua.pjsuaJNI.init:(JLorg/pjsip/pjsua/pjsua_config;JLorg/pjsip/pjsua/pjsua_logging_config;JLorg/pjsip/pjsua/pjsua_media_config;)I
11-04 10:19:20.973: E/AndroidRuntime(2961): at org.pjsip.pjsua.pjsuaJNI.init(Native Method)
11-04 10:19:20.973: E/AndroidRuntime(2961): at org.pjsip.pjsua.pjsua.init(pjsua.java:812)
I have received this warning as well
No implementation found for native Lorg/pjsip/pjsua/pjsuaJNI;.init (JLorg/pjsip/pjsua/pjsua_config;JLorg/pjsip/pjsua/pjsua_logging_config;JLorg/pjsip/pjsua/pjsua_media_config;)I
Why isn't this a native method? I am looking into the libraries I have called but other than that I don't know why this isn't working.
Any help on this matter would be great.
Thanks
PJ Code
pjsua.java
public synchronized static int init(pjsua_config ua_cfg, pjsua_logging_config log_cfg, pjsua_media_config media_cfg) {
return pjsuaJNI.init(pjsua_config.getCPtr(ua_cfg), ua_cfg, pjsua_logging_config.getCPtr(log_cfg), log_cfg, pjsua_media_config.getCPtr(media_cfg), media_cfg);
}
pjsuaJNI.java
public final static native int init(long jarg1, pjsua_config jarg1_, long jarg2, pjsua_logging_config jarg2_, long jarg3, pjsua_media_config jarg3_);
pjsua_wrap.cpp
SWIGEXPORT jint JNICALL Java_org_pjsip_pjsua_pjsuaJNI_init(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_, jlong jarg3, jobject jarg3_) {
jint jresult = 0 ;
pjsua_config *arg1 = (pjsua_config *) 0 ;
pjsua_logging_config *arg2 = (pjsua_logging_config *) 0 ;
pjsua_media_config *arg3 = (pjsua_media_config *) 0 ;
pj_status_t result;
(void)jenv;
(void)jcls;
(void)jarg1_;
(void)jarg2_;
(void)jarg3_;
arg1 = *(pjsua_config **)&jarg1;
arg2 = *(pjsua_logging_config **)&jarg2;
arg3 = *(pjsua_media_config **)&jarg3;
result = (pj_status_t)pjsua_init((pjsua_config const *)arg1,(pjsua_logging_config const *)arg2,(pjsua_media_config const *)arg3);
jresult = (jint)result;
return jresult;
}
{"init", "(JLorg/pjsip/pjsua/pjsua_config;JLorg/pjsip/pjsua/pjsua_logging_config;JLorg/pjsip/pjsua/pjsua_media_config;)I", (void*)& Java_org_pjsip_pjsua_pjsuaJNI_init},
EDIT 2
So after working on this I have gotten to a point of frustration. I am not seeing what I am doing wrong so I will put my entire process here to see if someone has a suggestion.
I start by getting the pjsip library: svn co http://svn.pjsip.org/repos/pjproject/trunk pjproject
run `./configure --prefix=/usr/local
make dep & make
sudo make install
I then get the pjjni code svn checkout svn://svn.code.sf.net/p/pjsip-jni/code/ pjsip-jni-code
I follow the Makefile instructions
After Makefile runs successfully (after some code cleanup) I have 2 .so files (libpjsua_jni.so and libpjsua_jni_x64.so)
Create jni folder with Android.mk file and .so libraries
Run ndk-build (How to load another .so file in your android project?)
Add to ADT
Close project. Change native support from Java to Android. Open project
(Convert existing project into Android project in Eclipse?)
Add that project to my TestPJ project (Android -> Library -> Add)
Call System.loadLibrary("pjsualib") -- Name of the new lib.so
Receive Error
11-22 13:55:44.784: W/dalvikvm(11464): No implementation found for native Lorg/pjsip/pjsua/pjsuaJNI;.swig_module_init:()V
11-22 13:55:48.792: W/dalvikvm(11464): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lorg/pjsip/pjsua/pjsuaJNI;
11-22 13:55:51.417: E/AndroidRuntime(11464): java.lang.UnsatisfiedLinkError: Native method not found: org.pjsip.pjsua.pjsuaJNI.swig_module_init:()V
11-22 13:55:51.417: E/AndroidRuntime(11464): at org.pjsip.pjsua.pjsuaJNI.swig_module_init(Native Method)
11-22 13:55:51.417: E/AndroidRuntime(11464): at org.pjsip.pjsua.pjsuaJNI.(pjsuaJNI.java:1450)
Any help would be great. Thanks!
An example of project which explores JNI calls from Java and from C can be found here.
The error mentioned in the question (java.lang.UnsatisfiedLinkError: Native method not found: org.pjsip.pjsua.pjsuaJNI) means one of the following problems:
- wrong native method name or/and its arguments/return value. If you have access to native code of the library than you can fix it. According to the error message and JNI considerations native method must have name Java_org_pjsip_pjsua_pjsuaJNI_init(JNIEnv *env, jobject obj, ..), where env is a pointer to JVM interface, obj is a "this" pointer and the remaining arguments can be determined from java init method of pjsuaJNI class of package org.pjsip.pjsua. Simple parameter types must be jint, jstring etc. Also return value must be correct as well. Fixing all these allows to use this method from pjsuaJNI class. Additional details can be found from Oracle Documentation to Oracle Documentation to Java 6 JNI (or Java 7 if you're using android 4.4).
- wrong java method name/signature/class name or package name. This case is almost reverse to the first one. Again, according to the mentioned error name of the method must be "init", class name pjsuaJNI and package org.pjsip.pjsua. If at least one of them is wrong the mentioned exception will happen. Signature or parameters must also be correct. In the boundaries of this error it can be considered as a parameters of the method (in addition in native JNIEnv* and jobject appears). So also must be checked and fixed if necessary.
In case of call from native code to java signature can be considered as a representation of java method with parameters as a string and can be viewed with javap -s *.class command applied to java *.class file. And in the last warning from question this signature of the method can be seen.
Also to use method pjsip library must be loaded with System.loadLibrary() in some static section of the Java class.
Unfortunately, this problem happens at runtime (it would be nice if it happened during compilation time).
It's a bit late but I'll try to help on this. I think that your problem should be related to your native method not been surrounded by extern "C"{} and name mangling in C++.
If you don't declare one native C function as extern "C", C++ build mangles it and JNI mechanism can not find native method matching provided signature. On the other hand, declaring it as external C function, the builder creates both, mangled and unmanged, versions and JNI can find the proper one.
Hope this helps.

rand() function Bionic vs glibc

I found that rand() function from bionic does't work without including stdlib.h
extern int rand(void);
static void foo()
{
int a = rand();
}
int main()
{
foo()
return 0;
}
Results for glibc:
Compilation successful
Results for bionic:
Compilation unsuccessful
error: undefined reference to 'rand'
In bionic sources we have following implementation:
static __inline__ int rand(void) {
return (int)lrand48();
}
Why it works for glibc but not for bionic
glibc's rand() is a "normal" function located in a library. I suspect you're getting a warning about the symbol not being defined from the compiler (which cannot find it), but since the function exists in a library, the link stage can still find and link to it.
bionic's rand() is a static inline in your header file, that means it's not even defined unless the header file is included. Since the compiler cannot find it without the include and it does not exist in any library that the linker can resolve it from, compilation will fail at the linking stage.
You will get the answer if you just compare bionic and glibc sources of rand function: https://github.com/android/platform_bionic/blob/master/libc/include/stdlib.h and
http://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/rand.c;hb=glibc-2.15#l26
You can see that in bionic library it inlined in header file so without .h file you can't use it.And in glibc it is separated like most of the functions. The reason is obvious I think - bionic library was developed specially for use in embedded and mobile devices with memory and speed limits, so less code and inline optimizations is useful.

Sporadically UnsatisfiedLinkError with Android NDK

I'm looking for clues why my Android app sporadically throws an UnsatisfiedlinkError exception on a specific ndk call:
I have an Android app that loads one native library through ndk.
The native library is written in C++ and uses STL (in my makefile I have set APP_STL := stlport_static). No other libraries are used or required. The native library has about 300 native methods for about 20 java classes.
All works fine, most of the time. But every now and then my users get an UnsatisfiedLinkError exception. The exception is always triggered at the same spot with the same native function call, but strangely enough it is not even the first call to a native function, not even the first call to that specific native function.
Any clue would be welcome why one specific native call could fails. Can ndk easily be corrupted? How do you debug for such a problem?
Simple test programs with the same call work just fine. Nevertheless here's some parts of the source code:
Here's the problem ndk function in Java:
public class Server {
...
static private native long longObserveError( );
...
}
Here's the header file:
extern "C" {
...
JNIEXPORT jlong JNICALL Java_be_gentgo_tetsuki_Server_longObserveError
(JNIEnv *, jclass);
...
}
And here's the implementation:
IGSProtocol *server = 0; // initialized later...
JNIEXPORT jlong JNICALL Java_be_gentgo_tetsuki_Server_longObserveError
(JNIEnv *env, jclass)
{
Messenger &mess = server->ObserveError( );
return (long)(&mess);
}
What is the name of your library file? I came across another SO post (https://stackoverflow.com/a/18024816) where the guy had a library name that was conflicting with a system library that was only present on some devices. Renaming his library fixed the problem.

Categories

Resources