Connecting to Indy Pool from Android Client Using SDK and JavaWrapper

I have an Indy pool set up and can connect to that Indy pool via the Indy-CLI, the Python example and the NodeJS example but can’t connect to it via Android. I’ve started based on the example at https://github.com/ap050492/Hyperledger-Indy-Wallet---Android (apparently created by a commenter in another thread here about an Android Indy client: Using SDK and JavaWrapper in Android Studio ). I can’t connect it to that same Indy pool from an Android client, even though I’m using the same genesis file as the other working clients. I get the following error when trying to connect to the pool:

java.util.concurrent.ExecutionException: org.hyperledger.indy.sdk.ledger.TimeoutException: Timeout happens for ledger operation.

Do you know where I might find sample Android code that successfully connects to an indy pool?
Do you have any suggestions about how I might debug the connection?

How do your run your indy test-network? Do you use docker? The exception suggests that you can’t communicate with the indy-network instance, but your sdk seems to be running well, because you don’t get nullpointer exceptions.

Assuming your indy-sdk is working, here is a code (libindy 1.90), which works for me:
1 - You have to set up the environment:

    private void setEnvironmentAndLoadLibindyAndroid() throws IndyFacadeException {
            try {
                Os.setenv("EXTERNAL_STORAGE", environmentPath, true);
                File file = new
                        File(indyClientPath);
                if (!file.exists()) {
                    file.mkdir();
                }
                System.loadLibrary("indy");
            } catch (ErrnoException e) {
                throw new IndyFacadeException("could not set android environment", e);
            }

        }

The environment path is:

getExternalFilesDir(null).getAbsolutePath();

After you have loaded libindy, you need the genesis transactions and the pool configuration. Here are some methods, which work for me.

2 - Writing default genesis transactions:

 public void writeDefaultGenesisTransactions(String poolIPAdress) throws IndyFacadeException {
            String[] genesisTransactions = getDefaultGenesisTxn(poolIPAdress);
            writeGenesisTransactions(genesisTransactions, DEFAULT_GENESIS_FILE);
        }



public void writeGenesisTransactions(String[] genesisContent, String genesisFileName) throws IndyFacadeException {
        try {
            File genesisFile = new File(indyClientPath + "/" + genesisFileName);
            FileWriter fw = new FileWriter(genesisFile);
            for (String s : genesisContent) {
                fw.write(s);
                fw.write("\n");
            }
            fw.flush();
            fw.close();
        } catch (IOException e) {
            throw new IndyFacadeException("could not write default genesis transactions", e);
        }
    }


private String[] getDefaultGenesisTxn(String poolIPAddress) {
        String[] s = new String[]{String.format(
                "{\"reqSignature\":{},\"txn\":{\"data\":{\"data\":{\"alias\":\"Node1\",\"blskey\":\"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba\",\"blskey_pop\":\"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1\",\"client_ip\":\"%s\",\"client_port\":9702,\"node_ip\":\"%s\",\"node_port\":9701,\"services\":[\"VALIDATOR\"]},\"dest\":\"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv\"},\"metadata\":{\"from\":\"Th7MpTaRZVRYnPiabds81Y\"},\"type\":\"0\"},\"txnMetadata\":{\"seqNo\":1,\"txnId\":\"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62\"},\"ver\":\"1\"}",
                poolIPAddress, poolIPAddress),
                String.format(
                        "{\"reqSignature\":{},\"txn\":{\"data\":{\"data\":{\"alias\":\"Node2\",\"blskey\":\"37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk\",\"blskey_pop\":\"Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5\",\"client_ip\":\"%s\",\"client_port\":9704,\"node_ip\":\"%s\",\"node_port\":9703,\"services\":[\"VALIDATOR\"]},\"dest\":\"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb\"},\"metadata\":{\"from\":\"EbP4aYNeTHL6q385GuVpRV\"},\"type\":\"0\"},\"txnMetadata\":{\"seqNo\":2,\"txnId\":\"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc\"},\"ver\":\"1\"}\n",
                        poolIPAddress, poolIPAddress),
                String.format(
                        "{\"reqSignature\":{},\"txn\":{\"data\":{\"data\":{\"alias\":\"Node3\",\"blskey\":\"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5\",\"blskey_pop\":\"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh\",\"client_ip\":\"%s\",\"client_port\":9706,\"node_ip\":\"%s\",\"node_port\":9705,\"services\":[\"VALIDATOR\"]},\"dest\":\"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya\"},\"metadata\":{\"from\":\"4cU41vWW82ArfxJxHkzXPG\"},\"type\":\"0\"},\"txnMetadata\":{\"seqNo\":3,\"txnId\":\"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4\"},\"ver\":\"1\"}\n",
                        poolIPAddress, poolIPAddress),
                String.format(
                        "{\"reqSignature\":{},\"txn\":{\"data\":{\"data\":{\"alias\":\"Node4\",\"blskey\":\"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw\",\"blskey_pop\":\"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP\",\"client_ip\":\"%s\",\"client_port\":9708,\"node_ip\":\"%s\",\"node_port\":9707,\"services\":[\"VALIDATOR\"]},\"dest\":\"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA\"},\"metadata\":{\"from\":\"TWwCRQRZ2ZHMJFn9TzLp7W\"},\"type\":\"0\"},\"txnMetadata\":{\"seqNo\":4,\"txnId\":\"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008\"},\"ver\":\"1\"}",
                        poolIPAddress, poolIPAddress)};
        return s;

3 - After you have the genesis file, you can create the pool configuration:

public void createDefaultPool() throws IndyFacadeException {
        createPool("myIndyPool", "genesis.txn");

    }

public void createPool(String poolName, String genesisTransactionsFileName) throws IndyFacadeException {
        try {
            PoolJSONParameters.CreatePoolLedgerConfigJSONParameter createPoolLedgerConfigJSONParameter = new PoolJSONParameters.CreatePoolLedgerConfigJSONParameter(
                    indyClientPath + "/" + genesisTransactionsFileName);
            Pool.createPoolLedgerConfig(poolName, createPoolLedgerConfigJSONParameter.toJson()).get();
        } catch (InterruptedException | ExecutionException | IndyException e) {
            throw new IndyFacadeException("could not create pool", e);
        }
    }

Where the indy-client path is:

indyClientPath = environmentPath + "/" + ".indy_client";

Now you can try to open the default pool:

public void openDefaultPool() throws IndyFacadeException {
        openPool(myIndyPool, null);
    }

At this step you should be able to open the (default) pool and e.g. read a verkey from the ledger:

public String readVerKeyForDidFromLedger(String did) throws IndyFacadeException {
        try {
            String key = Did.keyForDid(pool, wallet, did).get();
            return key;
        } catch (InterruptedException | ExecutionException | IndyException e) {
            throw new IndyFacadeException("could not read verkey for did from ledger", e);
        }
    }

The above code is from a recent project of mine, but unfortunately I can’t publish the entire projekt. Please let me know, if you have any further trouble. Wenn Sie weiterhin Probleme haben, kann ich Ihnen ein Beispielprojekt auf github erstellen.

1 Like

Yes. I’ve had success from the other clients using the localhost and specific IP Docker setup at:

I started with Android emulator to locahost pool. Then I thought that the android emulator to localhost might be the problem so I moved the indy pool to an EC2 instance (still successful with the other non-Android clients).

I think that I’ll be able to try out your code tomorrow afternoon before the holiday. There seem to be a few differences in the code you’ve posted versus ap050492’s github and what I’ve tried. Hopefully that will help me connect.

Thanks for that code.

In my app I have also the following network security configuration:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

The xml lies in
app/src/main/res/xml

My permissions in the manifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
1 Like

Unfortunately, I’ve gone from bad to worse. I started by adding the following method:

public void openPool(String poolName)  {
    try {
        Pool.setProtocolVersion(2);
        Pool.openPoolLedger(poolName, null).get();
    } catch (InterruptedException | ExecutionException | IndyException e) {
        e.printStackTrace();
    }
}

The application started crashing in the openPool method (not throwing an exception) after I updated the app to 1.9.0. I used the pre-built binaries at https://repo.sovrin.org/android/libindy/stable/1.9.0/ and changed app level gradle to:
api 'org.hyperledger:indy:1.9.0'

The following is an excerpt from the crash log:
--------- beginning of crash
2019-12-27 09:07:09.931 6181-6246/com.example.myapplication A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 6246 (Thread-16)

2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #10 pc 0000ccaf /data/app/com.example.myapplication-2/lib/x86/libjnidispatch.so
2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #11 pc 00010d72 /data/app/com.example.myapplication-2/lib/x86/libjnidispatch.so

2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #13 pc 00332bd0 /data/app/com.example.myapplication-2/lib/x86/libindy.so (ZN63$LT$indy…utils…logger…LibindyLogger$u20$as$u20$log…Log$GT$3log17h89fe33b32c7fa735E+672)
2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #14 pc 010fe29a /data/app/com.example.myapplication-2/lib/x86/libindy.so (_ZN3log17__private_api_log17h2ef5c01549b81d6fE+138)
2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #15 pc 004387ef /data/app/com.example.myapplication-2/lib/x86/libindy.so (indy_get_current_error+111)
2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #16 pc 00011069 /data/app/com.example.myapplication-2/lib/x86/libjnidispatch.so (ffi_call_SYSV+25)
2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #17 pc 00010ce0 /data/app/com.example.myapplication-2/lib/x86/libjnidispatch.so (ffi_call+176)
2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #18 pc 000051e0 /data/app/com.example.myapplication-2/lib/x86/libjnidispatch.so
2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #19 pc 00007c1f /data/app/com.example.myapplication-2/lib/x86/libjnidispatch.so (Java_com_sun_jna_Native_invokeInt+63)
2019-12-27 09:07:09.942 6250-6250/? A/DEBUG: #20 pc 005c5792 /data/app/com.example.myapplication-2/oat/x86/base.odex (offset 0x55f000)

Did you use a particular libjnidispatch.so and/or force a particular NDK version? I see that some docs at https://hyperledger-indy.readthedocs.io/projects/sdk/en/latest/docs/build-guides/android-build.html suggest a particular version of libjnidispatch.so may be necessary for particular libindy versions. And I might have seen that a particular NDK might be required for particular libindy versions as well ( github . com/hyperledger/indy-sdk/pull/1901 ).

I had a similiar problem. As I remember the reason was the logger. I’ll make a sample android-project and upload it to github, so hopefully you will have a full running example.

1 Like

Thanks. It seems that it might be related to your prior post at Indy_get_current_error fails when using maven dependency

Here is a sample android project

The app creates a wallet and a sample pool. It then opens the pool and reads a verKey for the steward did from the ledger. The app consists of an empty activity and and the relevant events are written to logcat, e.g.:

2019-12-27 20:10:05.440 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: environment set successfully
2019-12-27 20:10:05.456 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: genesis file created successfully
2019-12-27 20:10:05.502 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: pool created successfully
2019-12-27 20:10:29.700 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: wallet opened successfully
2019-12-27 20:10:29.749 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: Steward-DID is Th7MpTaRZVRYnPiabds81Y
2019-12-27 20:10:29.885 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: pool opened successfully
2019-12-27 20:10:29.894 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: read verKey from ledger successfully FYmoFw55GeQH7SRFa37dkx1d2dZ3zUF8ckg7wmL7ofN4
2019-12-27 20:10:29.909 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: pool closed successfully
2019-12-27 20:10:29.916 17048-17048/com.example.myapplication D/SampleWalletTest: onCreate: wallet closed successfully

The onCreate of the MainActivity contains all the code. See the comments there. If you try on your machine, please dont forget to change the IP for the genesis file. Otherwise you will run into an IndyException.

The java wrapper in 1.90 had a problem with execptions especially in android. According to IS-1252 the problem lies in the logger. Compare also my latest post in:

I solved the problem by changing the logger to the default one. The app has been tested with libindy 1.90 on a samsung galaxy s7 and the latest docker image (27.12.2019). Libindy and the (modfied) java wrapper are included in the github project, so you can use them out of the box.

Please let me know, if you could connect to the pool :slight_smile:

1 Like

I was able to connect to a pool. I commented out line 38 ( indyFacade.deletePool(IndyFacade.DEFAULT_POOL_NAME); ) in https://github. com/jSh4rk/SampleIndyWallet/blob/master/app/src/main/java/com/example/sampleindywallet/MainActivity.java
on an initial run on an API 24 emulator. as it produced an error on the initial run (error: java.lang.NoSuchMethodError: No virtual method toPath()Ljava/nio/file/Path; in class Ljava/io/File; or its super classes (declaration of 'java.io.File' appears in /system/framework/core-oj.jar) ). But uninstalling and running the app succeeds from environment setup through wallet closing. On an API 27 emulator, it worked fine. The file deletion is trivial and solvable compared to connecting to the Indy pool.

Many thanks for the working sample code.

I have a one question.

I’m trying to connect the Android app to the indy pool, but I see error( Timeout happens for ledger operation)
The test environment installed Ubuntu in the Virtual Box of Windows 10 and then a docker in Ubuntu.
For example, ip of Ubuntu is 1.1.1.1 and ip of the docker is 2.2.2.2.
I know the port was mapped by the option Docker Run -itd 9701-9708:9701-9708.
So when using the Android app, designated the IP to be accessed as Ubuntu’s IP, but could not open the pool.
The Genesis file was also converted to Ubuntu’s ip.
What’s the problem?