From 6811bdf47a5def52dd364fa67158484d047a6a62 Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Mon, 29 May 2023 01:51:16 +0200 Subject: [PATCH] lib: initial support for InProcess provider --- .../molly/monero/demo/data/MoneroSdkClient.kt | 8 ++-- .../aidl/im/molly/monero/IStorageAdapter.aidl | 2 +- .../src/main/kotlin/im/molly/monero/Binder.kt | 10 +++++ .../kotlin/im/molly/monero/StorageAdapter.kt | 32 ++++++++-------- .../kotlin/im/molly/monero/WalletDataStore.kt | 8 ++-- .../kotlin/im/molly/monero/WalletNative.kt | 37 +++++++++++-------- .../kotlin/im/molly/monero/WalletService.kt | 2 + 7 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 lib/android/src/main/kotlin/im/molly/monero/Binder.kt diff --git a/demo/android/src/main/kotlin/im/molly/monero/demo/data/MoneroSdkClient.kt b/demo/android/src/main/kotlin/im/molly/monero/demo/data/MoneroSdkClient.kt index ac85662..064f5aa 100644 --- a/demo/android/src/main/kotlin/im/molly/monero/demo/data/MoneroSdkClient.kt +++ b/demo/android/src/main/kotlin/im/molly/monero/demo/data/MoneroSdkClient.kt @@ -8,9 +8,9 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import okhttp3.OkHttpClient import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream import java.io.IOException +import java.io.InputStream +import java.io.OutputStream class MoneroSdkClient(private val context: Context) { @@ -86,7 +86,7 @@ class MoneroSdkClient(private val context: Context) { throw IOException("Cannot create wallet data directory: ${walletDataDir.path}") } - override suspend fun write(writer: (FileOutputStream) -> Unit) { + override suspend fun write(writer: (OutputStream) -> Unit) { val output = file.startWrite() try { writer(output) @@ -97,7 +97,7 @@ class MoneroSdkClient(private val context: Context) { } } - override suspend fun read(): FileInputStream { + override suspend fun read(): InputStream { return file.openRead() } } diff --git a/lib/android/src/main/aidl/im/molly/monero/IStorageAdapter.aidl b/lib/android/src/main/aidl/im/molly/monero/IStorageAdapter.aidl index bc86f48..2e0da0f 100644 --- a/lib/android/src/main/aidl/im/molly/monero/IStorageAdapter.aidl +++ b/lib/android/src/main/aidl/im/molly/monero/IStorageAdapter.aidl @@ -2,5 +2,5 @@ package im.molly.monero; interface IStorageAdapter { boolean writeAsync(in ParcelFileDescriptor pfd); - void readAsync(in ParcelFileDescriptor pfd); + oneway void readAsync(in ParcelFileDescriptor pfd); } diff --git a/lib/android/src/main/kotlin/im/molly/monero/Binder.kt b/lib/android/src/main/kotlin/im/molly/monero/Binder.kt new file mode 100644 index 0000000..27f2657 --- /dev/null +++ b/lib/android/src/main/kotlin/im/molly/monero/Binder.kt @@ -0,0 +1,10 @@ +package im.molly.monero + +import android.os.IInterface + +/** + * Returns whether this interface is in a remote process. + */ +fun IInterface.isRemote(): Boolean { + return asBinder() !== this +} diff --git a/lib/android/src/main/kotlin/im/molly/monero/StorageAdapter.kt b/lib/android/src/main/kotlin/im/molly/monero/StorageAdapter.kt index bee9f79..0041295 100644 --- a/lib/android/src/main/kotlin/im/molly/monero/StorageAdapter.kt +++ b/lib/android/src/main/kotlin/im/molly/monero/StorageAdapter.kt @@ -17,30 +17,28 @@ internal class StorageAdapter(var dataStore: WalletDataStore?) : IStorageAdapter override fun writeAsync(pfd: ParcelFileDescriptor?): Boolean { requireNotNull(pfd) val localDataStore = dataStore - if (localDataStore == null) { - pfd.close() - return false - } - val inputStream = ParcelFileDescriptor.AutoCloseInputStream(pfd) - ioStorageScope.launch { - mutex.withLock { - localDataStore.write { output -> - inputStream.copyTo(output) + return if (localDataStore != null) { + val inputStream = ParcelFileDescriptor.AutoCloseInputStream(pfd) + ioStorageScope.launch { + mutex.withLock { + localDataStore.write { output -> + inputStream.copyTo(output) + } } - } - }.invokeOnCompletion { inputStream.close() } - return true + }.invokeOnCompletion { inputStream.close() } + true + } else { + pfd.close() + false + } } override fun readAsync(pfd: ParcelFileDescriptor?) { requireNotNull(pfd) - val localDataStore = dataStore - if (localDataStore == null) { - pfd.close() - throw IllegalArgumentException("WalletDataStore cannot be null") - } val outputStream = ParcelFileDescriptor.AutoCloseOutputStream(pfd) ioStorageScope.launch { + val localDataStore = + dataStore ?: throw IllegalArgumentException("WalletDataStore cannot be null") mutex.withLock { localDataStore.read().use { input -> input.copyTo(outputStream) diff --git a/lib/android/src/main/kotlin/im/molly/monero/WalletDataStore.kt b/lib/android/src/main/kotlin/im/molly/monero/WalletDataStore.kt index 44b5ebd..2ab2279 100644 --- a/lib/android/src/main/kotlin/im/molly/monero/WalletDataStore.kt +++ b/lib/android/src/main/kotlin/im/molly/monero/WalletDataStore.kt @@ -1,9 +1,9 @@ package im.molly.monero -import java.io.FileInputStream -import java.io.FileOutputStream +import java.io.InputStream +import java.io.OutputStream interface WalletDataStore { - suspend fun write(writer: (FileOutputStream) -> Unit) - suspend fun read(): FileInputStream + suspend fun write(writer: (OutputStream) -> Unit) + suspend fun read(): InputStream } diff --git a/lib/android/src/main/kotlin/im/molly/monero/WalletNative.kt b/lib/android/src/main/kotlin/im/molly/monero/WalletNative.kt index 249eeeb..abed699 100644 --- a/lib/android/src/main/kotlin/im/molly/monero/WalletNative.kt +++ b/lib/android/src/main/kotlin/im/molly/monero/WalletNative.kt @@ -11,7 +11,7 @@ import kotlin.coroutines.CoroutineContext class WalletNative private constructor( networkId: Int, - private val storageAdapter: IStorageAdapter?, + private val storageAdapter: IStorageAdapter, private val remoteNodeClient: IRemoteNodeClient?, private val scope: CoroutineScope, private val ioDispatcher: CoroutineDispatcher, @@ -21,7 +21,7 @@ class WalletNative private constructor( // TODO: Find better name because this is a local synchronization wallet, not a full node wallet suspend fun fullNode( networkId: Int, - storageAdapter: IStorageAdapter? = null, + storageAdapter: IStorageAdapter, remoteNodeClient: IRemoteNodeClient? = null, secretSpendKey: SecretKey? = null, restorePoint: Long? = null, @@ -59,13 +59,17 @@ class WalletNative private constructor( private val handle: Long = nativeCreate(networkId) private suspend fun tryWriteState(): Boolean { - requireNotNull(storageAdapter) return withContext(ioDispatcher) { - val pipe = ParcelFileDescriptor.createReliablePipe() - pipe[1].use { writeSide -> - val storageIsReady = storageAdapter.writeAsync(pipe[0]) + val pipe = ParcelFileDescriptor.createPipe() + val readFd = pipe[0] + val writeFd = pipe[1] + val storageIsReady = storageAdapter.writeAsync(readFd) + if (storageAdapter.isRemote()) { + readFd.close() + } + writeFd.use { if (storageIsReady) { - val result = nativeSave(handle, writeSide.fd) + val result = nativeSave(handle, it.fd) if (!result) { logger.e("Wallet data serialization failed") } @@ -79,15 +83,18 @@ class WalletNative private constructor( } private suspend fun readState() { - requireNotNull(storageAdapter) - return withContext(ioDispatcher) { - val pipe = ParcelFileDescriptor.createReliablePipe() - pipe[0].use { readSide -> - pipe[1].use { writeSide -> - storageAdapter.readAsync(writeSide) + withContext(ioDispatcher) { + val pipe = ParcelFileDescriptor.createPipe() + val readFd = pipe[0] + val writeFd = pipe[1] + storageAdapter.readAsync(writeFd) + if (storageAdapter.isRemote()) { + writeFd.close() + } + readFd.use { + if (!nativeLoad(handle, it.fd)) { + throw IllegalStateException("Wallet data deserialization failed") } - val result = nativeLoad(handle, readSide.fd) - check(result) { "Wallet data deserialization failed" } } } } diff --git a/lib/android/src/main/kotlin/im/molly/monero/WalletService.kt b/lib/android/src/main/kotlin/im/molly/monero/WalletService.kt index 3532e4c..3ba6a65 100644 --- a/lib/android/src/main/kotlin/im/molly/monero/WalletService.kt +++ b/lib/android/src/main/kotlin/im/molly/monero/WalletService.kt @@ -78,6 +78,7 @@ internal class WalletServiceImpl( callback: IWalletServiceCallbacks?, ) { requireNotNull(config) + requireNotNull(storage) serviceScope.launch { val wallet = WalletNative.fullNode( networkId = config.networkId, @@ -97,6 +98,7 @@ internal class WalletServiceImpl( restorePoint: Long? = null, ): IWallet { requireNotNull(config) + requireNotNull(storage) requireNotNull(secretSpendKey) return WalletNative.fullNode( networkId = config.networkId,