JNI java.lang.NoSuchFieldError in Android Studio - java

I have a c++ library being linked with JNI to Java classes in an Android Studio project, and I am getting an error when trying to use env->GetFieldID() inside JNIAnalalog.cpp
This is the error I get when trying to load in a double from AnalogConfig.java
07-12 14:30:57.597 14360-14360/com.company.myapplication A/art: art/runtime/java_vm_ext.cc:470] JNI DETECTED ERROR IN APPLICATION: JNI NewGlobalRef called with pending exception java.lang.NoSuchFieldError: no "D" field "deadband" in class "[Lcom/automatak/dnp3/AnalogConfig;" or its superclasses
art/runtime/java_vm_ext.cc:470] at java.lang.String java.lang.Runtime.nativeLoad(java.lang.String, java.lang.ClassLoader, java.lang.String) (Runtime.java:-2)
art/runtime/java_vm_ext.cc:470] at java.lang.String java.lang.Runtime.doLoad(java.lang.String, java.lang.ClassLoader) (Runtime.java:1060)
art/runtime/java_vm_ext.cc:470] at void java.lang.Runtime.loadLibrary0(java.lang.ClassLoader, java.lang.String) (Runtime.java:975)
art/runtime/java_vm_ext.cc:470] at void java.lang.System.loadLibrary(java.lang.String) (System.java:1567)
art/runtime/java_vm_ext.cc:470] at void com.company.myapplication.MainActivity.<clinit>() (MainActivity.java:10) // This is where the c++ library is being loaded
I get the error no "D" field "deadband"; however, deadband is defined as a public double inside its java class, as seen below. So I am not sure why it is not considered a "D" field.
If I comment these two lines out and try to use the other env->GetFieldID(), I get a similar error: java.lang.NoSuchFieldError: no "[Lcom/automatak/dnp3/enums/EventAnalogVariation;" field "eventVariation" in class "[Lcom/automatak/dnp3/AnalogConfig;" or its superclasses.
I am not sure if the error is caused only by JNI or if Android Studio is part of the problem
This file set up inside my /app/src/main/ for the relevant files
.
|-- cpp
| `-- // Other libraries are located here
|
|-- dotnet
|
|-- java
| |-- com
| | |-- automatak
| | | `-- dnp3
| | | |-- AnalogConfig.java
| | | |-- EventConfig.java
| | | |-- enums
| | | | |-- EventAnalogVariation.java
| | | | `-- PointClass.java
| | | |-- impl
| | | `-- mock
| | `-- company
| | `-- myapplication
| | `-- MainActivity.java
| |-- cpp
| | |-- adapters
| | |-- JNI.cpp
| | `-- JNI.h
| | `-- jni
| | |-- JNIAnalogConfig.cpp
| | `-- JNIAnalogConfig.h
This is all of the relevant code inside the files
JNIAnalogConfig.cpp
#include "JNIAnalogConfig.h"
namespace jni
{
namespace cache
{
bool AnalogConfig::init(JNIEnv* env)
{
auto clazzTemp = env->FindClass("[Lcom/automatak/dnp3/AnalogConfig;");
this->clazz = (jclass) env->NewGlobalRef(clazzTemp);
env->DeleteLocalRef(clazzTemp);
this->deadbandField = env->GetFieldID(this->clazz, "deadband", "D");
if(!this->deadbandField) return false; // Crashes here
this->eventVariationField = env->GetFieldID(this->clazz, "eventVariation", "[Lcom/automatak/dnp3/enums/EventAnalogVariation;");
if(!this->eventVariationField) return false;
this->clazzField = env->GetFieldID(this->clazz, "clazz", "[Lcom/automatak/dnp3/enums/PointClass;");
if(!this->clazzField) return false;
return true;
}
}
}
JNIAnalog.Config.h
#ifndef OPENDNP3JAVA_JNIANALOGCONFIG_H
#define OPENDNP3JAVA_JNIANALOGCONFIG_H
#include <jni.h>
#include "../adapters/LocalRef.h"
namespace jni
{
struct JCache;
namespace cache
{
class AnalogConfig
{
friend struct jni::JCache;
bool init(JNIEnv* env);
void cleanup(JNIEnv* env);
public:
// field getter methods
jdouble getdeadband(JNIEnv* env, jobject instance);
LocalRef<jobject> geteventVariation(JNIEnv* env, jobject instance);
LocalRef<jobject> getstaticVariation(JNIEnv* env, jobject instance);
private:
jclass clazz = nullptr;
// field ids
jfieldID deadbandField = nullptr;
jfieldID eventVariationField = nullptr;
jfieldID clazzField = nullptr;
};
}
}
#endif
AnaglogConfig.java
import com.automatak.dnp3.enums.EventAnalogVariation;
public class AnalogConfig extends EventConfig {
public AnalogConfig(int index) {
super(index);
}
public double deadband = 0;
public EventAnalogVariation eventVariation = EventAnalogVariation.Group32Var1;
}
EventConfig.java
import com.automatak.dnp3.enums.PointClass;
public class EventConfig {
public EventConfig(int vIndex) {
this.vIndex = vIndex;
this.clazz = PointClass.Class1;
}
public int vIndex;
public PointClass clazz;
}
Any help is appreciated, thank you!

Related

how to add a .aidl file in a delphi android project?

I have two .aidl files and I need to add them to a project delphi.
IPrinterCallback.aidl
IPrinterService.aidl
// IPrinterService.aidl
package com.xcheng.printerservice;
import com.xcheng.printerservice.IPrinterCallback;
//import java.util.List;
//import java.util.Map;
// Declare any non-default types here with import statements
interface IPrinterService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
...
...
The idea is simply to create a button to click and print anything using these packages.
I read several tutorials and got nothing so far.
In one of them I saw that I needed to generate the equivalent .java files and add them to the project.
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.xcheng.printerservice;
//import java.util.List;
//import java.util.Map;
// Declare any non-default types here with import statements
public interface IPrinterService extends android.os.IInterface
{
/** Default implementation for IPrinterService. */
public static class Default implements com.xcheng.printerservice.IPrinterService
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
#Override public void upgradePrinter() throws android.os.RemoteException
{
...
...
...
So I generated them using aidl.exe from skd android and put them in a folder inside the project.
But I always get a compilation error when I try to import it in 'uses'.
my code without importing the AIDL
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.Os,
Android.ServiceApplication,
FMX.Helpers.Android;
type
TForm1 = class(TForm)
Print: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
end.
MyProject/
|
+-- src/
| |
| +-- com/
| |
| +-- xcheng/
| |
| +-- printerservice/
| |
| +-- IPrinterService.aidl
| +-- IPrinterCallback.aidl
| +-- IPrinterService.java
| +-- IPrinterCallback.java
|
+-- assets/
|
+-- AndroidManifest.xml
+-- MyProject.dproj
...
...
Can anyone help me import and consume these files .aidl or .java in android delphi?

JPMS Module System Issue

I created 2 random modules I exported "com.alice" package from folder A and require the module in folder B. Then compiled A: javac mod10/A/module-info.java mod10/A/com/alice/Ship.java and tried to compile B javac mod10/B/module-info.java mod10/B/com/alice2/Main.java. And I get this:
error: module not found: com.alice
requires com.alice;
^
1 error
why I get "module not found" error message?
Shouldn't it be the whole point of exporting a module to make it visible to other modules?
In mod10/A/module-info.java
module com.alice {
exports com.alice;
}
Ship.java
package com.alice;
public class Ship {
private String name;
public Ship() {
this.name = "Ship Created";
}
public String getName() {
return this.name;
}
}
In mod10/B/module-info.java
module com.alice2 {
requires com.alice;
}
Main.java
package com.alice2;
import com.alice;
public class Main {
public static void main(String[] args) {
Ship x = new Ship();
System.out.println("Ship made" + x.getName());
}
}
Folder Tree:
mod10 (root)
|_ A
|_ com
|_ alice
|_ Ship.java
|_ module-info.java
|_ B
|_ com
|_ alice2
|_ Main.java
|_ module-info.java

Can't succeed in calling a Java function from a C++ module with JNI

I'm trying to call a Java class function from a C++ file using JNI and I always get a crash. My C++ file is :
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring
JNICALL
Java_MyPackage_MyApp_stringFromJNI(JNIEnv *env,jobject /* this */,int a,jobject v) {
jclass V = env->GetObjectClass(v);
jmethodID setName=env->GetMethodID(V,"setName","(Ljava/lang/String;)V");
env->CallVoidMethod(v,setName,"new name");
return env->NewStringUTF("Testsss");
}
The problem arises when I try to call the method. In the logcat, I see this :
runtime.cc:500] JNI DETECTED ERROR IN APPLICATION: use of deleted global reference 0x785cd7227ac6
runtime.cc:500] from java.lang.String MyPackage.MyApp.stringFromJNI(int, MyPackage.V)
runtime.cc:500] "main" prio=5 tid=1 Runnable
runtime.cc:500] | group="main" sCount=0 dsCount=0 flags=0 obj=0x73f2d670 self=0x785ceecbea00
runtime.cc:500] | sysTid=7020 nice=-10 cgrp=default sched=0/0 handle=0x785cf3b94a08
runtime.cc:500] | state=R schedstat=( 167686135 3959959 183 ) utm=14 stm=2 core=2 HZ=100
runtime.cc:500] | stack=0x7ffe2c4fc000-0x7ffe2c4fe000 stackSize=8MB
runtime.cc:500] | held mutexes= "mutator lock"(shared held)
The code for my Java function is :
void setName(String name) {
this.name = name;
}
Any idea about what could be the root of the problem?
You can't call the method like that:
env->CallVoidMethod(v,setName,"new name");
You have to construct a Java string first, and then pass that:
jstring str = env->NewStringUTF("new name");
env->CallVoidMethod(v, setName, str);
Otherwise the VM tries to interpret the passed const char* as a global reference to a j.l.String. But it can't find it, so you get that error.

If a class dependency is missing, how can I determine which class is missing?

I am getting the error "Could not find or load main class" even though the class is there. From this answer, I learned that the class can likely be found, but not loaded, due to missing dependencies.
Aside from manually decompiling the class, checking all its dependencies, and if those are on the classpath, and so on for each dependent class ad infinitum....is there any way to determine exactly which class is missing from the classpath therefore causing Java to be unable to load my main class?
That other question was a question of a missing parent/interface class. But I have manually checked that all ancestor classes are either on the specified classpath or in the JDK, as below.
$ cd ~/picketbox
$ java -cp picketbox-4.1.1.Final-redhat-1.jar org.picketbox.datasource.security.SecureIdentityLoginModule HelloWorld
Error: Could not find or load main class org.picketbox.datasource.security.SecureIdentityLoginModule
$ jar xvf picketbox-4.1.1.Final-redhat-1.jar > jarxvf.txt
$ cat jarxvf.txt | grep SecureIdentityLoginModule
inflated: org/picketbox/datasource/security/SecureIdentityLoginModule.class
$ cd org/picketbox/datasource/security/
$ javap SecureIdentityLoginModule.class | grep main
public static void main(java.lang.String[]) throws java.lang.Exception;
$ javap SecureIdentityLoginModule.class | grep extends
public class org.picketbox.datasource.security.SecureIdentityLoginModule extends org.picketbox.datasource.security.AbstractPasswordCredentialLoginModule {
$ cat ~/picketbox/jarxvf.txt | grep AbstractPasswordCredentialLoginModule
inflated: org/picketbox/datasource/security/AbstractPasswordCredentialLoginModule.class
$ javap AbstractPasswordCredentialLoginModule.class | grep extends
public abstract class org.picketbox.datasource.security.AbstractPasswordCredentialLoginModule extends org.jboss.security.auth.spi.AbstractServerLoginModule {
$ cat ~/picketbox/jarxvf.txt | grep AbstractServerLoginModule
inflated: org/jboss/security/auth/spi/AbstractServerLoginModule.class
$ cd ~/picketbox/org/jboss/security/auth/spi/
$ javap AbstractServerLoginModule.class | grep implements
public abstract class org.jboss.security.auth.spi.AbstractServerLoginModule implements javax.security.auth.spi.LoginModule {
$ cd ~/rtjar
$ cp /usr/java/jdk1.8.0_141/jre/lib/rt.jar ./
$ jar xvf rt.jar | grep spi/LoginModule
extracted: javax/security/auth/spi/LoginModule.class
$ cd javax/security/auth/spi/
$ javap LoginModule.class | grep interface
public interface javax.security.auth.spi.LoginModule {
$
Create a helper class:
public class Helper {
public static void main(String[] args) {
YourActualMainClass.main(args);
}
}
and try to run this helper class instead of YourActualMainClass.
The key point is that neither Helper’s inheritance tree nor the signatures of its members depend on the problematic class, so loading and even initialization will succeed, given HotSpot’s lazy resolving strategy, so it will only attempt to load and resolve YourActualMainClass when trying to execute the Helper.main method. At this point, it will throw a detailed error telling you which class was actually missing.
This matches the behavior described in the linked answer that the use of a class leads to a specific error message when the inheritance of the using class does not depend on it.
Alternatively, you could try to run the application with Java 9, as its launcher will print the cause when the loading of the main class failed.
You can try to use JVMTI to display all exceptions that were thrown. Starting with Identifying exceptions through JVMTI. Just use the following updated agent.c source code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <jvmti.h>
#define CHECK_JVMTI_ERROR(x,call) \
{ if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }
/* Global static data */
static jvmtiEnv *jvmti;
static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
jthread thread, jmethodID method, jlocation location, jobject exception,
jmethodID catch_method, jlocation catch_location)
{
jclass exceptionClass = (*jni_env)->GetObjectClass(jni_env, exception);
jmethodID methodId = (*jni_env)->GetMethodID(jni_env, exceptionClass,
"printStackTrace",
"()V");
(*jni_env)->CallVoidMethod(jni_env, exception, methodId);
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiError r;
jvmtiCapabilities capabilities;
jvmtiEventCallbacks callbacks;
/* Get JVMTI environment */
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
if (rc != JNI_OK)
{
fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
return -1;
}
/* Get/Add JVMTI capabilities */
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_generate_exception_events = 1;
r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
CHECK_JVMTI_ERROR(r, AddCapabilities);
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = &cb_Exception;
r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(r, SetEventCallbacks);
/* Exception events */
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
return 0;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}

Java loading JAR dynamically: NoClassDefFoundError

Let's say I have a main class App that loads all jars in the sub-directory plugins using a URLClassLoader:
public class App(){
public static void main(String[] args){
for(File f : new File("plugins").listFiles()){
URL[] urls = { new URL("jar:file:" + "plugins/" + f.getName() + ".jar" + "!/") };
URLClassLoader cl = URLClassLoader.newInstance(urls);
Class<?> clazz = cl.loadClass(f.getName().toLowerCase()+"."+f.getName());
cl.close();
Plugin p = ((Plugin) clazz.newInstance());
}
}
}
All those jars contain a class that implements an interface Plugin.
+-- Main.jar
| +-- App.class
| +-- Plugin.class
|
+-- Plugins/
| +-- PluginTest.jar
| +-- plugintest
| +-- PluginTest.class
| +-- Two.class
That's all working fine if I write my code just in the PluginTest class. But as soon as I try to access Two from PluginTest, I'm getting a big error:
Exception in thread "Thread-4" java.lang.NoClassDefFoundError: plugintest/Two
[...]
How should I load the the class correctly? Need help! Thanks.
Do not close your classloader.
Remove cl.close();statement.

Categories