/*
* This file is part of GNU Taler
* (C) 2020 Taler Systems S.A.
*
* GNU Taler is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3, or (at your option) any later version.
*
* GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* GNU Taler; see the file COPYING. If not, see
*/
package net.taler.wallet.backend
import android.app.Application
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Handler
import android.os.IBinder
import android.os.Message
import android.os.Messenger
import android.util.Log
import android.util.SparseArray
import org.json.JSONObject
import java.lang.ref.WeakReference
import java.util.*
class WalletBackendApi(
private val app: Application,
private val onConnected: (() -> Unit),
private val notificationHandler: ((payload: JSONObject) -> Unit)
) {
private var walletBackendMessenger: Messenger? = null
private val queuedMessages = LinkedList()
private val handlers = SparseArray<(isError: Boolean, message: JSONObject) -> Unit>()
private var nextRequestID = 1
private val walletBackendConn = object : ServiceConnection {
override fun onServiceDisconnected(p0: ComponentName?) {
Log.w(TAG, "wallet backend service disconnected (crash?)")
walletBackendMessenger = null
}
override fun onServiceConnected(componentName: ComponentName?, binder: IBinder?) {
Log.i(TAG, "connected to wallet backend service")
val bm = Messenger(binder)
walletBackendMessenger = bm
pumpQueue(bm)
val msg = Message.obtain(null, WalletBackendService.MSG_SUBSCRIBE_NOTIFY)
msg.replyTo = incomingMessenger
bm.send(msg)
onConnected.invoke()
}
}
private class IncomingHandler(strongApi: WalletBackendApi) : Handler() {
private val weakApi = WeakReference(strongApi)
override fun handleMessage(msg: Message) {
val api = weakApi.get() ?: return
when (msg.what) {
WalletBackendService.MSG_REPLY -> {
val requestID = msg.data.getInt("requestID", 0)
val operation = msg.data.getString("operation", "??")
Log.i(TAG, "got reply for operation $operation ($requestID)")
val h = api.handlers.get(requestID)
if (h == null) {
Log.e(TAG, "request ID not associated with a handler")
return
}
val response = msg.data.getString("response")
if (response == null) {
Log.e(TAG, "response did not contain response payload")
return
}
val isError = msg.data.getBoolean("isError")
val json = JSONObject(response)
h(isError, json)
}
WalletBackendService.MSG_NOTIFY -> {
val payloadStr = msg.data.getString("payload")
if (payloadStr == null) {
Log.e(TAG, "Notification had no payload: $msg")
} else {
val payload = JSONObject(payloadStr)
api.notificationHandler.invoke(payload)
}
}
}
}
}
private val incomingMessenger = Messenger(IncomingHandler(this))
init {
Intent(app, WalletBackendService::class.java).also { intent ->
app.bindService(intent, walletBackendConn, Context.BIND_AUTO_CREATE)
}
}
private fun pumpQueue(bm: Messenger) {
while (true) {
val msg = queuedMessages.pollFirst() ?: return
bm.send(msg)
}
}
fun sendRequest(
operation: String,
args: JSONObject?,
onResponse: (isError: Boolean, message: JSONObject) -> Unit = { _, _ -> }
) {
val requestID = nextRequestID++
Log.i(TAG, "sending request for operation $operation ($requestID)")
val msg = Message.obtain(null, WalletBackendService.MSG_COMMAND)
handlers.put(requestID, onResponse)
msg.replyTo = incomingMessenger
val data = msg.data
data.putString("operation", operation)
data.putInt("requestID", requestID)
if (args != null) {
data.putString("args", args.toString())
}
val bm = walletBackendMessenger
if (bm != null) {
bm.send(msg)
} else {
queuedMessages.add(msg)
}
}
fun destroy() {
// FIXME: implement this!
}
companion object {
const val TAG = "WalletBackendApi"
}
}