taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

commit f1d504e6bba93697091a11bd0a44cc810c4787c3
parent b1d1144e11ac869257ddf863abff2bc4494581f7
Author: Iván Ávalos <avalos@disroot.org>
Date:   Wed, 21 May 2025 19:04:54 +0200

[wallet] fix NFC from Android to Android and clear p2p URI

bug 0009986
bug 0009985

Diffstat:
Mcashier/src/main/java/net/taler/cashier/MainActivity.kt | 7+++++++
Mmerchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt | 7+++++++
Mtaler-kotlin-android/src/main/java/net/taler/lib/android/TalerNfcService.kt | 53+++++++++++++++++++++++++++++++++++++++++------------
Mwallet/src/main/java/net/taler/wallet/MainActivity.kt | 3+++
Mwallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt | 5+++++
Mwallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt | 2+-
Mwallet/src/main/res/xml/apduservice.xml | 2+-
7 files changed, 65 insertions(+), 14 deletions(-)

diff --git a/cashier/src/main/java/net/taler/cashier/MainActivity.kt b/cashier/src/main/java/net/taler/cashier/MainActivity.kt @@ -44,6 +44,8 @@ class MainActivity : AppCompatActivity() { val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment nav = navHostFragment.navController + + TalerNfcService.startService(this) } override fun onStart() { @@ -63,6 +65,11 @@ class MainActivity : AppCompatActivity() { TalerNfcService.unsetDefaultHandler(this) } + override fun onDestroy() { + super.onDestroy() + TalerNfcService.stopService(this) + } + @Deprecated("Deprecated in Java") override fun onBackPressed() { if (!configManager.hasConfig() && nav.currentDestination?.id == R.id.configFragment) { diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt @@ -58,6 +58,8 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener { ui = ActivityMainBinding.inflate(layoutInflater) setContentView(ui.root) + TalerNfcService.startService(this) + model.paymentManager.payment.observe(this) { payment -> payment?.talerPayUri?.let { TalerNfcService.setUri(this, it) @@ -109,6 +111,11 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener { TalerNfcService.unsetDefaultHandler(this) } + override fun onDestroy() { + super.onDestroy() + TalerNfcService.stopService(this) + } + override fun onNavigationItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.nav_order -> nav.navigate(R.id.action_global_order) diff --git a/taler-kotlin-android/src/main/java/net/taler/lib/android/TalerNfcService.kt b/taler-kotlin-android/src/main/java/net/taler/lib/android/TalerNfcService.kt @@ -18,9 +18,11 @@ package net.taler.lib.android import android.app.Activity import android.app.Service +import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.nfc.NdefMessage import android.nfc.NdefRecord import android.nfc.NfcAdapter.getDefaultAdapter @@ -28,6 +30,7 @@ import android.nfc.cardemulation.CardEmulation import android.nfc.cardemulation.HostApduService import android.os.Bundle import android.util.Log +import androidx.localbroadcastmanager.content.LocalBroadcastManager import java.math.BigInteger class TalerNfcService : HostApduService() { @@ -52,11 +55,14 @@ class TalerNfcService : HostApduService() { private var readCapabilityContainerCheck = false - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - intent?.getStringExtra("uri")?.let { uri = it } - - Log.d(TAG, "onStartCommand() | URI: $uri") + private val broadcastReceiver = object: BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + intent?.getStringExtra("uri").let { uri = it } + Log.d(TAG, "onReceive() | URI: $uri") + } + } + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { return Service.START_STICKY } @@ -201,14 +207,24 @@ class TalerNfcService : HostApduService() { return fillByteArrayToFixedDimension(filledArray, fixedSize) } + override fun onCreate() { + super.onCreate() + LocalBroadcastManager.getInstance(this).registerReceiver( + broadcastReceiver, + IntentFilter(SET_URI_INTENT), + ) + } + override fun onDestroy() { super.onDestroy() Log.d(TAG, "onDestroy() NFC service") + LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver) uri = null } companion object { private const val TAG = "taler-wallet-hce" + const val SET_URI_INTENT = "taler-wallet-set-url" private val APDU_SELECT = byteArrayOf( 0x00.toByte(), // CLA - Class - Class of instruction @@ -245,14 +261,14 @@ class TalerNfcService : HostApduService() { ) private val READ_CAPABILITY_CONTAINER_RESPONSE = byteArrayOf( - 0x00.toByte(), 0x11.toByte(), // CCLEN length of the CC file + 0x00.toByte(), 0x0F.toByte(), // CCLEN length of the CC file 0x20.toByte(), // Mapping Version 2.0 - 0xFF.toByte(), 0xFF.toByte(), // MLe maximum - 0xFF.toByte(), 0xFF.toByte(), // MLc maximum + 0x00.toByte(), 0x3B.toByte(), // MLe maximum + 0x00.toByte(), 0x34.toByte(), // MLc maximum 0x04.toByte(), // T field of the NDEF File Control TLV 0x06.toByte(), // L field of the NDEF File Control TLV 0xE1.toByte(), 0x04.toByte(), // File Identifier of NDEF file - 0xFF.toByte(), 0xFE.toByte(), // Maximum NDEF file size of 65534 bytes + 0x00.toByte(), 0xFE.toByte(), // Maximum NDEF file size of 65534 bytes 0x00.toByte(), // Read access without any security 0xFF.toByte(), // Write access without any security 0x90.toByte(), 0x00.toByte(), // A_OKAY @@ -318,17 +334,30 @@ class TalerNfcService : HostApduService() { emulation.unsetPreferredService(activity) } + fun startService(activity: Activity) { + val intent = Intent(activity, TalerNfcService::class.java) + activity.startService(intent) + } + + fun stopService(activity: Activity) { + val intent = Intent(activity, TalerNfcService::class.java) + activity.stopService(intent) + } + fun setUri(activity: Activity, uri: String) { if (!hasNfc(activity)) return - val intent = Intent(activity, TalerNfcService::class.java) + val broadcastManager = LocalBroadcastManager.getInstance(activity) + val intent = Intent(SET_URI_INTENT) intent.putExtra("uri", uri) - activity.startService(intent) + broadcastManager.sendBroadcast(intent) } fun clearUri(activity: Activity) { if (!hasNfc(activity)) return - val intent = Intent(activity, TalerNfcService::class.java) - activity.stopService(intent) + val broadcastManager = LocalBroadcastManager.getInstance(activity) + val intent = Intent(SET_URI_INTENT) + intent.putExtra("uri", null as String?) + broadcastManager.sendBroadcast(intent) } } } \ No newline at end of file diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/MainActivity.kt @@ -78,6 +78,8 @@ class MainActivity : AppCompatActivity(), OnPreferenceStartFragmentCallback { setContentView(ui.root) setupInsets() + TalerNfcService.startService(this) + val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment nav = navHostFragment.navController @@ -263,6 +265,7 @@ class MainActivity : AppCompatActivity(), OnPreferenceStartFragmentCallback { override fun onDestroy() { super.onDestroy() + TalerNfcService.stopService(this) TalerNfcService.clearUri(this) model.stopWallet() } diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt @@ -61,6 +61,11 @@ abstract class TransactionDetailFragment : Fragment() { } } + override fun onDestroy() { + super.onDestroy() + transactionManager.selectTransaction(null) + } + private fun dialogTitle(t: TransactionAction): Int = when (t) { Delete -> R.string.transactions_delete_dialog_title Abort -> R.string.transactions_abort_dialog_title diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt @@ -191,7 +191,7 @@ class TransactionManager( } ?: Log.d(TAG, "Error updating selected transaction $id") } - fun selectTransaction(tx: Transaction) = scope.launch { + fun selectTransaction(tx: Transaction?) = scope.launch { mSelectedTransaction.value = tx } diff --git a/wallet/src/main/res/xml/apduservice.xml b/wallet/src/main/res/xml/apduservice.xml @@ -18,7 +18,7 @@ android:description="@string/host_apdu_service_desc" android:requireDeviceUnlock="true"> <aid-group - android:category="other" + android:category="payment" android:description="@string/host_apdu_service_desc"> <aid-filter android:name="F00054414C4552" /> <aid-filter android:name="D2760000850101"/>