My mobile game should be able to run an executable on an Android phone.
Making the executable: I installed Android NDK, created a JNI Folder and Android.mk file, and compiled an executable named servTest in C. Then, took the file from the libs ->arm64-v8a folder.
Code in C:
#include <stdio.h>
int returnFive() {
return 5;
}
int main() {
int result = returnFive();
printf("The result is: %d\n", result);
return 0;
}
Running the executable:
First, I parse the compiled file to the device if it wasn’t already in the past.
var desiredPermissions = FileAccess.UnixPermissionFlags.ReadOwner |
FileAccess.UnixPermissionFlags.WriteOwner |
FileAccess.UnixPermissionFlags.ExecuteOwner |
FileAccess.UnixPermissionFlags.ReadGroup |
FileAccess.UnixPermissionFlags.ReadOther;
if (OS.GetName() == "Android")
{
if (FileAccess.FileExists(iopiperMobileUserPath))
{
using var servFile = FileAccess.Open(iopiperMobileUserPath, FileAccess.ModeFlags.ReadWrite);
iopiper = servFile.GetPathAbsolute();
}
else
{
iopiper = CopyFromRes(iopiperMobileProjPath, iopiperMobileUserPath, desiredPermissions);
}
}
public static string CopyFromRes(string from, string to, FileAccess.UnixPermissionFlags UPFlags = 0)
{
using var fileFrom = FileAccess.Open(from, FileAccess.ModeFlags.Read);
GD.Print("CopyFromRes Open(from) status: ", FileAccess.GetOpenError());
using var fileTo = FileAccess.Open(to, FileAccess.ModeFlags.Write);
fileTo.StoreBuffer(fileFrom.GetBuffer((long)fileFrom.GetLength()));
string path = fileTo.GetPathAbsolute();
if (UPFlags != 0)
{
var error = FileAccess.SetUnixPermissions(to, UPFlags);
if (error != Error.Ok)
{
GD.Print("Error setting permissions: " + error);
}
}
return path;}
The file is copied successfully and then I try to run it using this method:
public void TestServer()
{
var output = new Godot.Collections.Array();
int exit = OS.Execute(iopiper, new string[] { engine }, output, true);
GD.Print("exit: " + exit);
GD.Print("output: " + output);
}
My “desiredPermissions” variable is the setup of UnixPermissionFlags for the file.
The file is written to the data/data folder of the game and the game process is supposed to own the binary. Is there a way to ensure/check it does?
I mean you could go onto the phone and look at the file if you start an adb shell ls -al <path to file>. or if there isnt a fileaccess function to get permission and group/owner you could use the ls command in an execute to get the status of the file and do some string search. But starting an adb shell would be easier.
Do you do adb root first? I guess I’m used to unlocked devices, so I don’t know if that is possible to use that command on a normal phone.
You could have the game execute that ls command and you could just print the output for visual inspection. My guess is that the data path is owned by the game, and that is why it can’t be accessed by an underprivileged shell. But if the game ls’d its own path it should work.
I guess you could use Android studio to inspect the filesystem as well.
I’m sorry for the necro, but after running to this same issue, and after tinkering with this for a while, it indeed is still possible to run OS.Execute, with the caveat that you will need to run it from within the /data/app//lib// folder, which is read only, meaning you can only run binaries which is built into your .apk /lib/ folder.
also, for it to be included, you must also rename the binary to lib_.so, in your case this will be libservtest.so.
Copy whatever binary you need to run on android to <ROOT>/android/build/libs/debug/<Target Arch>/<here> and <ROOT>/android/build/libs/release/<Target Arch>/<here>
Ex: <ROOT>/android/build/libs/debug/arm64-v8a/libservtest.so
You will need to build a small android plugin to get context.applicationinfo.nativelibrarydir which is where your .so file will be located. [Google Docs]. This is required because your apps /data/app folder location is kind of random. Example: /data/app/~~OU6KXmuTmy3jQVq-8BQncg==/com.example.xyz-L_C_k73pxnpzIdA1IF1Xrw==/lib/arm64
You’ll need to add android:extractNativeLibs="true" option to your project’s AndroidManifest.xml application setting. Example: <application android:label="@string/godot_project_name_string" android:allowBackup="false" android:icon="@mipmap/icon" android:appCategory="game" android:isGame="true" android:hasFragileUserData="false" android:requestLegacyExternalStorage="false" android:extractNativeLibs="true" tools:ignore="GoogleAppIndexingWarning" >
Now you can exec your binary using the path from context.applicationinfo.nativelibrarydir + “/” + “<binary name here?”, the full path might look something like /data/app/~~OU6KXmuTmy3jQVq-8BQncg==/com.example.xyz-L_C_k73pxnpzIdA1IF1Xrw==/lib/arm64/libservtest.so
Finally cracked this after 2 days because i needed to run Stockfish on android lol.
Wanted to post the resources i solved it with, but i only registered to reply to this post, so i’m limited to two links haha.