Difficulties with calling Android NDK function directly from Delphi

Using the JNI and NDK functions, you can call the Android C function from Delphi. There is a lot of work to implement this, and it has been proposed to directly call NDK functions. To do this, I created a small example file to declare an external C function by the lines that I found in the Delphi source code. More specifically in <path to delphi>\source\rtl\android .

I created a very small test program to test the functionality of calling a C function directly from Delphi. All of the source code that you find below is what I am testing now.

  unit DLL_external; interface const MIDI_Lib = '/usr/lib/libmiditest.so'; test_fun = 'test_1'; function test_1 (n: Integer): Integer; cdecl; external MIDI_Lib name test_fun; implementation initialization finalization end. 

Initialization and finalization are necessary because other errors are related to the lack of an initialization and completion code. The calling class:

  unit DLL_Test_Main; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, DLL_external; //{$I Androidapi.inc} type TForm1 = class(TForm) Button_Load: TButton; Label1: TLabel; procedure Button_LoadClick (Sender: TObject); procedure FormCreate(Sender: TObject); public procedure call_external_function (value: Integer); end; // Class: TForm1 // var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate (Sender: TObject); begin Label1.Text := 'External function not called yet'; end; // FormCreate // procedure TForm1.Button_LoadClick (Sender: TObject); begin call_external_function (3); end; // Button_LoadClick // procedure TForm1.call_external_function (value: integer); var n: Int32; begin n := test_1 (value); Label1.Text := Format ('%d = test_1 (%d)', [n, value]); end; // call_external_function // end. 

Together with the native miditest library. This was built using ndk-build . The resulting libmiditest.so library was copied to C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\android-ndk-r8e\platforms\android-14\arch-arm\usr\lib , since this is the place where Delphi has hosted its own libraries.

  #include <jni.h> int test_1 (int n) // little test for callability { return n * n; } 

When I do ndk-build , the libmiditest.so file is created in the libs\armeabi-v7a . I copied this file to <path to your ndk directory>\platforms\android-14\arch-arm\usr\lib . Since I had some binding errors at the beginning (wrong names and such stupid errors), I used readelf -AWs libmiditest.so to create a list of characters and the expected library architecture. The name test_1 was in the character list, as in the arm v7 architecture (I use Nexus 7 for testing). When I run the Delphi program, it immediately runs on Android: "Unfortunately, DLL_Test_Propject has stopped." Looking at adb output (see below), it is expected that the file libDLL_Test_Project.so is expected. I replaced libmiditest.so with the unit DLL_external with libDLL_Test_Project.so and copied /usr/lib/libmiditest.so to /usr/lib/libDLL_Test_Project.so . It did not help.

Does anyone understand why the application created by Delphi is trying to load its own library? And better: any suggestion, what should I call Android C-function through Delphi?

  I/InputReader( 608): Reconfiguring input devices. changes=0x00000010 D/dalvikvm( 799): GC_FOR_ALLOC freed 2003K, 15% free 14582K/16964K, paused 29ms, total 29ms I/PCKeyboard( 799): Loaded dictionary, len=841005 I/HK/LatinKeyboardBaseView( 799): onMeasure width=1200 I/HK/LatinKeyboardBaseView( 799): onMeasure width=1200 D/Documents( 3358): Used cached roots for com.android.providers.downloads.documents D/Documents( 3358): Used cached roots for com.android.externalstorage.documents D/Documents( 3358): Used cached roots for com.android.providers.media.documents D/Documents( 3358): Used cached roots for com.google.android.apps.docs.storage D/Documents( 3358): Update found 7 roots in 28ms D/BackupManagerService( 608): Received broadcast Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.embarcadero.DLL_Test_Project flg=0x4000010 (has extras) } V/BackupManagerService( 608): addPackageParticipantsLocked: #1 D/SystemBroadcastService( 987): Received broadcast action=android.intent.action.PACKAGE_ADDED and uri= W/ContextImpl( 987): Implicit intents with startService are not safe: Intent { act=com.google.android.gms.games.service.INTENT } android.content.ContextWrapper.startService:494 com.google.android.gms.games.service.GamesIntentService.a:101 com.google.android.gms.games.service.GamesIntentService.b:368 I/ActivityManager( 608): Delay finish: com.android.vending/com.google.android.finsky.receivers.PackageMonitorReceiver$RegisteredReceiver I/ActivityManager( 608): Resuming delayed broadcast I/ActivityManager( 608): Delay finish: com.google.android.apps.plus/.service.PackagesMediaMonitor I/ActivityManager( 608): Resuming delayed broadcast V/GelStubAppWatcher( 3631): onReceive: android.intent.action.PACKAGE_ADDED I/Icing.InternalIcingCorporaProvider( 3631): Updating corpora: A: com.embarcadero.DLL_Test_Project, C: MAYBE I/ActivityManager( 608): START u0 {flg=0x10800000 cmp=com.estrongs.android.pop/.app.InstallMonitorActivity (has extras)} from pid 3466 D/dalvikvm( 608): GC_EXPLICIT freed 1385K, 11% free 19671K/22028K, paused 3ms+8ms, total 194ms D/dalvikvm( 608): WAIT_FOR_CONCURRENT_GC blocked 24ms D/AndroidRuntime( 4437): Shutting down VM D/dalvikvm( 4437): GC_CONCURRENT freed 95K, 16% free 560K/660K, paused 0ms+0ms, total 2ms W/InputMethodManagerService( 608): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@42c98f48 attribute=null, token = android.os.BinderProxy@42b91f10 D/dalvikvm( 3631): GC_CONCURRENT freed 560K, 6% free 10268K/10860K, paused 2ms+2ms, total 23ms D/AndroidRuntime( 4476): D/AndroidRuntime( 4476): >>>>>> AndroidRuntime START com.android.internal.os.RuntimeInit <<<<<< D/AndroidRuntime( 4476): CheckJNI is OFF D/dalvikvm( 4476): Trying to load lib libjavacore.so 0x0 D/dalvikvm( 4476): Added shared lib libjavacore.so 0x0 D/dalvikvm( 4476): Trying to load lib libnativehelper.so 0x0 D/dalvikvm( 4476): Added shared lib libnativehelper.so 0x0 D/dalvikvm( 4476): No JNI_OnLoad found in libnativehelper.so 0x0, skipping init D/dalvikvm( 4476): Note: class Landroid/app/ActivityManagerNative; has 179 unimplemented (abstract) methods D/AndroidRuntime( 4476): Calling main entry com.android.commands.am.Am I/ActivityManager( 608): START u0 {flg=0x10000000 cmp=com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity (has extras)} from pid 4476 D/dalvikvm( 608): GC_FOR_ALLOC freed 807K, 12% free 19517K/22028K, paused 63ms, total 63ms D/AndroidRuntime( 4476): Shutting down VM D/dalvikvm( 4476): GC_CONCURRENT freed 96K, 15% free 586K/684K, paused 0ms+0ms, total 2ms D/dalvikvm( 4507): Late-enabling CheckJNI I/ActivityManager( 608): Start proc com.embarcadero.DLL_Test_Project for activity com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity: pid=4507 uid=10113 gids={50113, 3003, 1028, 1015} I/dalvikvm( 4507): Enabling JNI app bug workarounds for target SDK version 9... V/PhoneStatusBar( 667): setLightsOn(true) D/AndroidRuntime( 4507): Shutting down VM W/dalvikvm( 4507): threadid=1: thread exiting with uncaught exception (group=0x41ccbba8) E/AndroidRuntime( 4507): FATAL EXCEPTION: main E/AndroidRuntime( 4507): Process: com.embarcadero.DLL_Test_Project, PID: 4507 E/AndroidRuntime( 4507): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativ eActivity}: java.lang.IllegalArgumentException: Unable to load native library: /data/app-lib/com.embarcadero.DLL_Test_Project-1/libDLL_Test_Project.so E/AndroidRuntime( 4507): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195) E/AndroidRuntime( 4507): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245) E/AndroidRuntime( 4507): at android.app.ActivityThread.access$800(ActivityThread.java:135) E/AndroidRuntime( 4507): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196) E/AndroidRuntime( 4507): at android.os.Handler.dispatchMessage(Handler.java:102) E/AndroidRuntime( 4507): at android.os.Looper.loop(Looper.java:136) E/AndroidRuntime( 4507): at android.app.ActivityThread.main(ActivityThread.java:5017) E/AndroidRuntime( 4507): at java.lang.reflect.Method.invokeNative(Native Method) E/AndroidRuntime( 4507): at java.lang.reflect.Method.invoke(Method.java:515) E/AndroidRuntime( 4507): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) E/AndroidRuntime( 4507): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) E/AndroidRuntime( 4507): at dalvik.system.NativeStart.main(Native Method) E/AndroidRuntime( 4507): Caused by: java.lang.IllegalArgumentException: Unable to load native library: /data/app-lib/com.embarcadero.DLL_Test_Project-1/libDLL_Test_Project.so E/AndroidRuntime( 4507): at android.app.NativeActivity.onCreate(NativeActivity.java:183) E/AndroidRuntime( 4507): at com.embarcadero.firemonkey.FMXNativeActivity.onCreate(FMXNativeActivity.java:67) E/AndroidRuntime( 4507): at android.app.Activity.performCreate(Activity.java:5231) E/AndroidRuntime( 4507): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087) E/AndroidRuntime( 4507): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159) E/AndroidRuntime( 4507): ... 11 more W/ActivityManager( 608): Force finishing activity com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity I/WindowManager( 608): Screenshot max retries 4 of Token{42a06c48 ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNat iveActivity t17 f}} appWin=Window{42a1bab8 u0 Starting com.embarcadero.DLL_Test_Project} drawState=4 W/WindowManager( 608): Screenshot failure taking screenshot for (1200x1920) to layer 21015 W/ActivityManager( 608): Activity pause timeout for ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity t17 f} E/WindowManager( 608): Starting window AppWindowToken{4308ff58 token=Token{42a06c48 ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity t17}}} timed out I/Process ( 4507): Sending signal. PID: 4507 SIG: 9 W/InputMethodManagerService( 608): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@42a1abd0 attribute=null, token = android.os.BinderProxy@42b91f10 I/ActivityManager( 608): Process com.embarcadero.DLL_Test_Project (pid 4507) has died. D/Finsky ( 1567): [1] 5.onFinished: Installation state replication succeeded. 

Update 1

From the comments, I realized that code can be part of a larger system. This code is a small standalone program. The native code library is really as small as you see here.

Update 2

As Arioch'The indicates that when using static binding (or implicit loading in terms of Windows), the main program does not load when the library does not load. This explains the adb post mentioned above. So querstion: why is libmiditest.so not loading?

+6
source share
1 answer

One solution is to dynamically link the library, as suggested by Arioch'The in his comments. The mechanism on how to do this is described in his wiki link. For communication in the library, use dlopen from the Posix.Dlfcn block.

However, you must provide the path to the library. In the following code example, I created the Data\d directory in the sdcard0 directory and copied libmiditest.so into it. This directory may be different on other systems. In fact, this may explain the reason why static binding does not work (for now). The library is not copied in the paths that the system searches for when searching for libraries.

  unit DLL_Test_Main; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, Posix.Dlfcn, FMX.Layouts, FMX.Memo, System.Math; const lib_path = '/storage/sdcard0/Data/d/libmiditest.so'; fun_name = 'test_1'; type TForm1 = class(TForm) Button_Load: TButton; Memo1: TMemo; procedure Button_LoadClick (Sender: TObject); procedure FormCreate(Sender: TObject); protected Lib_Handle: THandle; public procedure call_external_function (value: Integer); end; // Class: TForm1 // var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate (Sender: TObject); begin Lib_Handle := THandle (dlopen (lib_path, RTLD_LAZY)); if Lib_Handle = 0 then begin Memo1.Lines.Add ('Cannot open library: ' + lib_path); end else begin Memo1.Lines.Add ('Opened library: ' + lib_path); end; // if end; // FormCreate // procedure TForm1.Button_LoadClick (Sender: TObject); begin call_external_function (RandomRange (0, 99)); end; // Button_LoadClick // procedure TForm1.call_external_function (value: integer); var test_1: function (n: integer): integer; cdecl; n: Int32; begin if Lib_Handle <> 0 then begin test_1 := dlsym (Lib_Handle, fun_name); if not assigned (test_1) then begin Memo1.Lines.Add ('Cannot create function: ' + fun_name); end else begin n := test_1 (value); Memo1.Lines.Add (Format ('%d = %s (%d)', [n, fun_name, value])); end; end; end; // call_external_function // end. 
+2
source

All Articles