FindClass crashes in Android NDK - java-native-interface

I have a purely native Android app, where the entry point is:
void android_main(struct android_app* state)
{
}
I need to access device information such as, android.os.Build.MODEL and in my android_main, have the following:
JNIEnv *env = state->activity->env;
jclass build_class = env->FindClass("android/os/Build");
jfieldID brand_id = env->GetStaticFieldID(build_class, "MODEL", "Ljava/lang/String;");
jstring brand_obj = (jstring)env->GetStaticObjectField(build_class, brand_id);
const char *nativeString = env->GetStringUTFChars(brand_obj, 0);
Unfortunately it crashes at the second line - "FindClass". I know that env is not null because I checked for this, but otherwise I am unable to debug this and find out why it is crashing :(

Turns out I had to:
state->activity->vm->AttachCurrentThread(&env, NULL);
instead of
JNIEnv *env = state->activity->env;
This post was helpful:
http://blog.tewdew.com/post/6852907694/using-jni-from-a-native-activity

Alternatively , you can do this in native code :-
#include <sys/system_properties.h>
#define ANDROID_OS_BUILD_MODEL "ro.product.model"
char model_id[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>.
int len;
len = __system_property_get(ANDROID_OS_BUILD_MODEL, model_id);
// On return, len will equal (int)strlen(model_id).
model_id will contain the req info
Refer this for all constants

Related

How to convert logon hours of active directory user hex value to human readable format

I am creating a web application for active directory users using java servlet and native c++ for active directory code and they are connected using jni interface. Here the permitted loggon hours are stored in the active directory as octet string. I wrote a function which gets the value stored in active directory which is a hex value and sends this as a BYTE array back to java using jni. I want to convert this value back into human readable time ie Byte array to Log on permitted hours ! I cant find any solution regarding this or any other solution idea will also be helpful.
JNIEXPORT jbyteArray JNICALL Java_NativeMethods_getLogonHours
(JNIEnv* env, jobject obj, jstring user) {
jbyteArray result;
IADsUser* pUser;
CoInitialize(NULL);
LPCWSTR userPath = jstring2string(env, user);
CComBSTR path = L"LDAP://WIN-F94H2MP3UJR.Test.local/CN=";
path += userPath;
path += L",CN=Users,DC=Test,DC=local";
HRESULT hr = ADsOpenObject(path, L"Administrator", L"pass#12",
ADS_SECURE_AUTHENTICATION, // For secure authentication
IID_IADsUser,
(void**)&pUser);
if (FAILED(hr)) { return NULL; }
VARIANT var;
hr = pUser->get_LoginHours(&var);
if (SUCCEEDED(hr)) {
SAFEARRAY* d = var.parray;
CComSafeArray<BYTE> sa(d);
const LONG lb = sa.GetLowerBound();
const LONG ub = sa.GetUpperBound();
jbyteArray result;
result = env->NewByteArray(ub);
jbyte r[5000];
for (LONG i = lb; i <= ub; i++)
{
r[i] = sa.GetAt(i);
}
env->SetByteArrayRegion(result, 0, ub, r);
return result;
}
}
This the code which gets the value for active director and send the value as byte array back to java. How to use this value.

Jni C++ findclass function return null

I found a problem with jni about C calling Java code.
Environment WIN10 JDK1.8
Currently I need C++ code to call Java code. At first I wrote a demothat was successful. Code show as below:
public class Sample2 {
public String name;
public static String sayHello(String name) {
return "Hello, " + name + "!";
}
public String sayHello() {
return "Hello, " + name + "!";
}
}
Some of the C++ code is as follows:
int main(){
printf("hello world");
JavaVMOption options[3];
JNIEnv* env;
JavaVM* jvm;
JavaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;
jfieldID fid;
jobject obj;
char opt1[] = "-Djava.compiler=NONE";
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
options[0].optionString = opt1; options[0].extraInfo = NULL;
options[1].optionString = opt2; options[1].extraInfo = NULL;
options[2].optionString = opt3; options[2].extraInfo = NULL;
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = 0;
// 启动虚拟机
status = JNI_CreateJavaVM(&jvm, (void**)& env, &vm_args);
if (status != JNI_ERR){
// 先获得class对象
cls = env->FindClass("Sample2");
}
}
I used Eclipse to compile the Java code into a .class file, copy the .class file into my C++ project, the above DEMO C++ call Java function is successful, and the findclass function returns to normal.
Because I have to introduce a third-party JAR package org.eclipse.paho.client.mqttv3-1.2.0.jar in my own Java, based on the above example, I modified the Java code in DEMO, but when I want to reference the JAR package function, And then run successfully in Eclipse, when I copy the .class file to the C++ project. JNI_CreateJavaVM in the C++ code is returned successfully, but FINDCLASS always returns null, I don't know why. I have not changed the other parts code.
Some Java code:
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class Sample2 {
public String name;
static MqttAsyncClient mqttClient = null;
static String username = "xxx";
static String password = "xxx";
static String broker = "xxx";
public static void main(String[] args) throws InterruptedException {
System.out.print("hello");
}
public static void start() {
String clientId = "mqttserver" + String.valueOf(System.currentTimeMillis());
try {
mqttClient = new MqttAsyncClient(broker, clientId, new MemoryPersistence());
} catch (Exception me) {
me.printStackTrace();
}
}
When in start function is added
mqttClient = new MqttAsyncClient(broker, clientId, new MemoryPersistence()); After the code, there will be problems
Take a look here
char opt1[] = "-Djava.compiler=NONE";
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
options[0].optionString = opt1; options[0].extraInfo = NULL;
options[1].optionString = opt2; options[1].extraInfo = NULL;
options[2].optionString = opt3; options[2].extraInfo = NULL;
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
You are passing three options (array of options with three options defined) but then, you say something like this
vm_args.nOptions = 1;
which means you are passing just one option. It means your options
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
are not even read. You have to change your code to
vm_args.nOptions = 3;
Also, make sure to put on java.class.path all the JARs, folders, where classes required by your code are.

JNI: How to convert a group of data from c++ to Java

I'm trying to send some data from c++ to java using JNI.
In c++ I have:
Array[0]:
string name = "myName"
int iterations = 16
float value = 15
... etc
So I want to use JNI to return all data on Java, I'm trying this, but don't work
JNIEXPORT jobjectArray JNICALL Java_com_testing_data_MainActivity_getDATA(JNIEnv *env, jobject obj)
{
// 1º Create a temp object
jobject dataClass
{
jstring name;
jint iterations;
jfloat value;
};
jobject tempObject = env->NewObject();
// Get data in c++ format int temp object type
std::vector<dataClass > data = getDataClass(); // First error, must be a c++ class, how could i get it?
// How much memory i need?
int dataSize = data.size();
// Reserve memory in java format
jint tempValues[dataSize];
jobjectArray tempArray = env->NewObjectArray(dataSize,dataClass,0); // 2º Error, it doesn 't create the class
// Temporal store data in jarray
for (int i = 0; i < dataSize ; i++)
{
tempArray[i].name = data[i].name;
tempArray[i].iterations = data[i].iterations;
tempArray[i].value = data[i].value;
}
return tempArray; // return temp array
}
Are correct this steps to return a structure/object with data? How is possible to fix the errors?
Converting everything to JNI types is not a good idea. Generally, it is better to create a peer object, i.e. a handle pointer to the native resource - like the hWnd in the Windows GUI Programming.
You can use a string to store all data as a sequence data. Field are separated by a separator (":" for example), like this:
std::string sequenceData = "my name" + ":" + "16" + ":" + "15" + ...;
Pass this sequence to java then split it to get desired value. Use String.split() or StringTokenizer.

Calling a Oracle procedure with output variables using OCILIB

I have a procedure with the following signature:
procedure countryExists(iCountryName in varchar2, oCount out integer)
When I run it using OCILIB, I can't get the right value for oCount. If I register it as integer (using OCI_RegisterInt), I get the error:
ORA-03116: invalid buffer length passed to a conversion routine
If I register it as a string, it runs, but OCI_GetString returns a null pointer and OCI_GetInt returns 0 (instead of the expected result 1).
The test code is:
int init = OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT|OCI_ENV_CONTEXT);
OCI_Connection *cn = OCI_ConnectionCreate("mydb", "myuser", "mypass", OCI_SESSION_DEFAULT);
OCI_Statement *st = OCI_StatementCreate(cn);
int resultprepare = OCI_Prepare(st, "call mypackage.countryExists('BRAZIL', :oCount)");
//int registercount = OCI_RegisterString(st, ":oCount", 100);
int registercount= OCI_RegisterInt(st, ":oCount");
int executeresult = OCI_Execute(st);
OCI_Error *err1 = OCI_GetLastError();
const char *error1 = OCI_ErrorGetString(err1);
OCI_Resultset *resultset = OCI_GetResultset(st);
const wchar_t *valstr = OCI_GetString(resultset, 1);
int valint = OCI_GetInt(resultset, 1);
OCI_Error *err2 = OCI_GetLastError();
const char *error2 = OCI_ErrorGetString(err2);
Running the procedure using, for example, PL/SQL Developer works fine.
Is this the right way of calling procedures using OCILIB?
Also note that I'm using the mixed version of the library.
You're not coding in Java....
Here is the right way to do it :
OCI_Connection *cn;
OCI_Statement *st;
int count;
OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT|OCI_ENV_CONTEXT);
cn = OCI_ConnectionCreate("mydb", "myuser", "mypass", OCI_SESSION_DEFAULT);
st = OCI_StatementCreate(cn);
OCI_Prepare(st, "begin mypackage.countryExists('BRAZIL', :oCount); end;");
OCI_BindInt(st, ":oCount", &count);
OCI_Execute(st);
Check the OCILIB documentation and/or manual
Vincent

Trouble calling on a Java method from a native thread using JNI (C++)

I have a JNI problem which I hope someone can help me out with.
I'm trying to call on a constructor of a Java class called LUSOutputJNI from a native thread.
It keeps failing on FindClass(...) of this specific class.
Here is the code:
LOGE("1");
JNIEnv *env = NULL;
LOGE("2");
int res = -1;
res = g_vm->AttachCurrentThread(&env, NULL);
if(env == NULL)
{
LOGE("env is NULL, AttachCurrentThread failed");;
}
if(res >= 0)
LOGE("AttachCurrentThread was successful");
jclass clazz = NULL;
jmethodID cid;
jclass clazzESEngine;
jmethodID callbackid;
jobject jCoreOut;
static jfieldID fid_ActionState = NULL;
static jfieldID fid_nSpeed = NULL;
static jfieldID fid_nType = NULL;
static jfieldID fid_nInProcess = NULL;
static jfieldID fid_nX = NULL;
static jfieldID fid_nY = NULL;
LOGE("3");
static const char* const ECOClassName = "lus/android/sdk/LUSOutputJNI";
//static const char* const ECOClassName = "android/widget/TextView";
clazz = env->FindClass(ECOClassName);
if (clazz == NULL) {
LOGE("Can't find class LUSOutputJNI");
}
else
LOGE("lus/android/sdk/LUSOutputJNI was found, YEY!!");
LOGE("4");
cid = env->GetMethodID(clazz,"<init>", "()V");
LOGE("5");
jCoreOut = env->NewObject(clazz, cid);
LOGE("6");
Here is the logcat output from when it fails:
E/lusCore_JNI( 3040): 1
E/lusCore_JNI( 3040): 2
E/lusCore_JNI( 3040): AttachCurrentThread was successful
E/lusCore_JNI( 3040): 3
E/lusCore_JNI( 3040): Can't find class LUSOutputJNI
E/lusCore_JNI( 3040): 4
W/dalvikvm( 3040): JNI WARNING: JNI method called with exception raised
Observations:
I get a result from AttachCurrentThread which is 0, which means that this attachment was successful + the env pointer is no longer NULL.
I'm sure about the package name declaration of LUSOutputJNI (triple checked it...)
When I try to run FindClass(..) with a more popular class name such as android/widget/TextView , I get a positive match. It is there. Meaning the thread attachment and the env variables are ok. (Can I assume that?)
When I try to run the following code from a JNI method which has a JNI thread running it, it finds the LUSOutputJNI class without a problem.
What am I doing wrong?
Help will be much appreciated :)
Thanks for your time,
Ita
Found the answer \ work around in here. (Look for FAQ: "FindClass didn't find my class" in
JNI tips)
I basicaly saved a global ref to the needed jclass objects.
Did however had to overcome some evil JNI changes between C/JNI and C++/JNI in order for the code to compile.
This is how I got the NewGlobalRef to compile and work.
jclass localRefCls = env->FindClass(strLUSClassName);
if (localRefCls == NULL) {
LOGE("Can't find class %s",strLUSCoreClassName);
return result;
}
//cache the EyeSightCore ref as global
/* Create a global reference */
clazzLUSCore = (_jclass*)env->NewGlobalRef(localRefCls);
/* The local reference is no longer useful */
env->DeleteLocalRef(localRefCls);
/* Is the global reference created successfully? */
if (clazzLUSCore == NULL) {
LOGE("Error - clazzLUSCore is still null when it is suppose to be global");
return result; /* out of memory exception thrown */
}
I hope this helps anyone.
To get a bit more information about whats going wrong ( it will log to stderr, not LOGE, and I'm not sure how to change that) you can add some exception printing - you can change this:
//static const char* const ECOClassName = "android/widget/TextView";
clazz = env->FindClass(ECOClassName);
if (clazz == NULL) {
LOGE("Can't find class LUSOutputJNI");
}
else
LOGE("lus/android/sdk/LUSOutputJNI was found, YEY!!");
To this:
//static const char* const ECOClassName = "android/widget/TextView";
clazz = env->FindClass(ECOClassName);
if (clazz == NULL) {
LOGE("Can't find class LUSOutputJNI");
jthrowable exc = env->ExceptionOccurred();
if(exc)
{
env->ExceptionDescribe();
env->ExceptionClear();
}
}
else
LOGE("lus/android/sdk/LUSOutputJNI was found, YEY!!");