package com.rfidsystem.hardware import android.util.Log data class PasscodeRecord( val passcode: String, val createdAt: Long ) /** Gen2 memory banks, in the order [RFIDWithUHF.generateLockCode] expects them. */ enum class TagMemoryBank { KILL_PASSWORD, ACCESS_PASSWORD, EPC, TID, USER } /** * Only the reversible operations are exposed. * * The vendor SDK also offers `PLOCK`/`PUNLOCK` (permalock) and `killTag`. Those are physically * irreversible — a permalocked or killed tag is scrap — and this implementation has not been * validated against real hardware, so they are deliberately not reachable from the app. */ enum class TagLockAction { LOCK, UNLOCK } sealed interface TagLockResult { data object Success : TagLockResult /** The tag rejected the operation, usually a wrong access password. */ data object Rejected : TagLockResult /** The connected SDK does not expose the lock API; nothing was attempted. */ data class Unsupported(val reason: String) : TagLockResult data class Error(val message: String) : TagLockResult } /** * Real Gen2 lock/unlock against the Chainway SDK, reached by reflection because the vendor jars are * not on the compile classpath of every build. * * The previous implementation of this class compared the supplied passcode against two hardcoded * strings and returned `true` for `F9B201A8` or `00000000` — it never touched the radio. That is * worse than an unimplemented feature: it reported success while leaving tags untouched. */ class TagLockManager(private val readerProvider: () -> Any?) { /** * Builds a lock code that holds every bank except [bank], then applies it to the tag currently * singulated by [epc]. */ fun setLock( epc: String, bank: TagMemoryBank, action: TagLockAction, accessPassword: String ): TagLockResult { val reader = readerProvider() ?: return TagLockResult.Unsupported("No RFID reader connected.") return try { val lockModeClass = Class.forName("com.rscja.deviceapi.RFIDWithUHF\$LockModeEnum") val hold = enumConstant(lockModeClass, "HOLD") val target = enumConstant(lockModeClass, action.name) ?: return TagLockResult.Unsupported("SDK has no ${action.name} lock mode.") // generateLockCode(kill, access, epc, tid, user) — HOLD leaves a bank unchanged. val modes = Array(TagMemoryBank.entries.size) { hold } modes[bank.ordinal] = target val generate = reader.javaClass.getMethod( "generateLockCode", lockModeClass, lockModeClass, lockModeClass, lockModeClass, lockModeClass ) val lockCode = generate.invoke(reader, *modes) as? String ?: return TagLockResult.Error("SDK returned no lock code.") val lockMem = reader.javaClass.getMethod("lockMem", String::class.java, String::class.java) val applied = lockMem.invoke(reader, accessPassword, lockCode) // lockMem is overloaded: RFIDWithUHFUART returns Boolean, the base class returns a String. val ok = when (applied) { is Boolean -> applied is String -> applied.isNotBlank() else -> false } Log.i(TAG, "${action.name} ${bank.name} on $epc -> $ok") if (ok) TagLockResult.Success else TagLockResult.Rejected } catch (e: NoSuchMethodException) { TagLockResult.Unsupported("Connected SDK does not expose lockMem/generateLockCode.") } catch (e: ClassNotFoundException) { TagLockResult.Unsupported("Chainway SDK not present on this device.") } catch (e: Exception) { Log.e(TAG, "Lock operation failed", e) TagLockResult.Error(e.localizedMessage ?: e.javaClass.simpleName) } } /** * Tries each historic passcode until one is accepted. * * @return the passcode that worked, or null. Stops early on [TagLockResult.Unsupported] — if the * SDK cannot lock at all, trying the remaining passcodes is pointless. */ fun unlockTagWithHistoryProbing( epc: String, passwordHistory: List, bank: TagMemoryBank = TagMemoryBank.EPC ): Pair { Log.i(TAG, "Probing ${passwordHistory.size} historic passcodes for $epc") for (record in passwordHistory) { when (val result = setLock(epc, bank, TagLockAction.UNLOCK, record.passcode)) { is TagLockResult.Success -> return true to record.passcode is TagLockResult.Unsupported -> { Log.w(TAG, "Aborting probe: ${result.reason}") return false to null } else -> Unit // wrong password or transient error: try the next one } } return false to null } private fun enumConstant(enumClass: Class<*>, name: String): Any? = enumClass.enumConstants?.firstOrNull { (it as? Enum<*>)?.name == name } companion object { private const val TAG = "TagLockManager" } }