How to add a shared library (.so) in android project

From Epic Wiki
Jump to: navigation, search

Template:Rating Template:Review

Author Syntopia. Thanks to Chris Babcock for the info.

For example, to add support for OpenSSL.

The latest compiled version of the library you can find in github (or compiled by hand), take this for example:

https://github.com/ph4r05/android-openssl

openssl-1.0.2j

Select the desired architecture, for this example we will take "arch-armeabi-v7a". After downloading, create a folder "/armv7" in the source folder "C:\Users\Vadim\Documents\Unreal Projects\AndroidOpenSSL\Source\AndroidOpenSSL" in my case. And move the "libcrypto.so" there in "armv7". Next open the file .Build.cs it should look something like this, here I just add the library to work with OpenSSL in the Windows.

Headers are in "AndroidOpenSSL/ThirdParty/OpenSSL/Includes" connect them like this:

string IncludesPath = Path.Combine(ThirdPartyPath, "OpenSSL", "Includes");
PublicIncludePaths.Add(IncludesPath);

For android target set the path to the library and APL (AndroidPluginLanguage).

using UnrealBuildTool;
using System.IO;
using System;  //Console.WriteLine("");

public class AndroidOpenSSL : ModuleRules
{
    private string ModulePath
    {
        get { return ModuleDirectory; }
    }

    private string ThirdPartyPath
    {
        get { return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/")); }
    }

    public AndroidOpenSSL(TargetInfo Target)
    {
	PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

	PrivateDependencyModuleNames.AddRange(new string[] {  });

        string IncludesPath = Path.Combine(ThirdPartyPath, "OpenSSL", "Includes");
        PublicIncludePaths.Add(IncludesPath);

        if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
        {
            string LibrariesPath = Path.Combine(ThirdPartyPath, "OpenSSL", "Libraries");

            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "libeay32.lib"));
            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "ssleay32.lib"));


        }

        if (Target.Platform == UnrealTargetPlatform.Android)
        {
            string BuildPath = Utils.MakePathRelativeTo(ModuleDirectory, BuildConfiguration.RelativeEnginePath);

            AdditionalPropertiesForReceipt.Add(new ReceiptProperty("AndroidPlugin", Path.Combine(BuildPath, "My_APL_armv7.xml")));

            PublicAdditionalLibraries.Add(BuildPath + "/armv7/libcrypto.so");
        }
    }
}

Create in the same place file "My_APL_armv7.xml". A great example can be found here:

https://github.com/EpicGames/UnrealEngine/blob/4.10/Engine/Source/Programs/UnrealBuildTool/Android/AndroidPluginLanguage.cs

The contents will look like this:

<?xml version="1.0" encoding="utf-8"?>
<!-- steps to add to build additions -->
<root xmlns:android="http://schemas.android.com/apk/res/android">
	<!-- init section is always evaluated once per architecture -->
	<init>
	    <setBool result="bSupported" value="false"/>
	        <isArch arch="armeabi-v7a">
	            <setBool result="bSupported" value="true"/>
	        </isArch>
	</init>

	<!-- optional files or directories to copy to Intermediate/Android/APK -->
	<resourceCopies>
	    <isArch arch="armeabi-v7a">
	        <copyFile src="$S(PluginDir)/armv7/libcrypto.so"
	                  dst="$S(BuildDir)/libs/armeabi-v7a/libcrypto.so" />
		</isArch>
	</resourceCopies>

	<!-- optional libraries to load in GameActivity.java before libUE4.so -->
	<soLoadLibrary>
	    <if condition="bSupported">
	        <true>
		    <loadLibrary name="myso" failmsg="Failed to load myso library" />
		</true>
	    </if>
	</soLoadLibrary>
</root>

That's all you can packing. If everything is fine, we'll see it in the log:

UATHelper: Packaging (Android (ETC2)): UnrealBuildTool: File C:\Users\Vadim\Documents\Unreal Projects\AndroidOpenSSL\Source\AndroidOpenSSL/armv7/libcrypto.so copied to C:\Users\Vadim\Documents\Unreal Projects\AndroidOpenSSL\Intermediate/Android/APK/libs/armeabi-v7a/libcrypto.so

Possible errors (for OpenSSL):

After installing the application on androyd I can not run it, looked into the logcat, there was such an error:

E/AndroidRuntime( 1574): java.lang.UnsatisfiedLinkError: dlopen failed: could not load library "libcrypto.so.1.0.0" needed by "libUE4.so"; caused by library "libcrypto.so.1.0.0" not found

If you rename the .apk to .zip open and go to the "\lib" you will see that there is not "libcrypto.so.1.0.0" only "libcrypto.so". Version number sewn somewhere in .so, a simple rename will not help. We need to recompile OpenSSL with the required flag, or change something in .so using rpl:

rpl -R -e .so.1.0.0 "_1_0_0.so" /path/to/libcrypto.so

More details in the answer: http://stackoverflow.com/questions/33103867/unsatisfiedlinkerror-libcrypto-so-1-0-0-not-found

An example of calculating the hash SHA256.

I created the HUD for this:

// OpenSSL tests
#include <openssl/evp.h>
#include <sstream>
#include <iomanip>

void ADebugHUD::DrawHUD()
{
	Super::DrawHUD();

	FString hashTest = "Hash test (sha256): " + GetSHA256_s("Baron", strlen("Baron"));

	DrawText(hashTest, FColor::White, 50, 50, HUDFont);
}

FString ADebugHUD::GetSHA256_s(const void * data, size_t data_len)
{
	EVP_MD_CTX mdctx;
	unsigned char md_value[EVP_MAX_MD_SIZE];
	unsigned int md_len;

	EVP_DigestInit(&mdctx, EVP_sha256());
	EVP_DigestUpdate(&mdctx, data, (size_t)data_len);
	EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
	EVP_MD_CTX_cleanup(&mdctx);

	std::stringstream s;
	s.fill('0');

	for (size_t i = 0; i < md_len; ++i)
		s << std::setw(2) << std::hex << (unsigned short)md_value[i];

	return s.str().c_str();
}

Output:

45476.JPG