I have two small snippets for calculating SHA1.
One is very fast but it seems that it isn't correct and the other is very slow but correct.
I think the FileInputStream conversion to ByteArrayInputStream is the problem.
Fast version:
MessageDigest md = MessageDigest.getInstance("SHA1");
FileInputStream fis = new FileInputStream("path/to/file.exe");
ByteArrayInputStream byteArrayInputStream =
new ByteArrayInputStream(fis.toString().getBytes());
DigestInputStream dis = new DigestInputStream(byteArrayInputStream, md);
BufferedInputStream bis = new BufferedInputStream(fis);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int ch;
while ((ch = dis.read()) != -1) {
byteArrayOutputStream.write(ch);
}
byte[] newInput = byteArrayOutputStream.toByteArray();
System.out.println("in digest : " +
byteArray2Hex(dis.getMessageDigest().digest()));
byteArrayOutputStream = new ByteArrayOutputStream();
DigestOutputStream digestOutputStream =
new DigestOutputStream(byteArrayOutputStream, md);
digestOutputStream.write(newInput);
System.out.println("out digest: " +
byteArray2Hex(digestOutputStream.getMessageDigest().digest()));
System.out.println("length: " +
new String(
byteArray2Hex(digestOutputStream.getMessageDigest().digest())).length());
digestOutputStream.close();
byteArrayOutputStream.close();
dis.close();
Slow version:
MessageDigest algorithm = MessageDigest.getInstance("SHA1");
FileInputStream fis = new FileInputStream("path/to/file.exe");
BufferedInputStream bis = new BufferedInputStream(fis);
DigestInputStream dis = new DigestInputStream(bis, algorithm);
// read the file and update the hash calculation
while (dis.read() != -1);
// get the hash value as byte array
byte[] hash = algorithm.digest();
Conversion method:
private static String byteArray2Hex(byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash) {
formatter.format("%02x", b);
}
return formatter.toString();
}
I hope there is another possibility to get it running because I need the performance.
I used a high performance c++ implementation which I load with JNI.
For more details write a comment, please.
EDIT:
Requirements for JNI is the Android NDK. For Windows is needed in addition cygwin or something similar.
If you decided for cygwin, I give you some little instructions how to get it working with the NDK:
Download the setup.exe from cygwin and execute it.
Click on Next and choice Install from Internet confirm with Next.
The next two steps adjust the settings as desired and as always click Next.
Select your internet connection and the same procedure as in the final stages.
A download page will catch the eye select it or take just a download page, which is in your country. There is nothing more to say.
We need the packages make and gcc-g++. You can find them using the search in the left upper corner, click on the Skip til a version is displayed and the first field is selected. Do that what we have always done after a selection.
You will get the information, that there are dependencies, which must be resolved. It is usually not necessary to do it yourself and confirm it.
The download and installation started.
If you need you can create shortcuts otherwise click on exceptional Finish.
Download the zip file and extract the NDK to a non space containing path.
You can start now cygwin.
Navigate to the NDK. The path /cydrive gives you all available drives f.e. cd /cygdrive/d navigates to the drive with the letter D.
In the root folder of the NDK you can execute the file ndk-build with ./ndk-build. There should be an error occurs like Android NDK: Could not find application project directory !.
You have to navigate in an Android project to execute the command. So let's start with a project.
Before we can start with the project search for a C/C++ implementation of the hash algorithm. I took the code from this site CSHA1.
You should edit the source code for your requirements.
Now we can start with JNI.
You create a folder called jni in your Android project. It contains all native source files and the Android.mk (more about that file later), too.
Copy your downloaded (and edited) source files in that folder.
My java package is called de.dhbw.file.sha1, so I named my source files similar to find them easily.
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
# How the lib is called?
LOCAL_MODULE := SHA1Calc
# Which is your main SOURCE(!) file?
LOCAL_SRC_FILES := de_dhbw_file_sha1_SHA1Calc.cpp
include $(BUILD_SHARED_LIBRARY)
Java code:
I used the AsyncTask with a ProgressDialog to give the user some feedback about the action.
package de.dhbw.file.sha1;
// TODO: Add imports
public class SHA1HashFileAsyncTask extends AsyncTask<String, Integer, String> {
// [...]
static {
// loads a native library
System.loadLibrary("SHA1Calc");
}
// [...]
// native is the indicator for native written methods
protected native void calcFileSha1(String filePath);
protected native int getProgress();
protected native void unlockMutex();
protected native String getHash();
// [...]
}
Native code (C++):
Remember accessing variables inside native code or other way around using threads needs synchronizing or you will get a segmentation fault soon!
For JNI usage you have to add #include <jni.h>.
For logging insert following include #include <android/log.h>.
Now you can log with __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "Version [%s]", "19");.
The first argument is the type of message and the second the causing library.
You can see I had a version number in my code. It is very helpful because sometimes the apk builder doesn't use the new native libraries. Troubleshooting can be extremely shortened, if the wrong version is online.
The naming conventions in the native code are a little bit crasier: Java_[package name]_[class name]_[method name].
The first to arguments are always given, but depending on the application you should distinguish:
func(JNIEnv * env, jobject jobj) -> JNI call is an instance method
func(JNIEnv * env, jclass jclazz) -> JNI call is a static method
The header for the method calcFileSha1(...):
JNIEXPORT void JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_calcFileSha1(JNIEnv * env, jobject jobj, jstring file)
The JDK delivers the binary javah.exe, which generates the header file for the native code. The usage is very simple, simply call it with the full qualified class:
javah de.dhbw.file.sha1.SHA1HashFileAsyncTask
In my case I have to give the bootclasspath additionally, because I use Android classes:
javah -bootclasspath <path_to_the_used_android_api> de.dhbw.file.sha1.SHA1HashFileAsyncTask
That would be the generated file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class de_dhbw_file_sha1_SHA1HashFileAsyncTask */
#ifndef _Included_de_dhbw_file_sha1_SHA1HashFileAsyncTask
#define _Included_de_dhbw_file_sha1_SHA1HashFileAsyncTask
#ifdef __cplusplus
extern "C" {
#endif
#undef de_dhbw_file_sha1_SHA1HashFileAsyncTask_ERROR_CODE
#define de_dhbw_file_sha1_SHA1HashFileAsyncTask_ERROR_CODE -1L
#undef de_dhbw_file_sha1_SHA1HashFileAsyncTask_PROGRESS_CODE
#define de_dhbw_file_sha1_SHA1HashFileAsyncTask_PROGRESS_CODE 1L
/*
* Class: de_dhbw_file_sha1_SHA1HashFileAsyncTask
* Method: calcFileSha1
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_calcFileSha1
(JNIEnv *, jobject, jstring);
/*
* Class: de_dhbw_file_sha1_SHA1HashFileAsyncTask
* Method: getProgress
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_getProgress
(JNIEnv *, jobject);
/*
* Class: de_dhbw_file_sha1_SHA1HashFileAsyncTask
* Method: unlockMutex
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_unlockMutex
(JNIEnv *, jobject);
/*
* Class: de_dhbw_file_sha1_SHA1HashFileAsyncTask
* Method: getHash
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_getHash
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
You can change the file without further notice. But do not use javah again!
Class and methods
To get a class instance you can use jclass clz = callEnv->FindClass(CALL_CLASS);. In this case is CALL_CLASS the full qualified path to the class de/dhbw/file/sha1/SHA1HashFileAsyncTask.
To find a method you need the JNIEnv and an instance of the class:
jmethodID midSet = callEnv->GetMethodID(callClass, "setFileSize", "(J)V");
The first argument is the instance of the class, the second the name of the method and the third is the signature of the method.
The signature you can get with the from JDK given binary javap.exe. Simply call it with the full qualified path of the class f.e. javap -s de.dhbw.file.sha1.SHA1HashFileAsyncTask.
You will get an result like:
Compiled from "SHA1HashFileAsyncTask.java"
public class de.dhbw.file.sha1.SHA1HashFileAsyncTask extends android.os.AsyncTas
k<java.lang.String, java.lang.Integer, java.lang.String> {
[...]
static {};
Signature: ()V
public de.dhbw.file.sha1.SHA1HashFileAsyncTask(android.content.Context, de.dhb
w.file.sha1.SHA1HashFileAsyncTask$SHA1AsyncTaskListener);
Signature: (Landroid/content/Context;Lde/dhbw/file/sha1/SHA1HashFileAsyncTas
k$SHA1AsyncTaskListener;)V
protected native void calcFileSha1(java.lang.String);
Signature: (Ljava/lang/String;)V
protected native int getProgress();
Signature: ()I
protected native void unlockMutex();
Signature: ()V
protected native java.lang.String getHash();
Signature: ()Ljava/lang/String;
[...]
public void setFileSize(long);
Signature: (J)V
[...]
}
If the method is found the variable is not equal 0.
Calling the method is very easy:
callEnv->CallVoidMethod(callObj, midSet, size);
The first argument is the given jobject from the "main" method and I think the others are clear.
Remember that you can call from native code although private methods of the class, because the native code is part of it!
Strings
The given string would be converted with following code:
jboolean jbol;
const char *fileName = env->GetStringUTFChars(file, &jbol);
And the other way:
TCHAR* szReport = new TCHAR;
jstring result = callEnv->NewStringUTF(szReport);
It can be every char* variable.
Exceptions
Can be thrown with the JNIEnv:
callEnv->ThrowNew(callEnv->FindClass("java/lang/Exception"),
"Hash generation failed");
You can also check if there is an exception occurred also with JNIEnv:
if (callEnv->ExceptionOccurred()) {
callEnv->ExceptionDescribe();
callEnv->ExceptionClear();
}
Specifications
Java Native Interface Specifications
Build/Clean
Build
After we have created all files and filled them with content, we can build it.
Open cygwin, navigate to the project root and execute from there the ndk-build, which is in the NDK root.
This start the compile, if it is success you will get an output like that:
$ /cygdrive/d/android-ndk-r5c/ndk-build
Compile++ thumb : SHA1Calc <= SHA1Calc.cpp
SharedLibrary : libSHA1Calc.so
Install : libSHA1Calc.so => libs/armeabi/libSHA1Calc.so
If there is any error, you will get the typical output from the compiler.
Clean
Open cygwin, switch in your Android project and execute the command /cygdrive/d/android-ndk-r5c/ndk-build clean.
Build apk
After you have build the native libraries you can build your project. I've found clean, it is advantageous to use the eclipse feature clean project.
Debugging
Debugging of java code isn't different as before.
The debugging of c++ code will follow in the next time.
Do this:
MessageDigest md = MessageDigest.getInstance("SHA1");
InputStream in = new FileInputStream("hereyourinputfilename");
byte[] buf = new byte[8192];
for (;;) {
int len = in.read(buf);
if (len < 0)
break;
md.update(buf, 0, len);
}
in.close();
byte[] hash = md.digest();
Performance comes from handling data by blocks. An 8 kB buffer, as here, ought to be blocky enough. You do not have to use a BufferedInputStream since the 8 kB buffer also serves as I/O buffer.
The reason the fast one is fast and incorrect is (I think) that it is not hashing the file contents!
FileInputStream fis = new FileInputStream("C:/Users/Ich/Downloads/srware_iron.exe");
ByteArrayInputStream byteArrayInputStream =
new ByteArrayInputStream(fis.toString().getBytes());
The fis.toString() call does not read the contents of the file. Rather it gives you a string that (I suspect) looks something like this:
"java.io.FileInputStream#xxxxxxxx"
which you are then proceeding to calculate the SHA1 hash for. FileInputStream and its superclasses do not override Object::toString ...
The simple way to read the entire contents of an InputStream to a byte[] is to use an Apache Commons I/O helper method - IOUtils.toByteArray(InputStream).
public void computeSHAHash(String path)// path to your file
{
String SHAHash = null;
try
{
MessageDigest md = MessageDigest.getInstance("SHA1");
InputStream in = new FileInputStream(path);
byte[] buf = new byte[8192];
int len = -1;
while((len = in.read(buf)) > 0)
{
md.update(buf, 0, len);
}
in.close();
byte[] data = md.digest();
try
{
SHAHash = convertToHex(data);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Toast.makeToast(getApplicationContext(),"Generated Hash ="+SHAHash,Toast.LENGTH_SHORT).show();
}
private static String convertToHex(byte[] data) throws java.io.IOException
{
StringBuffer sb = new StringBuffer();
String hex = null;
hex = Base64.encodeToString(data, 0, data.length, NO_OPTIONS);
sb.append(hex);
return sb.toString();
}
Related
I am working on a research project which includes Hotspot profiler's feedback. Currently I am working on a JVMTI agent which should have following features:
listen any compiled load event.
Extract and analyse the complete class file which has hotspot method.
Modify/Redefine the bytecodes of the class.
I have a lot of API functions available in JVMTI to get the information about the class file having the method which is being compiled by JIT. However, I want the complete class file of the method as described in java virtual machine specification.
If it is not possible to get a whole class file, I would at least want a class file in following format:
typedef struct {
unsigned int magic;
unsigned short minor_version;
unsigned short major_version;
unsigned short constant_pool_count;
unsigned char *constant_pool;
unsigned short access_flags;
unsigned short this_class;
unsigned short super_class;
unsigned short interfaces_count;
unsigned char *interfaces;
unsigned short fields_count;
unsigned char *fields;
unsigned short methods_count;
unsigned char *methods;
unsigned short attributes_count;
unsigned char *attributes;
}ClassFile;
I have following code so far which serves the purpose partially:
void JNICALL compiled_method_load(jvmtiEnv *jvmti, jmethodID method, jint code_size, const void* code_addr, jint map_length, const jvmtiAddrLocationMap* map, const void* compile_info)
{
static ClassFile *clazz;
jvmtiError err;
jclass klass;
jint constant_pool_count_pointer;
jint constant_pool_byte_count_pointer;
jint local_entry_count_ptr;
jint minor, major;
jint modifier_ptr;
jvmtiLocalVariableEntry* table_ptr;
unsigned char* constant_pool_bytes_ptr;
char* name = NULL;
char* signature = NULL;
char* generic_ptr = NULL;
unsigned char* bytecodes_ptr = NULL;
err = (*jvmti)->RawMonitorEnter(jvmti,lock);
check_jvmti_error(jvmti, err, "raw monitor enter");
clazz->magic = 0xCAFEBABE;
err = (*jvmti)->GetMethodDeclaringClass(jvmti,method, &klass);
check_jvmti_error(jvmti, err, "Get Declaring Class");
err = (*jvmti)->GetClassVersionNumbers(jvmti, klass, &minor, &major);
check_jvmti_error(jvmti, err, "Get Class Version Number");
clazz->minor_version = (u2_int)minor;
clazz->major_version = (u2_int)major;
err = (*jvmti)->GetConstantPool(jvmti, klass, &constant_pool_count_pointer,
&constant_pool_byte_count_pointer, &constant_pool_bytes_ptr);
check_jvmti_error(jvmti, err, "Get Constant Pool");
clazz->constant_pool_count = constant_pool_count_pointer;
clazz->constant_pool = constant_pool_bytes_ptr;
err = (*jvmti)->GetClassModifiers(jvmti,klass, &modifier_ptr);
check_jvmti_error(jvmti, err, "Get Access Flags");
clazz->access_flags = (u2_int)modifier_ptr;
err = (*jvmti)->GetBytecodes(jvmti,method, &code_size, &bytecodes_ptr);
check_jvmti_error(jvmti, err, "Get Bytecodes");
err = (*jvmti)->GetLocalVariableTable(jvmti,method, &local_entry_count_ptr, &table_ptr);
check_jvmti_error(jvmti, err, "Get Local Variable table");
if (constant_pool_bytes_ptr != NULL) {
err = (*jvmti)->Deallocate(jvmti,(unsigned char*)constant_pool_bytes_ptr);
check_jvmti_error(jvmti, err, "deallocate bytecodes pointer");
}
if (bytecodes_ptr != NULL) {
err = (*jvmti)->Deallocate(jvmti,(unsigned char*)bytecodes_ptr);
check_jvmti_error(jvmti, err, "deallocate bytecodes pointer");
}
if (name != NULL) {
err = (*jvmti)->Deallocate(jvmti,(unsigned char*)name);
check_jvmti_error(jvmti, err, "deallocate name");
}
if (signature != NULL) {
err = (*jvmti)->Deallocate(jvmti,(unsigned char*)signature);
check_jvmti_error(jvmti, err, "deallocate signature");
}
if (generic_ptr != NULL) {
err = (*jvmti)->Deallocate(jvmti,(unsigned char*)generic_ptr);
check_jvmti_error(jvmti, err, "deallocate generic_ptr");
}
err = (*jvmti)->RawMonitorExit(jvmti,lock);
}
My questions are:
Is it possible to get complete class file through an agent?
If not, how can I fill the ClassFile structure using JVMTI or JNI API?
Any other strategy to achieve my objective?
Limitations:
I have to examine and manipulate bytecodes during runtime a long time after class loading. So AFAIK java agents using libraries like ASM and JAVASSIST wouldn't be of any help.
Any help would be highly appreciated.
So I finally got it working. The ideas from Holger in the comments are the main sources of this answer. I don't think anybody would need this, but just to answer it, here goes.
Simply put, to get the whole class file, there is only one possibility in JVMTI APIs and that is ClassFileLoadHook event. This event is triggered whenever a new class is being loaded in JVM or when Retransformclasses or RedefineClasses functions are called. So I called Retransformclasses function as a dummy call just to invoke ClassFileLoadHookEvent and then finally got the whole class.
Added following function in my source code mainly besides adding capabilities and callback setups:
void JNICALL
Class_File_Load_Hook(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jclass class_being_redefined,
jobject loader,
const char* name,
jobject protection_domain,
jint class_data_len,
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data)
{
jvmtiError err;
unsigned char* jvmti_space = NULL;
char* args = "vop";
javab_main(3, args, class_data, class_data_len);
err = (*jvmti_env)->Allocate(jvmti_env, (jlong)global_pos, &jvmti_space);
check_jvmti_error(jvmti_env, err, "Allocate new class Buffer.");
(void)memcpy((void*)jvmti_space, (void*)new_class_ptr, (int)global_pos);
*new_class_data_len = (jint)global_pos;
*new_class_data = jvmti_space;
}
The class_data variable here contains the complete class file on which the ClassFileLoadHook event was invoked. I analyzed this class file and instrumented it in a new char* array using javab_main method and finally pointed the new array towards new_class_data variable. The new_class_ptr is a global variable which contains the changed definition of the class.
I have removed the metadata from an image using the below imagemagick command.
convert input.png -strip output.png
It almost reduces 20% of the size for the 2MB file.
I need to do the same using Jmagick java api.
Is there any api available in Jmagick to remove the metadata?
I can't read Java, but there seems to be a strip method in src/magick/magick_MagickImage.c:
/*
* Class: magick_MagickImage
* Method: strip
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_magick_MagickImage_strip
(JNIEnv *env, jobject self) {
Image *image = NULL;
jboolean retVal;
image = (Image*) getHandle(env, self, "magickImageHandle", NULL);
if (image == NULL) {
throwMagickException(env, "Unable to retrieve image handle");
return JNI_FALSE;
}
retVal = StripImage(image);
return(retVal);
}
It depends on the version you are using!
JMagick up to version 6.7.7:
MagickImage has no method strip()
Current (Master) JMagick version:
MagickImage has a method named strip()
I am trying to call some java code from a JNI C++ function and I get an exception: java.lang.NoSuchMethodError
The C++ code where the java code is called is a callback function called from another native library.
This code was working perfectly fine when I used the setup described in
here:
Basically an APK that was using a JNI and that JNI was making calls to a native libray compiled elsewhere).
But then I wanted to compile all my code in the AOSP, so I put the code of my native library, the JNI and the APK code in vendor/MyCode/MyApp. It compiles fine, native methods called from the java work OK but java code being called from the JNI now crashes all.
Here is the java code of the method I want to call from the JNI:
package com.android.mycode.myapp;
import android.app.Activity;
public class MainActivity extends Activity implements OnClickListener
{
private native int powerOn();
...
public void ndefRead(int tech, int protocol, byte[] ndef)
{
Log.d(tag, "ndefRead()");
Message msg = mHandler.obtainMessage();
msg.what = MSG_NDEF_READ_RECEIVED;
msg.obj = ndef;
msg.arg1 = tech;
msg.arg2 = protocol;
mHandler.sendMessage(msg);
}
...
}
My JNI code where I register the java data:
JNIEXPORT jint JNICALL
Java_com_android_mycode_myapp_MainActivity_powerOn(JNIEnv * env, jobject obj)
{
LOGD("calling powerOn()"); //Or ANDROID_LOG_INFO, ...
env->GetJavaVM(&javaVM);
jclass cls = env->GetObjectClass(obj);
activityClass = (jclass) env->NewGlobalRef(cls);
activityObj = env->NewGlobalRef(obj);
...
return status;
}
The JNI code of the callback function where the java code is called:
void EventCallback(UINT8 event, tEVT_CBACK_DATA* eventData)
{
LOGD("EventCallback() - event: 0x%x", event);
switch (event)
{
case NDEF_READ_EVT:
{
LOGD("EventCallback() - NDEF_READ_EVT - data length: 0x%x", eventData->ndefReadEvt.length);
JNIEnv *env;
javaVM->AttachCurrentThread(&env, NULL);
jmethodID ndefReadID = env->GetMethodID(activityClass, "ndefRead", "(II[B)V");
if (ndefReadID == 0)
{
LOGD("Function ndefRead() not found");
return;
}
jbyteArray result = env->NewByteArray(eventData->ndefReadEvt.length);
if (result != NULL)
{
env->SetByteArrayRegion(result, 0, eventData->ndefReadEvt.length, (jbyte *) eventData->ndefReadEvt.p_ndef);
}
env->CallVoidMethod(activityObj, ndefReadID, eventData->ndefReadEvt.tech, eventData->ndefReadEvt.protocol, result);
javaVM->DetachCurrentThread();
}
break;
}
The makefile of the JNI looks like this:
LOCAL_MODULE := libinterface
LOCAL_SRC_FILES := interface.cpp
LOCAL_LDLIBS := -llog
LOCAL_SHARED_LIBRARIES := libnfc-nci
And when executing this code I get the following exception/error message:
Pending exception java.lang.NoSuchMethodError thrown by 'unknown throw location'
java.lang.NoSuchMethodError: no non-static method "Lcom/android/mycode/myapp/MainActivity;.ndefRead(II[B)V"
I have looked at several other problems of this type reported on this site but could not quite found something that is similar to mine. If someone has an idea it would be greatly appreciated.
Edited to add code of ndefRead() function and makefile of JNI.
These errors are usually caused by the make file or wrong file naming.
I didn't see any static { System.loadLibrary("your-c-module"); }, as defined in android MK, e.g:
#android.mk
include $(CLEAR_VARS)
LOCAL_MODULE := your-c-module
LOCAL_SRC_FILES := main.cpp
LOCAL_STATIC_LIBRARIES += fancy-library
LOCAL_LDLIBS += -llog -ldl
Also, I don't know if you filtered it out or not, but if Java_com_android_st_nfcnintendothread_MainActivity_powerOn is your c function, then your package name should be: com.android.st.nfcnintendothread and it's function: MainActivity.powerOn.
I am just spit-balling here, hopefully you'll find something useful in the above.
I'm running a basic Java wrapper for a C++ BSD socket client. I can compile the Java and generate a header file, but when I try to run it, it returns Exception in thread "main" java.lang.UnsatisfiedLinkError: JavaClient.socketComm()V
From what I've been able to find, it seems like this is indicative of a mismatch between method signatures, but I can't find anything wrong with mine.
Java Code
public class JavaClient
{
public native void socketComm();
public static void main(String[] args)
{
System.load("/home/cougar/workspace/ArbiterBSDSocketComms/JNIClient/JavaClient.so");
JavaClient client = new JavaClient();
client.socketComm();
System.out.println("Done");
}
}
C Implementation
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <cstdlib>
#include <stdio.h>
#include <jni.h>
#include "JavaClient.h"
#define MAXHOSTNAME 256
JNIEXPORT void JNICALL Java_JavaClient_socketComm
(JNIEnv *env, jobject obj) {
struct sockaddr_in remoteSocketInfo;
struct hostent *hPtr;
int socketHandle;
char *remoteHost="localhost";
int portNumber = 8080;
memset(&remoteSocketInfo, 0, sizeof(struct sockaddr_in)); //Clear structure memory
if ((hPtr = gethostbyname(remoteHost)) == NULL) //Get sysinfo
{
printf("System DNS resolution misconfigured.");
printf("Error number: ", ECONNREFUSED);
exit(EXIT_FAILURE);
}
if((socketHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0) //Create socket
{
close(socketHandle);
exit(EXIT_FAILURE);
}
memcpy((char *)&remoteSocketInfo.sin_addr,
hPtr->h_addr, hPtr->h_length); //Load sys info into sock data structures
remoteSocketInfo.sin_family = AF_INET;
remoteSocketInfo.sin_port = htons((u_short)portNumber); //Set port number
if(connect(socketHandle, (struct sockaddr *)&remoteSocketInfo, sizeof(struct sockaddr_in)) < 0)
{
close(socketHandle);
exit(EXIT_FAILURE);
}
int rc=0;
char buf[512];
strcpy(buf, "Sup server");
send(socketHandle, buf, strlen(buf)+1, 0);
}
void main(){}
Header File
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JavaClient */
#ifndef _Included_JavaClient
#define _Included_JavaClient
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: JavaClient
* Method: socketComm
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JavaClient_socketComm
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Excuse some of the bad formatting, I haven't used stackoverflow much and the code formatting is a bit sketchy.
These files all reside in /JNIClient.
I'm running Ubuntu 12.04 x64, and I have both 32- and 64-bit JDKs installed. I tried generating the .so with the 32-bit verison first, which would have been ideal, but I got an ELF mismatch, so I just went with the 64-bit so I wouldn't have to deal with that. (Any insight on that is welcome as well.)
My process is:
$>javac JavaClient.java
$>javah JavaClient
$>cc -m64 -g -I/usr/lib/jvm/java-6-openjdk-amd64/include -I/usr/lib/jvm/java-6-openjdk-amd64/include/linux -shared JavaClient.c -o JavaClient.so
$>java JavaClient
The full error message is
Exception in thread "main" java.lang.UnsatisfiedLinkError: JavaClient.socketComm()V
at JavaClient.socketComm(Native Method)
at JavaClient.main(JavaClient.java:9)
$>nm JavaClient.so returns:
cougar#Wanda:~/workspace/ArbiterBSDSocketComms/JNIClient$ nm JavaClient.so
0000000000200e50 a _DYNAMIC
0000000000200fe8 a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
0000000000200e30 d __CTOR_END__
0000000000200e28 d __CTOR_LIST__
0000000000200e40 d __DTOR_END__
0000000000200e38 d __DTOR_LIST__
00000000000005e0 r __FRAME_END__
0000000000200e48 d __JCR_END__
0000000000200e48 d __JCR_LIST__
0000000000201010 A __bss_start
w __cxa_finalize##GLIBC_2.2.5
0000000000000540 t __do_global_ctors_aux
0000000000000490 t __do_global_dtors_aux
0000000000201008 d __dso_handle
w __gmon_start__
0000000000201010 A _edata
0000000000201020 A _end
0000000000000578 T _fini
0000000000000438 T _init
0000000000000470 t call_gmon_start
0000000000201010 b completed.6531
0000000000201018 b dtor_idx.6533
0000000000000510 t frame_dummy
Edit: I have a theory that the .so is being built improperly, as $>nm JavaClient.so doesn't show the method names in it. Any suggestions on what's wrong about that cc command?
Okay, SO: I kept at this because nothing seemed right. The method signatures were all matched, nothing should be wrong, eclipse file properties said it was editing the right file, etc etc. I finally catted the JavaClient.c, and it was blank. Apparently eclipse wasn't ACTUALLY editing the file it said it was. Fixed that up and now everything's fine.
I will give you a far better solution.
public void socketComm() throws IOException
{
Socket socket = new Socket("localhost", 8080);
try
{
socket.getOutputStream().write("Sup server\u0000".getBytes());
}
finally
{
socket.close(); // You forgot this
}
}
No JNI required at all. It also doesn't close an invalid handle if socket() returns < 0, unlike your code.
I kept at this because nothing seemed right. The method signatures were all matched, nothing should be wrong, eclipse file properties said it was editing the right file, etc etc. I finally catted the JavaClient.c, and it was blank. Apparently eclipse wasn't ACTUALLY editing the file it said it was. Fixed that up and now everything's fine.
Not sure why Eclipse claimed to be editing a blank file, I checked and rechecked and it wasn't referencing any links or anything, but if you're having the same problem as me check it out.
I want to load my own native libraries in my java application. Those native libraries depend upon third-party libraries (which may or may not be present when my application is installed on the client computer).
Inside my java application, I ask the user to specify the location of dependent libs. Once I have this information, I am using it to update the "LD_LIBRARY_PATH" environment variable using JNI code. The following is the code snippet that I am using to change the "LD_LIBRARY_PATH" environment variable.
Java code
public static final int setEnv(String key, String value) {
if (key == null) {
throw new NullPointerException("key cannot be null");
}
if (value == null) {
throw new NullPointerException("value cannot be null");
}
return nativeSetEnv(key, value);
}
public static final native int nativeSetEnv(String key, String value);
Jni code (C)
JNIEXPORT jint JNICALL Java_Test_nativeSetEnv(JNIEnv *env, jclass cls, jstring key, jstring value) {
const char *nativeKey = NULL;
const char *nativeValue = NULL;
nativeKey = (*env)->GetStringUTFChars(env, key, NULL);
nativeValue = (*env)->GetStringUTFChars(env, value, NULL);
int result = setenv(nativeKey, nativeValue, 1);
return (jint) result;
}
I also have corresponding native methods to fetch the environment variable.
I can successfully update the LD_LIBRARY_PATH (this assertion is based on the output of C routine getenv().
I am still not able to load my native library. The dependent third-party libraries are still not detected.
Any help/pointers are appreciated. I am using Linux 64 bit.
Edit:
I wrote a SSCE (in C) to test if dynamic loader is working. Here is the SSCE
#include
#include
#include
#include
int main(int argc, const char* const argv[]) {
const char* const dependentLibPath = "...:";
const char* const sharedLibrary = "...";
char *newLibPath = NULL;
char *originalLibPath = NULL;
int l1, l2, result;
void* handle = NULL;
originalLibPath = getenv("LD_LIBRARY_PATH");
fprintf(stdout,"\nOriginal library path =%s\n",originalLibPath);
l1 = strlen(originalLibPath);
l2 = strlen(dependentLibPath);
newLibPath = (char *)malloc((l1+l2)*sizeof(char));
strcpy(newLibPath,dependentLibPath);
strcat(newLibPath,originalLibPath);
fprintf(stdout,"\nNew library path =%s\n",newLibPath);
result = setenv("LD_LIBRARY_PATH", newLibPath, 1);
if(result!=0) {
fprintf(stderr,"\nEnvironment could not be updated\n");
exit(1);
}
newLibPath = getenv("LD_LIBRARY_PATH");
fprintf(stdout,"\nNew library path from the env =%s\n",newLibPath);
handle = dlopen(sharedLibrary, RTLD_NOW);
if(handle==NULL) {
fprintf(stderr,"\nCould not load the shared library: %s\n",dlerror());
exit(1);
}
fprintf(stdout,"\n The shared library was successfully loaded.\n");
result = dlclose(handle);
if(result!=0) {
fprintf(stderr,"\nCould not unload the shared library: %s\n",dlerror());
exit(1);
}
return 0;
}
The C code also does not work. Apparently, the dynamic loader is not rereading the LD_LIBRARY_PATH environment variable. I need to figure out how to force the dynamic loader to re-read the LD_LIBRARY_PATH environment variable.
See the accepted answer here:
Changing LD_LIBRARY_PATH at runtime for ctypes
In other words, what you're trying to do isn't possible. You'll need to launch a new process with an updated LD_LIBRARY_PATH (e.g., use ProcessBuilder and update environment() to concatenate the necessary directory)
This is a hack used to manipulate JVM's library path programmatically. NOTE: it relies on internals of ClassLoader implementation so it might not work on all JVMs/versions.
String currentPath = System.getProperty("java.library.path");
System.setProperty( "java.library.path", currentPath + ":/path/to/my/libs" );
// this forces JVM to reload "java.library.path" property
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
This code uses UNIX-style file path separators ('/') and library path separator (':'). For cross-platform way of doing this use System Properties to get system-specific separators: http://download.oracle.com/javase/tutorial/essential/environment/sysprop.html
I have successfully implemented something similar for CollabNet Subversion Edge, which depends on the SIGAR libraries across ALL Operating Systems (we support Windows/Linux/Sparc both 32 bits and 64 bits)...
Subversion Edge is a web application that helps one managing Subversion repositories through a web console and uses SIGAR to the SIGAR libraries helps us provide users data values directly from the OS... You need to update the value of the property "java.library.path" at runtime. (https://ctf.open.collab.net/integration/viewvc/viewvc.cgi/trunk/console/grails-app/services/com/collabnet/svnedge/console/OperatingSystemService.groovy?revision=1890&root=svnedge&system=exsy1005&view=markup Note that the URL is a Groovy code, but I have modified it to a Java here)...
The following example is the implementation in URL above... (On Windows, your user will be required to restart the machine if he/she has downloaded the libraries after or downloaded them using your application)... The "java.library.path" will update the user's path "usr_paths" instead of System path "sys_paths" (permissions exception might be raised depending on the OS when using the latter).
133/**
134 * Updates the java.library.path at run-time.
135 * #param libraryDirPath
136 */
137 public void addDirToJavaLibraryPathAtRuntime(String libraryDirPath)
138 throws Exception {
139 try {
140 Field field = ClassLoader.class.getDeclaredField("usr_paths");
141 field.setAccessible(true);
142 String[] paths = (String[])field.get(null);
143 for (int i = 0; i < paths.length; i++) {
144 if (libraryDirPath.equals(paths[i])) {
145 return;
146 }
147 }
148 String[] tmp = new String[paths.length+1];
149 System.arraycopy(paths,0,tmp,0,paths.length);
150 tmp[paths.length] = libraryDirPath;
151 field.set(null,tmp);
152 String javaLib = "java.library.path";
153 System.setProperty(javaLib, System.getProperty(javaLib) +
154 File.pathSeparator + libraryDirPath);
155
156 } catch (IllegalAccessException e) {
157 throw new IOException("Failed to get permissions to set " +
158 "library path to " + libraryDirPath);
159 } catch (NoSuchFieldException e) {
160 throw new IOException("Failed to get field handle to set " +
161 "library path to " + libraryDirPath);
162 }
163 }
The Bootstrap services (Groovy on Grails application) class of the console runs a service and executes it with the full path to the library directory... UNiX-based servers do not need to restart the server to get the libraries, but Windows boxes do need a server restart after the installation. In your case, you would be calling this as follows:
String appHomePath = "/YOUR/PATH/HERE/TO/YOUR/LIBRARY/DIRECTORY";
String yourLib = new File(appHomePath, "SUBDIRECTORY/").getCanonicalPath();
124 try {
125 addDirToJavaLibraryPathAtRuntime(yourLib);
126 } catch (Exception e) {
127 log.error("Error adding the MY Libraries at " + yourLib + " " +
128 "java.library.path: " + e.message);
129 }
For each OS you ship your application, just make sure to provide a matching version of the libraries for the specific platform (32bit-Linux, 64bit-Windows, etc...).