All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.pylons.ipc.DroidIpcWire.kt Maven / Gradle / Ivy

package tech.pylons.ipc

import com.beust.klaxon.Json
import kotlin.random.Random
import tech.pylons.lib.klaxon
import java.util.*

abstract class DroidIpcWire {


    protected abstract fun writeString(s: String)

    protected abstract fun readString(): String?

    companion object {
        private const val HANDSHAKE_MAGIC = "__PYLONS_WALLET_SERVER"
        private const val HANDSHAKE_REPLY_MAGIC = "__PYLONS_WALLET_CLIENT"

        var implementation: DroidIpcWire? = null

        //according to the libpylons, these values to be in IPCLayer
        var clientId: Int = Random.nextInt()
        var walletId: Int = 0
        var messageId: Int = 0

        data class HandshakeMsg(
            @property:[Json(name = "MAGIC")]
            var MAGIC: String,
            @property:[Json(name = "pid")]
            var pid: String,
            @property:[Json(name = "walletId")]
            var walletId: String
        )

        data class HandshakeReplyMsg(
            @property:[Json(name = "MAGIC_REPLY")]
            var MAGIC_REPLY: String = "",
            @property:[Json(name = "clientId")]
            var clientId: String = "",
            @property:[Json(name = "appName")]
            var appName: String = "",
            @property:[Json(name = "appPkgName")]
            var appPkgName: String = ""
        )

        /**
         * establishConnection(appName: String, appClassName:String, callback:(Boolean) -> Unit)
         * called after ipc connection first establishes.
         * initiates Handshake with wallet
         *
         * @param   appName: String - caller app's Display Name
         * @param   appPkgName: String - app's Package Name
         * @param   callback: func handler - bring the handshake result to caller
         */
        fun establishConnection(appName: String, appPkgName: String, callback: (Boolean) -> Unit) {
            val ret = doHandshake(appName, appPkgName)
            callback(ret)
        }

        /**
         * DoHandshake
         * handshake with apps and wallet when establishing ipc connection
         * apps send cliendId, appName, appClassName to wallet
         * wallet respond with walletId
         *
         * return true if handshake succeed, or false when fails
         */
        private fun doHandshake(appName: String, appPkgName: String): Boolean {
            try {
                val msg = HandshakeReplyMsg(
                    MAGIC_REPLY = HANDSHAKE_REPLY_MAGIC,
                    clientId = clientId.toString(),
                    appName = appName,
                    appPkgName = appPkgName
                )
                val strMsg = klaxon.toJsonString(msg)
                writeMessage("$HANDSHAKE_REPLY_MAGIC$strMsg")
                println("IPC Reply HandShake Msg sent $strMsg")

                val retMsg = readMessage()
                if (retMsg != null) {
                    val serverMsg = klaxon.parse(retMsg.removePrefix(HANDSHAKE_MAGIC))
                    if (serverMsg?.MAGIC == HANDSHAKE_MAGIC) {
                        walletId = serverMsg.walletId.toInt()
                        return true
                    }
                }
            } catch (e: Error) {
                println("DoHandshake error: $e")
            }

            return false
        }

        fun makeRequestMessage(message: Message): String {
            val jsonString = klaxon.toJsonString(message)
            println("request msg: $jsonString")
            val msgJson =
                Base64.getEncoder().encodeToString(
                    jsonString.toByteArray(Charsets.US_ASCII)
                )
            return """{"type":"${message.javaClass.simpleName}", "msg":"$msgJson", "messageId":${messageId++}, "clientId":$clientId, "walletId": $walletId}"""
        }

        fun writeMessage(s: String) {
            //wallet handshake not initiated
            implementation!!.writeString(s)
        }

        fun readMessage(): String? {
            var ret: String?
            var elapsedMillis = 0L
            while (true) {
                ret = implementation!!.readString()
                //should also check whether ret is ""
                if (ret == null || ret == "") {
                    Thread.sleep(100)
                    elapsedMillis += 100L
                } else {
                    //if handshake msg, then handle handshake, else continue flow
                    return ret
                }

                // pls think on it. readMessage only takes 10 sec?
                // --> If it didn't respond for over 1 min, we could regard it has a problem.
                if (elapsedMillis > 60 * 1000L) { // no response for 60 secs
                    println("Error: Talking to wallet is not available at the moment!")
                    return null
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy