We are working on an Android and iOS (but we want to just get Android working for now) application that needs to rely on some native drivers (.so, .a and some .ini files). The company providing those drivers also provides a Xamarin project to showcase how the drivers are used. They seem to be storing those driver and other files as assets (.ini files under Assets/Files and .so files under Assets/lib/arm64-v8a and Assets/lib/armeabi-v7a respectively) and extracting the former using the following code:
void ExtractAssets()
{
var assets = ApplicationContext.Assets;
var paths = assets.List("Files");
foreach (var path in paths)
{
// Read the compressed file and extract
string readPath = Path.Combine("Files", path);
byte[] buffer = new byte[32000];
int bytesRead = 0;
using (var inStream = assets.Open(readPath))
using (var outStream = ApplicationContext.OpenFileOutput(path, Android.Content.FileCreationMode.Private))
{
do
{
bytesRead = inStream.Read(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
outStream.Write(buffer, 0, bytesRead);
}
}
while (bytesRead > 0);
}
System.Diagnostics.Debug.WriteLine(string.Format("Extracted '{0}' to '{1}'", readPath, path));
}
}
Since Xamarin is becoming obsolete and MAUI is the next big thing, we decided to build our project on that. My colleague and I have spent the entire afternoon on a sample project and we still do not manage to simply load a libhello-jni.so file (we use this one) at runtime to call a stringFromJNI(). We tried:
Using an Android Native Binding project — we never really got what that's about and how mappings are supposed to work
Trying to include the libhello-jni.so file as an asset and load it using JavaNative.LoadLibrary("hello-jni"); and this had various unsuccessful results, it either did not find the library at all or it was complaining about the lib being built for x86 time processor instead of x64.
Can anyone please help us with some sample code allowing us to just simply bind a native .so file in a MAUI project, or at least provide us with a good guide? Thank you and sorry for the long post.
Not sure if this is exactly what you are going for or not, but we use bindings to a C library that we develop for various platforms, including Android. The way to correctly place native libraries into the final APK is via the EmbeddedNativeLibrary type in .csproj as demonstrated here. This will include your native library alongside the other native libraries that the toolchain is going to create (like the .NET runtime, garbage collection native lib, etc).
One thing changed in .NET 6, however. This affects us as an SDK distributor, because we need to include all necessary files inside of Nuget packages, so it may not affect you. In Xamarin, when you embed native libraries, the native libraries are literally stitched into your assembly and no further files are needed. In .NET 6, now they are all placed into a separate .aar file which also needs to be available when building the final application.
If you do everything correctly, you will see your native library inside of the APK in the lib/<arch> directory. Our native library is not JNI, and thus we use P/Invoke so we don't need a LoadLibrary call but I am reasonably certain that the call should succeed with the libraries in this directory.
Related
I have an Android native library (C++ code base) called:
libserverapp.so
And I cannot get the Android build to find it:
"DllNotFoundException: serverapp"
I am using an internal build system, but when I parse the output of the build process, I can see many calls of the form:
android-ndk/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/arm-linux-androideabi-g++.exe -march=armv7-a
After building my Android app, I inspect the resulting APK (renaming to .zip and extracting), and can see my library file here:
lib/armeabi-v7a/libserverapp.so
I can confirm that "ARMv7" is the target architecture in the Android Player settings, and I access the library, in C#, via:
[DllImport("serverapp", CallingConvention = CallingConvention.Cdecl)]
private static extern void run_sim(StringBuilder matchInput, StringBuilder results, int randomSeed);
I have built a Windows DLL of the C++ code, to use in the Editor, and everything works great. However, when I move to Android, the .so cannot be found. The import settings for libserverapp.so are:
Platform: Android; CPU: ARMv7; Path: Assets/Plugins/Android/libserverapp.so; Type: Native
Given that the final APK includes the .so where I expect it to be (lib/armeabi-v7a/), I assume my Unity settings are correct? Also, I am not using IL2CPP for the Android build.
Finally, when I do an object dump of the library file (using arm-linux-androideabi-objdump.exe), the file format of the library file is "elf32-littlearm".
I feel that the issue here is simply finding the .so, not the functionality within it. Any ideas on what's going on here?
Thanks!
I ended up solving the problem. I mentioned that was using an internal build system. Well, there seems to be a bug in it. I ported things over to official Android NDK makefiles, and then it "just worked". So in this case, the library could be found, but its contents weren't valid.
I downloaded an APK from Play Store that contains native code binaries. In the APK file there is an lib/x86 folder that supposedly contains a library file containing native procedures, normally a .so extension. Since the code is in x86, is it possible to write a Java program to invoke the library on the desktop? Even if you dont have the source code for that library. The NDK function just has to accept parameters and return a value. For example, can we write
class AppNativeLoader
{
public static native void generateRand(int seed);
static
{
System.loadLibrary( "AndroidNDKLib" );
}
}
public class WCallTest
{
public static void main( String[ ] args )
{
long seed = System.currentTimeMillis();
if(args.length > 0) {
seed = Long.valueOf(args[0]);
}
long rand = AppNativeLoader.generateRand(seed);
System.out.println(rand);
}
}
NOTE: This is just an example. The actual environment differs. Using JRE 7 on RHEL, I extracted the x86 .so and placed it in the same directory as the .class file. I still get an UnSatisfiedLinkerError. Anything amiss? Assuming there are no callbacks and the function doesn't utilize and Android APIs, is this possible?
EDIT: I opened the lib in IDA Pro and I saw the following dependencies
.plt:0000B100 ; Needed Library 'liblog.so'
.plt:0000B100 ; Needed Library 'libz.so'
.plt:0000B100 ; Needed Library 'libc.so'
.plt:0000B100 ; Needed Library 'libm.so'
.plt:0000B100 ; Needed Library 'libstdc++.so'
.plt:0000B100 ; Needed Library 'libdl.so'
These should be available in my desktop environment, no?
Not all Linux environments are identical (even crossing distribution boundaries is not guaranteed to work). NDK binaries are built against Bionic and a handful of other Android specific libraries, whereas your RedHat system uses glibc and a bunch of other things available from the RedHat repositories.
tl;dr you can't run Android binaries on desktop Linux.
You can try downloading the needed shared libraries from here (make sure to choose the correct API version, and an architecture matching the architecture of the NDK shared library, to find out which shared libraries you need you can simply use ldd).
Then, to easily access the methods exposed by the shared lib, you can decompile the java code of the app using jadx, and then write your own code around the JNI classes.
Then, to compile your java code, you can use any version of the JDK.
Then, to execute it, you'll have to use a version of JRE matching the architecture of the NDK shared library (in your case, you'll have to download the 32-bit JRE).
However, this is not guaranteed to work: I am currently getting segfaults in the NDK shared library I'm trying to use on my PC, and since most NDK binaries are stripped, debugging is going to be a nightmare.
We have been working on an Android project using Adobe AIR for a long time and now we need to implement the feature to search for Upnp devices in the network. From my understanding, it is not possible to implement this feature on AIR level (please correct me if I am wrong), so an Android native extension is required. I could not find any UPNP Native Extension available and decided to build one based on Cling library Cling UPNP Browser. I could get it work as native Android application but when I tried to convert it to AIR native extension, it did not work at all. Does anyone successfully implement the UPNP on Android - AIR, any help will be greatly appreciate.
After spending more time to search for other alternative, finally, I have made it work using CyberLink For Java. The implementation of CyberLink library is very straight forward. For anyone plans to create your own UPNP Native Extension for Android, here is the process to build your .jar native extension.
Include the external cyberlink .jar library to your Android Java project.
To start searching for UPNP Devices, you have to open a new thread:
new SearchingForUpnpTask().execute("Empty Param"); //You can specify your own param...
private class SearchingForUpnpTask extends AsyncTask<String, Void, DeviceList>{
protected DeviceList doInBackground(String... params){
ControlPoint ctrPoint = new ControlPoint();
ctrPoint.start("upnp:rootdevice");
DeviceList devList = ctrPoint.getDeviceList();
int nRootDevs = devList.size();
for (int n=0; n < nRootDevs; n++) {
Device dev = devList.getDevice(n);
String devName = dev.getFriendlyName();
System.out.println("[" + n + "] = " + devName);
}
...
return devList;
}
To compile the .jar file for your Android will require some works as Adobe AIR does not understand your external cyberlink .jar file that we include. When you try to debug the native extension, you will receive the error Log: ...the class 'org...ControlPoint' is not found in the method ... . In order to make it work, you have to combine all the .jar files into one. We have 2 options here:
1st method: Export your Andoird .jar file, change the extension to .zip then unzip it. Then change the .jar extension of the cyperlink .jar file to .zip then unzip it. Copy the source from the cyperlink .jar folder to your Android's .jar folder. Finally, jar the whole folder again.
2nd method (easier): Use jarjar.jar from Google (thanks to Joe Ward).
Hope this helps.
I currently developing app for Android that provides various audio settings. I use android system prepared by someone else, and it provides (I see in source and compiled files) some methods that could be useful for me. For example there are (kernel/drivers/audio/audio.c) some methods to change bands (for equalizer). In compiled system there is audio_setting.so file in some audio dir on kernel. Is there a possibility to use this methods (library) in my application NDK? I don't want to compile my app with whole system, rather to dynamically add this lib.
edit:
It was simplier than I thought. I used:
void *some_lib;
bundlewrapper = dlopen("some/path/some_lib.so", RTLD_LAZY);
if ( some_lib!= NULL ) {
LOGV("Loaded lib\n");
// use methods from lib
}
Sure, you can use any code on the system.
Obviously if it is non-JNI code you'll have to call it from your own JNI code or wrapper.
In your Android.mk file you will need to add the extra lib in LOCAL_LDLIBS
Getting "system.entrypointnotfoundexception: loadlibrary" While trying to use SevenZipLib.dll to uncompress the .7z file containing media contents/file in the Android evn.
Context:
-The whole program is written in c# as a MONO Android Project. No Build/Deployment Error/warnings.
While running the apk, its throwing "system.entrypointnotfoundexception: loadlibrary".
-Also tested the same code as windows project (not mono) - uncompressing in the windows evn.
Assumptions for the issue:
7zip internally might be using COM components & Mono frame work is not supporting.
Question:
Has anyone come across similar issue? Please suggest some alternative dll/framework which can be used by my apk for uncompressing the .7z file.
Assuming that SevenZipLib.dll is the SevenZipLib Library on CodePlex, the problem is SevenZipLib\SevelZipLib\SevenZipArchive.cs:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern SafeLibraryHandle LoadLibrary(
[MarshalAs(UnmanagedType.LPTStr)] string lpFileName);
The project contains numerous P/Invokes into kernel32.dll (LoadLibrary(), GetProcAddress(), FreeLibrary()), ole32.dll (PropVariantClear()), oleaut32.dll (SafeArrayCreateVector()), and more.
In short, this library is intimately tied to Windows, and isn't going to work on a non-Windows platform in any meaningful fashion, not without a lot of work.
If you need 7z support in a Mono for Android application, you'll need to look into a different library. It looks like the 7-zip SDK includes C# source for reading LZMA files that doesn't rely on P/Invoke, so perhaps that would work?