Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

check for android compatibility #50

Closed
marc0olo opened this issue Aug 18, 2019 · 12 comments
Closed

check for android compatibility #50

marc0olo opened this issue Aug 18, 2019 · 12 comments
Labels
help wanted Extra attention is needed

Comments

@marc0olo
Copy link
Member

we need to check whether the SDK can work with Android.

there was a thread opened in the forum:

I think 3 dependencies could make problems:

@marc0olo marc0olo added the help wanted Extra attention is needed label Aug 18, 2019
@Defuera
Copy link

Defuera commented Aug 23, 2019

@marc0olo I can try to help you with this one. What end result do you expect? Should it be made available via maven/jitpack? Or just building a jar would be enough?

@marc0olo
Copy link
Member Author

marc0olo commented Aug 24, 2019

Hey @Defuera, thanks for asking. Honestly I haven’t thought about the android support in detail.
This issue contains all information about what could (currently) prevent android support.

Most important thing is to check whether KeyPair generation and tx signing is working when using the SDK in android.

I don’t know about how to use libsodium on android. That might be the first hurdle. We probably need to ship libsodium dependencies within the jar file and build a separate version of the SDK specifically for android.

I think the bouncycastle / spongycastle topic shouldn’t be a problem on newer android versions. But this is only an assumption.

argon2-jvm probably won’t work at all right now (see linked issue above). But since we use argon2 only for creation and import of keystore files that wouldn’t be necessary for the moment.

Building a separate jar for android would be enough if you get it working and if it is necessary to make some dependency changes specifically for android.
We could then adapt your build and integrate this in our build process to always ship 2 versions on a new release.

Let me know what you think about that topic.

@Defuera
Copy link

Defuera commented Sep 3, 2019

Hi @marc0olo, I've started to look into the issue.
As expected I'm getting an error for bouncy castle dependency:

java.lang.NoClassDefFoundError: org/bouncycastle/crypto/KeyGenerationParameters

Do you think solution would be to replace bouncycastle for spongycastle lib in aepp-sdk-java repository or do you think it can be done in a different way?

@marc0olo
Copy link
Member Author

marc0olo commented Sep 3, 2019

thanks for testing @Defuera. I hoped and thought that newer android versions don't have that classpath issue with bouncycastle anymore :-/

there might be another problem because spongycastle is not always up to date with bouncycastle and Ed25519 was introduced in one of the most recent bouncycastle versions. so the currently available spongycastle version probably won't support it.

I think someone made a tool to automatically build spongycastle or rename the bouncycastle packages. but this build then would not be available on a central repository.

but when testing this and building it locally you could verify whether this solves your problem.

@marc0olo
Copy link
Member Author

@marc0olo
Copy link
Member Author

marc0olo commented Jan 3, 2020

@Defuera it seems like I don't run into the issue you had. I had some other issues but I will hopefully be able to share an example in the near future

here the solution for the problem that made me crazy:

@marc0olo
Copy link
Member Author

marc0olo commented Jan 3, 2020

generating KeyPairs and working with the KeyPairService in general works without problems. I now wanted to get account data from the node and have the problem that the app on the emulator isn't able to open a socket connection:

W/GenericResultObject: Error mapping GenericResultObject to class com.kryptokrauts.aeternity.sdk.service.account.domain.AccountResult
    cause: com.kryptokrauts.aeternity.generated.ApiException: Failed to open a socket.
    root cause: null
D/AndroidRuntime: Shutting down VM

screenshot (debugging):
Bildschirmfoto 2020-01-03 um 23 27 42

@marc0olo
Copy link
Member Author

marc0olo commented Jan 4, 2020

this could be solved by just adding the required permission(s). I forgot to set ACCESS_NETWORK_STATE 🤦‍♂

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

@marc0olo
Copy link
Member Author

marc0olo commented Jan 4, 2020

so here my general observations and required steps to get the SDK working on an emulator in my android studio.

gradle android settings

With the following settings I got an app working on the emulator:

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.kryptokrauts.showcase"
        minSdkVersion 26
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    packagingOptions {
        exclude 'META-INF/DISCLAIMER'
        exclude 'META-INF/io.netty.versions.properties'
        exclude 'META-INF/INDEX.LIST'
    }
    packagingOptions {
        exclude 'lib/x86_64/darwin/libscrypt.dylib'
        exclude 'lib/x86_64/freebsd/libscrypt.so'
        exclude 'lib/x86_64/linux/libscrypt.so'
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

The packaging exclusions are required because we make use of bitcoinj, see: https://stackoverflow.com/questions/54263808/android-bitcoin-j-install-failed-no-matching-abis-failed-to-extract-native-lib

The packaging options are currently also necessary. Maybe we can fix that in the SDK build so that those files in META-INF won't be included there.

Unfortunately it is currently required to use API version 26 at minimum due to the usage of java.time.Instant in a dependency. In older API versions this class isn't existent.

permissions

Following android permissions need to be set:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

logging

For logging you should add following dependency:

implementation 'org.slf4j:slf4j-android:1.7.30'

aepp-showcase-android

To get started quickly and play around with the SDK we published a simple showcase application which allows to transfer funds from Alice to Bob and vice versa:

@marc0olo
Copy link
Member Author

marc0olo commented Jan 5, 2020

another error that currently appears is:

I/krauts.showcas: Rejecting re-init on previously-failed class java.lang.Class<io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider>: java.lang.NoClassDefFoundError: Failed resolution of: Ljavax/naming/directory/InitialDirContext;
        at void io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.<clinit>() (DefaultDnsServerAddressStreamProvider.java:68)
        at io.netty.resolver.dns.DnsServerAddresses io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.defaultAddresses() (DefaultDnsServerAddressStreamProvider.java:176)
        at void io.vertx.core.impl.resolver.DnsResolverProvider.<init>(io.vertx.core.impl.VertxImpl, io.vertx.core.dns.AddressResolverOptions) (DnsResolverProvider.java:92)
        at io.vertx.core.spi.resolver.ResolverProvider io.vertx.core.spi.resolver.ResolverProvider.factory(io.vertx.core.Vertx, io.vertx.core.dns.AddressResolverOptions) (ResolverProvider.java:39)
        at void io.vertx.core.impl.AddressResolver.<init>(io.vertx.core.Vertx, io.vertx.core.dns.AddressResolverOptions) (AddressResolver.java:75)
        at void io.vertx.core.impl.VertxImpl.<init>(io.vertx.core.VertxOptions, io.vertx.core.net.impl.transport.Transport) (VertxImpl.java:170)
        at io.vertx.core.impl.VertxImpl io.vertx.core.impl.VertxImpl.vertx(io.vertx.core.VertxOptions) (VertxImpl.java:93)
        at io.vertx.core.Vertx io.vertx.core.impl.VertxFactoryImpl.vertx(io.vertx.core.VertxOptions) (VertxFactoryImpl.java:38)
        at io.vertx.core.Vertx io.vertx.core.impl.VertxFactoryImpl.vertx() (VertxFactoryImpl.java:30)
        at io.vertx.core.Vertx io.vertx.core.Vertx.vertx() (Vertx.java:75)
        at com.kryptokrauts.aeternity.generated.ApiClient com.kryptokrauts.aeternity.sdk.service.ServiceConfiguration.getApiClient() (ServiceConfiguration.java:65)
        at void com.kryptokrauts.aeternity.sdk.service.aeternity.impl.AeternityService.<init>(com.kryptokrauts.aeternity.sdk.service.aeternity.AeternityServiceConfiguration) (AeternityService.java:54)
        at com.kryptokrauts.aeternity.sdk.service.aeternity.impl.AeternityService com.kryptokrauts.aeternity.sdk.service.aeternity.AeternityServiceFactory.getServiceWithConfig(com.kryptokrauts.aeternity.sdk.service.aeternity.AeternityServiceConfiguration) (AeternityServiceFactory.java:16)
        at java.lang.Object com.kryptokrauts.aeternity.sdk.service.aeternity.AeternityServiceFactory.getServiceWithConfig(com.kryptokrauts.aeternity.sdk.service.ServiceConfiguration) (AeternityServiceFactory.java:6)
        at java.lang.Object com.kryptokrauts.aeternity.sdk.service.AbstractServiceFactory.getService(com.kryptokrauts.aeternity.sdk.service.ServiceConfiguration) (AbstractServiceFactory.java:32)
        at void com.kryptokrauts.showcase.MainActivity.onCreate(android.os.Bundle) (MainActivity.kt:45)
        at void android.app.Activity.performCreate(android.os.Bundle, android.os.PersistableBundle) (Activity.java:7802)
        at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:7791)
        at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1299)
        at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:3245)
        at android.app.Activity android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.app.servertransaction.PendingTransactionActions, android.content.Intent) (ActivityThread.java:3409)
        at void android.app.servertransaction.LaunchActivityItem.execute(android.app.ClientTransactionHandler, android.os.IBinder, android.app.servertransaction.PendingTransactionActions) (LaunchActivityItem.java:83)
        at void android.app.servertransaction.TransactionExecutor.executeCallbacks(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:135)
        at void android.app.servertransaction.TransactionExecutor.execute(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:95)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:2016)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:107)
        at void android.os.Looper.loop() (Looper.java:214)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:7356)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:492)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:930)
I/krauts.showcas: Caused by: java.lang.ClassNotFoundException: Didn't find class "javax.naming.directory.InitialDirContext" on path: DexPathList[[zip file "/data/app/com.kryptokrauts.showcase--kb_OTeb4Rc88eUWEzvtIg==/base.apk"],nativeLibraryDirectories=[/data/app/com.kryptokrauts.showcase--kb_OTeb4Rc88eUWEzvtIg==/lib/x86, /system/lib, /system/product/lib]]
        at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:196)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
        at void io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.<clinit>() (DefaultDnsServerAddressStreamProvider.java:68)
        at io.netty.resolver.dns.DnsServerAddresses io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.defaultAddresses() (DefaultDnsServerAddressStreamProvider.java:176)
        at void io.vertx.core.impl.resolver.DnsResolverProvider.<init>(io.vertx.core.impl.VertxImpl, io.vertx.core.dns.AddressResolverOptions) (DnsResolverProvider.java:92)
        at io.vertx.core.spi.resolver.ResolverProvider io.vertx.core.spi.resolver.ResolverProvider.factory(io.vertx.core.Vertx, io.vertx.core.dns.AddressResolverOptions) (ResolverProvider.java:39)
        at void io.vertx.core.impl.AddressResolver.<init>(io.vertx.core.Vertx, io.vertx.core.dns.AddressResolverOptions) (AddressResolver.java:75)
        at void io.vertx.core.impl.VertxImpl.<init>(io.vertx.core.VertxOptions, io.vertx.core.net.impl.transport.Transport) (VertxImpl.java:170)
        at io.vertx.core.impl.VertxImpl io.vertx.core.impl.VertxImpl.vertx(io.vertx.core.VertxOptions) (VertxImpl.java:93)
        at io.vertx.core.Vertx io.vertx.core.impl.VertxFactoryImpl.vertx(io.vertx.core.VertxOptions) (VertxFactoryImpl.java:38)
        at io.vertx.core.Vertx io.vertx.core.impl.VertxFactoryImpl.vertx() (VertxFactoryImpl.java:30)
        at io.vertx.core.Vertx io.vertx.core.Vertx.vertx() (Vertx.java:75)
        at com.kryptokrauts.aeternity.generated.ApiClient com.kryptokrauts.aeternity.sdk.service.ServiceConfiguration.getApiClient() (ServiceConfiguration.java:65)
        at void com.kryptokrauts.aeternity.sdk.service.aeternity.impl.AeternityService.<init>(com.kryptokrauts.aeternity.sdk.service.aeternity.AeternityServiceConfiguration) (AeternityService.java:54)
        at com.kryptokrauts.aeternity.sdk.service.aeternity.impl.AeternityService com.kryptokrauts.aeternity.sdk.service.aeternity.AeternityServiceFactory.getServiceWithConfig(com.kryptokrauts.aeternity.sdk.service.aeternity.AeternityServiceConfiguration) (AeternityServiceFactory.java:16)
        at java.lang.Object com.kryptokrauts.aeternity.sdk.service.aeternity.AeternityServiceFactory.getServiceWithConfig(com.kryptokrauts.aeternity.sdk.service.ServiceConfiguration) (AeternityServiceFactory.java:6)
        at java.lang.Object com.kryptokrauts.aeternity.sdk.service.AbstractServiceFactory.getService(com.kryptokrauts.aeternity.sdk.service.ServiceConfiguration) (AbstractServiceFactory.java:32)
        at void com.kryptokrauts.showcase.MainActivity.onCreate(android.os.Bundle) (MainActivity.kt:45)
        at void android.app.Activity.performCreate(android.os.Bundle, android.os.PersistableBundle) (Activity.java:7802)
        at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:7791)
        at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1299)
        at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:3245)
        at android.app.Activity android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.app.servertransaction.PendingTransactionActions, android.content.Intent) (ActivityThread.java:3409)
        at void android.app.servertransaction.LaunchActivityItem.execute(android.app.ClientTransactionHandler, android.os.IBinder, android.app.servertransaction.PendingTransactionActions) (LaunchActivityItem.java:83)
        at void android.app.servertransaction.TransactionExecutor.executeCallbacks(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:135)
        at void android.app.servertransaction.TransactionExecutor.execute(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:95)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:2016)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:107)
        at void android.os.Looper.loop() (Looper.java:214)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:7356)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:492)
I/krauts.showcas:     at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:930)

more information about it here:

should be solved when upgrading netty-dependency to version 4.1.33.Final

@marc0olo
Copy link
Member Author

for argon2 there exists an Android/Kotlin binding:

@marc0olo
Copy link
Member Author

marc0olo commented Jan 9, 2022

closing this for now. given the provided config in https://github.com/kryptokrauts/aepp-showcase-android the SDK can generally be used in Android applications

@marc0olo marc0olo closed this as completed Jan 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants