Please Excuse my absolute lack of Android JNI
I want to analize/call one function hidden in shared object: libEncodeAndDecodeUtils.so
orginal code uses it like this:
package com.midea.msmartsdk.common.utils;
public class EncodeAndDecodeUtils {
public native String esha(String str, String str2, String str3);
}
Ida decompiler shows:
Java_com_midea_msmartsdk_common_utils_EncodeAndDecodeUtils_esha
How can I call this function in my project?
My current attempt:
new AndroidStudio project with JNI support
adding folder \src\main\jniLibs\armeabi-v7a
copy libEncodeAndDecodeUtils.so to armeabi-v7a
buid.bradle android{ defaultConfig { ndk {abiFilters "armeabi-v7a"} }
and creating class
package com.midea.msmartsdk.common.utils.so_test4;
public class EncodeAndDecodeUtils {
static {
System.loadLibrary("EncodeAndDecodeUtils");
}
public native String esha(String str, String str2, String str3);
}
this lead me to error:
E/zygote: No implementation found for
java.lang.String com.midea.msmartsdk.common.utils.so_test4.EncodeAndDecodeUtils.esha
(java.lang.String,
java.lang.String,
java.lang.String)
(tried
Java_com_midea_msmartsdk_common_utils_so_1test4_EncodeAndDecodeUtils_esha
and Java_com_midea_msmartsdk_common_utils_so_1test4_EncodeAndDecodeUtils_esha__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2)
is this my problem?
Java_com_midea_msmartsdk_common_utils_EncodeAndDecodeUtils_esha
!=
Java_com_midea_msmartsdk_common_utils_so_1test4_EncodeAndDecodeUtils_esha
If you try to access esha from your own code, then you HAVE TO declare this method exactly the same as its original form, because JNI will try to find the implementation following some fixed convention. According to https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/design.html
Resolving Native Method Names
Dynamic linkers resolve entries based on their names. A native method name is concatenated from the following components:
the prefix Java_
a mangled fully-qualified class name
an underscore (“_”) separator
a mangled method name for overloaded native methods,
two underscores (“__”) followed by the mangled argument signature
Simply speaking, it will be something like Java_packagename_ClassName_methodsignature.
For example, in your case, it is Java_com_midea_msmartsdk_common_utils_EncodeAndDecodeUtils_esha.
So, try to remove so_test4 from your package name.
Related
I developed a Android Binding Library to bind a printer Android AAR file (The AAR has its build action set to LibraryProjectZip), and the project is compiled using Android 7.1, using class-parse android class parser, and AndroidCodeGen Target: XAJavaInterop1. I can provide the simple Project in order to have full access to the code and library.
But I have the following error: 'The type or namespace 'ILibrary' does not exist in the namespace 'Com.Sun.Jna' at Com.Sun.Jna.Native.cs. So I decided to check at the Android library using a decompiler and here is what I found:
The AAR file contains an interface 'Library' (not ILibrary), and the Native.class file contains a method whose signature is the following and is the source of the error and which requires a Library object as input
public static Library synchronizeLibrary(final Library library){...}
The C# generated code contains a Com.Sun.Jna.ILibrary.cs file which contains a public abstract class 'Library':
[Register ("com/sun/jna/Library", DoNotGenerateAcw=true)] public abstract class Library : Java.Lang.Object {...}
but the generated Native.cs file has the counterpart method SynchronizeLibrary :
// Metadata.xml XPath method reference: [Register ("synchronizedLibrary", "(Lcom/sun/jna/Library;)Lcom/sun/jna/Library;", "")] public static unsafe global::Com.Sun.Jna.ILibrary SynchronizedLibrary(global::Com.Sun.Jna.ILibrary library) {...}
I do not understand why the C# generated method SynchronizedLibrary demands an ILibrary and not the Library.
I tried to modify the Metadata.xml file to rename the class Library to ILibrary but with no success. Moreover I have a warning saying that:
class-parse: warning: could not load .jar entry 'libjnidispatch.jnilib': System.Argument.OutOfRangeException,
I tried to change the name of the interface from Library to ILibrary using <attr path="/api/package[#name='com.sun.jna']/class[#name='Library']" name="managedName">ILibrary</attr> but the definition of the interface does not change. It is strange that I have a reference path for the members of the interface but not the interface itself
[Register ("com/sun/jna/Library", DoNotGenerateAcw=true)]
public abstract class Library : Java.Lang.Object {
internal Library ()
{
}
// Metadata.xml XPath field reference: path="/api/package[#name='com.sun.jna']/interface[#name='Library']/field[#name='OPTION_ALLOW_OBJECTS']"
[Register ("OPTION_ALLOW_OBJECTS")]
public const string OptionAllowObjects = (string) "allow-objects";,
and I do not know if it is related to the prior problem.
Thank you for any suggestion
Finally, I found the way to correct the error by modifying the name of the interfaces by adding the following lines to the Metadata file:
<attr path="/api/package[#name='com.sun.jna']/interface[#name='Library']" name="name">ILibrary</attr>
But now I have errors telling me that JNIENv does not contain method like NewString. And I am wondering if it is related to the warning i have telling me that It cannot load .jar entry libjnidispatch.jnilib
Any ideas, thanks
I have an .aar third party library that I want to use in Xamarin Android. So I created a new Android Bindings Library, added the aar-library and changed the Build action of the aar file to LibraryProjectZip like described here: https://learn.microsoft.com/en-us/xamarin/android/platform/binding-java-library/binding-an-aar
Nothing else was changed and I would expect the project to compile and generate a dll file.
Instead I get a lot of errors saying Error CS0542 'xy': member names cannot be the same as their enclosing type.
When I jump to the origin of the error, I find the errors in generated code by Visual Studio with the classes looking something like:
public abstract class Albumin : Java.Lang.Object {
internal Albumin ()
{
}
// (removed for readability)
[Register ("ALBUMIN")]
public const string Albumin = (string) "albumin";
I cannot modify the source code of the library.
What can I do in order to build the Binding Library successfully?
Thank you very much #Leo Zhu for the answer in the comments:
The solution is Renaming Members.
So in my case the Metadata.xml in die Binings Library would look like the following:
<attr path="/api/package[#name='com.company.android.sdk.dataclass']/interface[#name='DataClass.Albumin']/field[#name='ALBUMIN']" name="name">ALBUMIN_Binding</attr>
I'm trying to embed some jars from a native project into my Xamarin Android app, and I have hit two separate (but I suspect related) problems:
One of the files in the package is jna-4.2.0.jar. When I try to build the binding project on its own, I get this error:
The type or namespace name 'ICallback' does not exist in the namespace 'Com.Sun.Jna' (are you missing an assembly reference?)
The generated code is as follows:
[Register ("getCallback", "(Ljava/lang/Class;Lcom/sun/jna/Pointer;)Lcom/sun/jna/Callback;", "")]
public static unsafe global::Com.Sun.Jna.ICallback GetCallback (global::Java.Lang.Class type, global::Com.Sun.Jna.Pointer p)
{
if (id_getCallback_Ljava_lang_Class_Lcom_sun_jna_Pointer_ == IntPtr.Zero)
id_getCallback_Ljava_lang_Class_Lcom_sun_jna_Pointer_ = JNIEnv.GetStaticMethodID (class_ref, "getCallback", "(Ljava/lang/Class;Lcom/sun/jna/Pointer;)Lcom/sun/jna/Callback;");
try {
JValue* __args = stackalloc JValue [2];
__args [0] = new JValue (type);
__args [1] = new JValue (p);
global::Com.Sun.Jna.ICallback __ret = global::Java.Lang.Object.GetObject<global::Com.Sun.Jna.ICallback> (JNIEnv.CallStaticObjectMethod (class_ref, id_getCallback_Ljava_lang_Class_Lcom_sun_jna_Pointer_, __args), JniHandleOwnership.TransferLocalRef);
return __ret;
} finally {
}
}
I have also tried downloading the latest version, jna-4.4.0.jar, from https://github.com/java-native-access/jna, but that generates the same error.
The library I'm using generates this error:
'Version' does not implement interface member
'IComparable.CompareTo(Object)'
I have tried to solve this issue, following the Xamarin documentation, and using the comments in the generated cs files, by adding this line to the metadata.xml file in my bindings project:
<attr path="/api/package[#name='com.innovatrics.iface']/class[#name='Version']/method[#name='compareTo'
and count(parameter)=1
and parameter[1][#type='com.innovatrics.iface.Version']]"
name="managedType">Java.Lang.Object</attr>
but this seems to have had no effect at all.
Have you try to change the classname in this case?
Try to add the following in the Metadata.xml file (untested):
<attr path="/api/package[#name='com.sun.jna']/interface[#name='Callback']" name="name">ICallback</attr>
This will change the interface name from Callback in Java to ICallback in C#
Using the above rename method, i was able to get past this, but found a few more errors when also attempting to Bind the JNA Jar file.
I wanted to know, how much further you got with this, and if possible could share the Metadata.xml file that allowed you to complete this binding please ?
Starting from an empty Qt for Android project, created with Qt Creator, how do I execute native Java code?
For example, if I have this java method:
class HelloJava {
public static String sayHello(int a, int b) {
return "Hello from Java, a+b=" + (a+b);
}
}
And I have this Qt method which should call the HelloJava.sayHello() method:
QString call_HelloJava_sayHello(int a, int b) {
// What goes in here?
}
Where do I put the Java code in my project, and what goes inside the call_HelloJava_sayHello() method?
The Qt doc is pretty verbose on the subject:
// Java class
package org.qtproject.qt5;
class TestClass
{
static String fromNumber(int x) { ... }
static String[] stringArray(String s1, String s2) { ... }
}
// C++ code
// The signature for the first function is "(I)Ljava/lang/String;"
QAndroidJniObject stringNumber = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/TestClass",
"fromNumber"
"(I)Ljava/lang/String;",
10);
// the signature for the second function is "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
QAndroidJniObject string1 = QAndroidJniObject::fromString("String1");
QAndroidJniObject string2 = QAndroidJniObject::fromString("String2");
QAndroidJniObject stringArray = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/TestClass",
"stringArray"
"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
string1.object<jstring>(),
string2.object<jstring>());
So the sig of your function should be "(I;I;)Ljava/lang/String;"
And also this:
ANDROID_PACKAGE_SOURCE_DIR: This variable can be used to specify a
directory where additions and modifications can be made to the default
Android package template. The androiddeployqt tool will copy the
application template from Qt into the build directory, and then it
will copy the contents of the ANDROID_PACKAGE_SOURCE_DIR on top of
this, overwriting any existing files. The update step where parts of
the source files are modified automatically to reflect your other
settings is then run on the resulting merged package. If you, for
instance, want to make a custom AndroidManifest.xml for your
application, then place this directly into the folder specified in
this variable. You can also add custom Java files in
ANDROID_PACKAGE_SOURCE_DIR/src.
Note: When adding custom versions of the build files (like
strings.xml, libs.xml, AndroidManifest.xml, etc.) to your project,
make sure you copy them from the package template, which is located in
$QT/src/android/java. You should never copy any files from the build
directory, as these files have been altered to match the current build
settings.
So it seems you need to specify that and the java files are automatically deployed.
Just add this to your .pro file (assuming your java sources are placed at /path/to/project/myjava/src:
android {
ANDROID_PACKAGE_SOURCE_DIR=$$_PRO_FILE_PWD_/android
QT += androidextras
}
_PRO_FILE_PWD_ is a qmake variable that will resolve to the project directory.
In my strings.xml file I have
<string name="continue">Continue</string>
I can't build my project because of the error: "Invalid symbol: 'continue'". Why I can't use such a name?
It's because continue is a reserved symbol in Java, so you cannot use it as a name for any object in your XML files or Java code.
The reason this is a problem is that the XML defined in your project is translated into Java code that the Dalvik VM can understand. So, your code above translates into the following in R.java:
public final class R {
public static final class string {
public static final int continue=0x7f040000;
}
}
The problem is more obvious when examining the (would-be) generated code.
See list of reserved Java symbols for others to avoid.
"continue" is a Java keyword and the R.java would not compile.
public static final int continue=0x7f040001;
the above code would cause an "Syntax error on token "continue", invalid VariableDeclaratorId" error.