Skip to content

Commit

Permalink
feat: implement dedicated handler thread for IO operations
Browse files Browse the repository at this point in the history
Previously, a new thread would be spawned for each IO operation, this
would in some cases lead to race conditions as IO operations could
overlap.

Fixes #166
  • Loading branch information
knthm committed May 29, 2024
1 parent 22f94c3 commit 2dbab25
Showing 1 changed file with 27 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.nfc.NfcAdapter
import android.nfc.NfcAdapter.*
import android.nfc.tech.*
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import im.nfc.flutter_nfc_kit.ByteUtils.canonicalizeData
import im.nfc.flutter_nfc_kit.ByteUtils.hexToBytes
Expand All @@ -30,7 +31,6 @@ import java.lang.ref.WeakReference
import java.lang.reflect.InvocationTargetException
import java.util.*
import kotlin.concurrent.schedule
import kotlin.concurrent.thread


class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
Expand All @@ -43,6 +43,9 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
private var ndefTechnology: Ndef? = null
private var mifareInfo: MifareInfo? = null

private lateinit var nfcHandlerThread: HandlerThread
private lateinit var nfcHandler: Handler

private fun TagTechnology.transceive(data: ByteArray, timeout: Int?): ByteArray {
if (timeout != null) {
try {
Expand All @@ -56,10 +59,18 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}

override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
nfcHandlerThread = HandlerThread("NfcHandlerThread")
nfcHandlerThread.start()
nfcHandler = Handler(nfcHandlerThread.looper)

val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_nfc_kit")
channel.setMethodCallHandler(this)
}

override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
nfcHandlerThread.quitSafely()
}

override fun onMethodCall(call: MethodCall, result: Result) {
handleMethodCall(call, MethodResultWrapper(result))
}
Expand Down Expand Up @@ -113,14 +124,14 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
val timeout = call.argument<Int>("timeout")!!
// technology and option bits are set in Dart code
val technologies = call.argument<Int>("technologies")!!
thread {
nfcHandler.post {
pollTag(nfcAdapter, result, timeout, technologies)
}
}

"finish" -> {
pollingTimeoutTask?.cancel()
thread {
nfcHandler.post {
try {
val tagTech = tagTechnology
if (tagTech != null && tagTech.isConnected) {
Expand Down Expand Up @@ -155,7 +166,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
val (sendingBytes, sendingHex) = canonicalizeData(data)

thread {
nfcHandler.post {
try {
switchTechnology(tagTech, ndefTechnology)
val timeout = call.argument<Int>("timeout")
Expand Down Expand Up @@ -187,7 +198,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
"readNDEF" -> {
if (!ensureNDEF()) return
val ndef = ndefTechnology!!
thread {
nfcHandler.post {
try {
switchTechnology(ndef, tagTechnology)
// read NDEF message
Expand Down Expand Up @@ -236,7 +247,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
result.error("405", "Tag not writable", null)
return
}
thread {
nfcHandler.post {
try {
switchTechnology(ndef, tagTechnology)
// generate NDEF message
Expand Down Expand Up @@ -283,7 +294,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
result.error("405", "Tag not writable", null)
return
}
thread {
nfcHandler.post {
try {
switchTechnology(ndef, tagTechnology)
if (ndef.makeReadOnly()) {
Expand Down Expand Up @@ -316,22 +327,22 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
val keyA = call.argument<Any>("keyA")
val keyB = call.argument<Any>("keyB")
thread {
nfcHandler.post {
try {
val tag = tagTech as MifareClassic
switchTechnology(tagTech, ndefTechnology)
// key A takes precedence if present
val success = if (keyA != null) {
if (keyA != null) {
val (key, _) = canonicalizeData(keyA)
tag.authenticateSectorWithKeyA(index, key)
val authStatus = tag.authenticateSectorWithKeyA(index, key)
result.success(authStatus)
} else if (keyB != null) {
val (key, _) = canonicalizeData(keyB)
tag.authenticateSectorWithKeyB(index, key)
val authStatus = tag.authenticateSectorWithKeyB(index, key)
result.success(authStatus)
} else {
result.error("400", "No keys provided", null)
return@thread
}
result.success(success)
} catch (ex: IOException) {
Log.e(TAG, "Authenticate block error", ex)
result.error("500", "Authentication error", ex.localizedMessage)
Expand All @@ -351,7 +362,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
result.error("400", "Invalid block/page index $index, should be in (0, $maxBlock)", null)
return
}
thread {
nfcHandler.post {
try {
switchTechnology(tagTech, ndefTechnology)
tagTech.readBlock(index, result)
Expand All @@ -374,7 +385,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
result.error("400", "Invalid sector index $index, should be in (0, $maxSector)", null)
return
}
thread {
nfcHandler.post {
try {
val tag = tagTech as MifareClassic
switchTechnology(tagTech, ndefTechnology)
Expand Down Expand Up @@ -407,7 +418,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
result.error("400", "Invalid data size ${bytes.size}, should be ${mifareInfo!!.blockSize}", null)
return
}
thread {
nfcHandler.post {
try {
switchTechnology(tagTech, ndefTechnology)
tagTech.writeBlock(index, bytes, result)
Expand All @@ -421,9 +432,6 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
}


override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}

override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = WeakReference(binding.activity)
}
Expand Down

0 comments on commit 2dbab25

Please sign in to comment.