From 6e31706905d2c2d5221878b96e7971c846464bab Mon Sep 17 00:00:00 2001 From: champignoom Date: Mon, 11 Aug 2025 15:31:49 +0800 Subject: [PATCH 1/3] Changed|Added(NfcAPI): Add all APIs for all NFC technologies --- app/src/main/AndroidManifest.xml | 2 +- .../main/java/com/termux/api/apis/NfcAPI.java | 2124 +++++++++++++++-- app/src/main/res/xml/nfc_tech_filter.xml | 9 + 3 files changed, 1891 insertions(+), 244 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index beb27e832..651f1d1a7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -111,8 +111,8 @@ android:exported="false" /> diff --git a/app/src/main/java/com/termux/api/apis/NfcAPI.java b/app/src/main/java/com/termux/api/apis/NfcAPI.java index d0d607941..4ec3fbc2b 100644 --- a/app/src/main/java/com/termux/api/apis/NfcAPI.java +++ b/app/src/main/java/com/termux/api/apis/NfcAPI.java @@ -4,108 +4,1860 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.nfc.FormatException; import android.nfc.NdefMessage; -import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.nfc.tech.MifareClassic; +import android.nfc.tech.MifareUltralight; import android.nfc.tech.Ndef; +import android.nfc.tech.NdefFormatable; +import android.nfc.tech.NfcA; +import android.nfc.tech.NfcB; +import android.nfc.tech.NfcBarcode; +import android.nfc.tech.NfcF; +import android.nfc.tech.NfcV; +import android.nfc.tech.TagTechnology; import android.os.Bundle; -import android.os.Parcelable; import android.util.JsonWriter; +import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.util.Function; import com.termux.api.util.PendingIntentUtils; import com.termux.api.util.ResultReturner; import com.termux.shared.logger.Logger; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +class NfcException extends RuntimeException { + NfcException() { + + } + + NfcException(String message) { + super(message); + } +} + +class UnsupportedTechnology extends NfcException {} +class NoConnectionException extends NfcException {} +class WrongTechnologyException extends NfcException { + WrongTechnologyException(@NonNull String expected, @NonNull String found) { + super(String.format("%s expected, but %s found", expected, found)); + } +} +class NfcUnavailableException extends NfcException {} +class ActivityFinishingException extends NfcException {} +class TagNullException extends NfcException {} + +class ArgumentException extends NfcException { + ArgumentException() { + super(); + } + + ArgumentException(String message) { + super(message); + } +} +class ArgNumberException extends ArgumentException { + ArgNumberException(int expected, int found) { + super(String.format("%d expected, found %d", expected, found)); + } +} +class InvalidHexException extends ArgumentException { + InvalidHexException(char c) { + super(String.format("%c", c)); + } +} +class InvalidHexLengthException extends ArgumentException {} +class InvalidCommandException extends ArgumentException {} + +class Utils { + static void checkTechnologyNonNull(@Nullable TagTechnology technology) throws NoConnectionException { + if (technology == null) { + throw new NoConnectionException(); + } + } + + static void checkTagNonNull(@Nullable Tag tag) throws TagNullException { + if (tag == null) { + throw new TagNullException(); + } + } + + static @NonNull T liftTagTechnology(@Nullable TagTechnology technology, @NonNull Class tClass) + throws NoConnectionException, WrongTechnologyException + { + checkTechnologyNonNull(technology); + + try { + T techLifted = tClass.cast(technology); + assert techLifted != null; + return techLifted; + } + catch (ClassCastException e) { + throw new WrongTechnologyException(tClass.getSimpleName(), technology.getClass().getSimpleName()); + } + } + + static void checkIntEqual(int expectedLength, int argLength) throws ArgNumberException { + if (expectedLength != argLength) { + throw new ArgNumberException(expectedLength, argLength); + } + } + + static byte parseHexOne(char c) throws ArgumentException { + if ('0' <= c && c <= '9') + return (byte)((byte)c - '0'); + if ('a' <= c && c <= 'f') + return (byte)((byte)c - 'a' + 10); + if ('A' <= c && c <= 'F') + return (byte)((byte)c - 'A' + 10); + throw new InvalidHexException(c); + } + + static byte parseHexTwo(char high, char low) throws ArgumentException { + return (byte)((parseHexOne(high) << 4) | parseHexOne(low)); + } + + /** + * Parse hex representation of byte[] array. + * @param hex the hex representation, e.g. "DEADBEEF" + * @return the byte[] array, e.g. {0xde, 0xad, 0xbe, 0xef}. + * @throws ArgumentException if it fails to parse. + */ + static byte[] parseHex(@NonNull String hex) throws ArgumentException { + if (hex.length()%2 != 0 || hex.isEmpty()) { + throw new InvalidHexLengthException(); + } + + byte[] result = new byte[hex.length()/2]; + for (int i=0; i "DEADBEEF" + * @param data the byte array to be formatted + * @return the formatted hex representation + */ + static String formatHex(@NonNull byte[] data) { + StringBuilder sb = new StringBuilder(); + for (byte b: data) { + sb.append(Utils.formatHexOne(b)); + } + return sb.toString(); + } + + + /** + * Collect "arg1" "arg2" "arg3" ... from intent extras, + * where the prefix "arg" can be specified. + * + * @param intent the intent + * @param argPrefix the prefix, usually "arg" + * + * @return collected arguments + */ + static String[] collectNumberedArgs(Intent intent, @NonNull String argPrefix) { + List result = new ArrayList<>(); + + for (int index=1; true; ++index) { + String argName = argPrefix + index; + String argValue = intent.getStringExtra(argName); + if (argValue == null) { + break; + } + result.add(argValue); + } + + return result.toArray(new String[0]); + } +} + +/** + * Return value of wrapped NFC API call. + */ +class CallResult { + @Nullable Object mData; + + CallResult(@Nullable Object data) { + mData = data; + } + + static CallResult successWithString(@NonNull String data) { + return new CallResult(data); + } + + static CallResult successWithHex(@NonNull byte[] data) { + return new CallResult(Utils.formatHex(data)); + } + + static CallResult successWithInt(int data) { + return new CallResult(data); + } + + static CallResult successWithBool(boolean data) { + return new CallResult(data); + } + + static CallResult success() { + return new CallResult(null); + } +} + +/** + * Single abstract method for NFC API invocation. + */ +interface TagTechnologyClosure { + CallResult call() throws IOException, android.nfc.FormatException, NfcException; +} + public class NfcAPI { - private static final String LOG_TAG = "NfcAPI"; + private static final String LOG_TAG = "NfcAPI"; + + public static void onReceive(final Context context, final Intent intent) { + Logger.logDebug(LOG_TAG, "onReceive"); + + Intent newIntent = new Intent(context, NfcActivity.class); + Bundle extras = intent.getExtras(); + if (extras == null) { + return; + } + newIntent.putExtras(extras).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(newIntent); + } + + + public static class NfcActivity extends AppCompatActivity { + // Closure for the first API call that is delayed after the discovery of tag. + private TagTechnologyClosure mDelayedClosure; + + // The result of connect() + private @Nullable TagTechnology mTechnology; + + // The last discovered tag + private Tag mTag; + + private NfcAdapter mAdapter; + + private static final String LOG_TAG = "NfcActivity"; + + // start of wrapper for quit + private final static String METHOD_NAME_QUIT = "quit"; + + private TagTechnologyClosure parseArgsQuit(final @NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(1, args.length); + return this::callQuit; + } + + private CallResult callQuit() { + finish(); + return CallResult.success(); + } + // end of wrapper for quit + + // start of wrappers for abstract class TagTechnology + // doc: https://developer.android.com/reference/android/nfc/tech/TagTechnology + final static String CLASS_NAME_TAG_TECHNOLOGY = "TagTechnology"; + + // start of wrapper for TagTechnology::isConnected + private final static String METHOD_NAME_TAG_TECHNOLOGY_IS_CONNECTED = "isConnected"; + + private TagTechnologyClosure parseArgsTagTechnologyIsConnected(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callTagTechnologyIsConnected; + } + + private CallResult callTagTechnologyIsConnected() { + Utils.checkTechnologyNonNull(mTechnology); + boolean response = mTechnology.isConnected(); + return CallResult.successWithBool(response); + } + // end of wrapper for TagTechnology::isConnected + + // start of wrapper for TagTechnology::close + private final static String METHOD_NAME_TAG_TECHNOLOGY_CLOSE = "close"; + + private TagTechnologyClosure parseArgsTagTechnologyClose(final @NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callTagTechnologyClose; + } + + private CallResult callTagTechnologyClose() throws IOException { + Utils.checkTechnologyNonNull(mTechnology); + mTechnology.close(); + return CallResult.success(); + } + // end of wrapper for TagTechnology::close + + // start of wrapper for TagTechnology::getTag + private final static String METHOD_NAME_TAG_TECHNOLOGY_GET_TAG = "getTag"; + + private TagTechnologyClosure parseArgsTagTechnologyGetTag(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callTagTechnologyGetTag; + } + + private CallResult callTagTechnologyGetTag() { + Utils.checkTagNonNull(mTag); + String description = mTag.toString(); + return CallResult.successWithString(description); + } + // end of wrappers for abstract class TagTechnology + + // start of wrappers for class NfcA + // doc: https://developer.android.com/reference/android/nfc/tech/NfcA + private static final String CLASS_NAME_NFC_A = "NfcA"; + + // start of wrapper for NfcA::connect + private final static String METHOD_NAME_NFC_A_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsNfcAConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcAConnect; + } + + private CallResult callNfcAConnect() throws IOException { + return callTagTechnologyConnect(NfcA::get); + } + // end of wrapper for NfcA::connect + + // start of wrapper for NfcA::getAtqa + private final static String METHOD_NAME_NFC_A_GET_ATQA = "getAtqa"; + + private TagTechnologyClosure parseArgsNfcAGetAtqa(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcAGetAtqa; + } + + private CallResult callNfcAGetAtqa() { + @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); + byte[] response = nfcA.getAtqa(); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcA::getAtqa + + // start of wrapper for NfcA::getMaxTransceiveLength + private final static String METHOD_NAME_NFC_A_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; + + private TagTechnologyClosure parseArgsNfcAGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcAGetMaxTransceiveLength; + } + + private CallResult callNfcAGetMaxTransceiveLength() { + @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); + int response = nfcA.getMaxTransceiveLength(); + return CallResult.successWithInt(response); + } + // end of wrapper for NfcA::getMaxTransceiveLength + + // start of wrapper for NfcA::getSak + private final static String METHOD_NAME_NFC_A_GET_SAK = "getSak"; + + private TagTechnologyClosure parseArgsNfcAGetSak(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcAGetSak; + } + + private CallResult callNfcAGetSak() { + @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); + short response = nfcA.getSak(); + return CallResult.successWithInt(response); + } + // end of wrapper for NfcA::getSak + + // start of wrapper for NfcA::getTimeout + private final static String METHOD_NAME_NFC_A_GET_TIMEOUT = "getTimeout"; + + private TagTechnologyClosure parseArgsNfcAGetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcAGetTimeout; + } + + private CallResult callNfcAGetTimeout() { + @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); + int response = nfcA.getTimeout(); + return CallResult.successWithInt(response); + } + // end of wrapper for NfcA::getTimeout + + // start of wrapper for NfcA::setTimeout + private final static String METHOD_NAME_NFC_A_SET_TIMEOUT = "setTimeout"; + + private TagTechnologyClosure parseArgsNfcASetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int timeout = Utils.parseInt(args[args.length-1]); + return () -> callNfcASetTimeout(timeout); + } + + private CallResult callNfcASetTimeout(int timeout) { + @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); + nfcA.setTimeout(timeout); + return CallResult.success(); + } + // end of wrapper for NfcA::setTimeout + + // start of wrapper for NfcA::transceive + private final static String METHOD_NAME_NFC_A_TRANSCEIVE = "transceive"; + + private TagTechnologyClosure parseArgsNfcATransceive(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] data = Utils.parseHex(args[args.length-1]); + return () -> callNfcATransceive(data); + } + + private CallResult callNfcATransceive(byte[] data) throws IOException { + @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); + byte[] response = nfcA.transceive(data); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcA::transceive + // end of wrappers for class NfcA + + // start of wrappers for class NfcB + // doc: https://developer.android.com/reference/android/nfc/tech/NfcB + private static final String CLASS_NAME_NFC_B = "NfcB"; + + // start of wrapper for NfcB::connect + private final static String METHOD_NAME_NFC_B_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsNfcBConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcBConnect; + } + + private CallResult callNfcBConnect() throws IOException { + return callTagTechnologyConnect(NfcB::get); + } + // end of wrapper for NfcB::connect + + // start of wrapper for NfcB::getApplicationData + private final static String METHOD_NAME_NFC_B_GET_APPLICATION_DATA = "getApplicationData"; + + private TagTechnologyClosure parseArgsNfcBGetApplicationData(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcBGetApplicationData; + } + + private CallResult callNfcBGetApplicationData() { + @NonNull NfcB nfcB = Utils.liftTagTechnology(mTechnology, NfcB.class); + byte[] response = nfcB.getApplicationData(); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcB::getApplicationData + + // start of wrapper for NfcB::getMaxTransceiveLength + private final static String METHOD_NAME_NFC_B_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; + + private TagTechnologyClosure parseArgsNfcBGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcBGetMaxTransceiveLength; + } + + private CallResult callNfcBGetMaxTransceiveLength() { + @NonNull NfcB nfcB = Utils.liftTagTechnology(mTechnology, NfcB.class); + int response = nfcB.getMaxTransceiveLength(); + return CallResult.successWithInt(response); + } + // end of wrapper for NfcB::getMaxTransceiveLength + + // start of wrapper for NfcB::getProtocolInfo + private final static String METHOD_NAME_NFC_B_GET_PROTOCOL_INFO = "getProtocolInfo"; + + private TagTechnologyClosure parseArgsNfcBGetProtocolInfo(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcBGetProtocolInfo; + } + + private CallResult callNfcBGetProtocolInfo() { + @NonNull NfcB nfcB = Utils.liftTagTechnology(mTechnology, NfcB.class); + byte[] response = nfcB.getProtocolInfo(); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcB::getProtocolInfo + + // start of wrapper for NfcB::transceive + private final static String METHOD_NAME_NFC_B_TRANSCEIVE = "transceive"; + + private TagTechnologyClosure parseArgsNfcBTransceive(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] data = Utils.parseHex(args[args.length-1]); + return () -> callNfcBTransceive(data); + } + + private CallResult callNfcBTransceive(byte[] data) throws IOException { + @NonNull NfcB nfcB = Utils.liftTagTechnology(mTechnology, NfcB.class); + byte[] response = nfcB.transceive(data); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcB::transceive + // end of wrappers for class NfcB + + // start of wrappers for class NfcF + // doc: https://developer.android.com/reference/android/nfc/tech/NfcF + private static final String CLASS_NAME_NFC_F = "NfcF"; + + // start of wrapper for NfcF::connect + private final static String METHOD_NAME_NFC_F_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsNfcFConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcFConnect; + } + + private CallResult callNfcFConnect() throws IOException { + return callTagTechnologyConnect(NfcF::get); + } + // end of wrapper for NfcF::connect + + // start of wrapper for NfcF::getManufacturer + private final static String METHOD_NAME_NFC_F_GET_MANUFACTURER = "getManufacturer"; + + private TagTechnologyClosure parseArgsNfcFGetManufacturer(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcFGetManufacturer; + } + + private CallResult callNfcFGetManufacturer() { + @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); + byte[] response = nfcF.getManufacturer(); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcF::getManufacturer + + // start of wrapper for NfcF::getMaxTransceiveLength + private final static String METHOD_NAME_NFC_F_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; + + private TagTechnologyClosure parseArgsNfcFGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcFGetMaxTransceiveLength; + } + + private CallResult callNfcFGetMaxTransceiveLength() { + @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); + int response = nfcF.getMaxTransceiveLength(); + return CallResult.successWithInt(response); + } + // end of wrapper for NfcF::getMaxTransceiveLength + + // start of wrapper for NfcF::getSystemCode + private final static String METHOD_NAME_NFC_F_GET_SYSTEM_CODE = "getSystemCode"; + + private TagTechnologyClosure parseArgsNfcFGetSystemCode(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcFGetSystemCode; + } + + private CallResult callNfcFGetSystemCode() { + @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); + byte[] response = nfcF.getSystemCode(); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcF::getSystemCode + + // start of wrapper for NfcF::getTimeout + private final static String METHOD_NAME_NFC_F_GET_TIMEOUT = "getTimeout"; + + private TagTechnologyClosure parseArgsNfcFGetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcFGetTimeout; + } + + private CallResult callNfcFGetTimeout() { + @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); + int response = nfcF.getTimeout(); + return CallResult.successWithInt(response); + } + // end of wrapper for NfcF::getTimeout + + // start of wrapper for NfcF::setTimeout + private final static String METHOD_NAME_NFC_F_SET_TIMEOUT = "setTimeout"; + + private TagTechnologyClosure parseArgsNfcFSetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int timeout = Utils.parseInt(args[args.length-1]); + return () -> callNfcFSetTimeout(timeout); + } + + private CallResult callNfcFSetTimeout(int timeout) { + @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); + nfcF.setTimeout(timeout); + return CallResult.success(); + } + // end of wrapper for NfcF::setTimeout + + // start of wrapper for NfcF::transceive + private final static String METHOD_NAME_NFC_F_TRANSCEIVE = "transceive"; + + private TagTechnologyClosure parseArgsNfcFTransceive(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] data = Utils.parseHex(args[args.length-1]); + return () -> callNfcFTransceive(data); + } + + private CallResult callNfcFTransceive(byte[] data) throws IOException { + @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); + byte[] response = nfcF.transceive(data); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcF::transceive + // end of wrappers for class NfcF + + // start of wrappers for class NfcV + // doc: https://developer.android.com/reference/android/nfc/tech/NfcV + private static final String CLASS_NAME_NFC_V = "NfcV"; + + // start of wrapper for NfcV::connect + private final static String METHOD_NAME_NFC_V_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsNfcVConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcVConnect; + } + + private CallResult callNfcVConnect() throws IOException { + return callTagTechnologyConnect(NfcV::get); + } + // end of wrapper for NfcV::connect + + // start of wrapper for NfcV::getDsfId + private final static String METHOD_NAME_NFC_V_GET_DSF_ID = "getDsfId"; + + private TagTechnologyClosure parseArgsNfcVGetDsfId(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcVGetDsfId; + } + + private CallResult callNfcVGetDsfId() { + @NonNull NfcV nfcV = Utils.liftTagTechnology(mTechnology, NfcV.class); + byte response = nfcV.getDsfId(); + return CallResult.successWithString(Utils.formatHexOne(response)); + } + // end of wrapper for NfcV::getDsfId + + // start of wrapper for NfcV::getMaxTransceiveLength + private final static String METHOD_NAME_NFC_V_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; + + private TagTechnologyClosure parseArgsNfcVGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcVGetMaxTransceiveLength; + } + + private CallResult callNfcVGetMaxTransceiveLength() { + @NonNull NfcV nfcV = Utils.liftTagTechnology(mTechnology, NfcV.class); + int response = nfcV.getMaxTransceiveLength(); + return CallResult.successWithInt(response); + } + // end of wrapper for NfcV::getMaxTransceiveLength + + // start of wrapper for NfcV::getResponseFlags + private final static String METHOD_NAME_NFC_V_GET_RESPONSE_FLAGS = "getResponseFlags"; + + private TagTechnologyClosure parseArgsNfcVGetResponseFlags(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcVGetResponseFlags; + } + + private CallResult callNfcVGetResponseFlags() { + @NonNull NfcV nfcV = Utils.liftTagTechnology(mTechnology, NfcV.class); + byte response = nfcV.getResponseFlags(); + return CallResult.successWithString(Utils.formatHexOne(response)); + } + // end of wrapper for NfcV::getResponseFlags + + // start of wrapper for NfcV::transceive + private final static String METHOD_NAME_NFC_V_TRANSCEIVE = "transceive"; + + private TagTechnologyClosure parseArgsNfcVTransceive(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] data = Utils.parseHex(args[args.length-1]); + return () -> callNfcVTransceive(data); + } + + private CallResult callNfcVTransceive(byte[] data) throws IOException { + @NonNull NfcV nfcV = Utils.liftTagTechnology(mTechnology, NfcV.class); + byte[] response = nfcV.transceive(data); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcV::transceive + // end of wrappers for class NfcV + + // start of wrappers for class IsoDep + // doc: https://developer.android.com/reference/android/nfc/tech/IsoDep + private static final String CLASS_NAME_ISO_DEP = "IsoDep"; + + // start of wrapper for IsoDep::connect + private final static String METHOD_NAME_ISO_DEP_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsIsoDepConnect(final @NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callIsoDepConnect; + } + + private CallResult callIsoDepConnect() throws IOException { + return callTagTechnologyConnect(IsoDep::get); + } + // end of wrapper for IsoDep::connect + + // start of wrapper for IsoDep::getHiLayerResponse + private final static String METHOD_NAME_ISO_DEP_GET_HI_LAYER_RESPONSE = "getHiLayerResponse"; + + private TagTechnologyClosure parseArgsIsoDepGetHiLayerResponse(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callIsoDepGetHiLayerResponse; + } + + private CallResult callIsoDepGetHiLayerResponse() { + @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); + byte[] response = isoDep.getHiLayerResponse(); + return CallResult.successWithHex(response); + } + // end of wrapper for IsoDep::getHiLayerResponse + + // start of wrapper for IsoDep::getHistoricalBytes + private final static String METHOD_NAME_ISO_DEP_GET_HISTORICAL_BYTES = "getHistoricalBytes"; + + private TagTechnologyClosure parseArgsIsoDepGetHistoricalBytes(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callGetHistoricalBytes; + } + + private CallResult callGetHistoricalBytes() { + @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); + byte[] response = isoDep.getHistoricalBytes(); + return CallResult.successWithHex(response); + } + // end of wrapper for IsoDep::getHistoricalBytes + + // start of wrapper for IsoDep::getMaxTransceiveLength + private final static String METHOD_NAME_ISO_DEP_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; + + private TagTechnologyClosure parseArgsIsoDepGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callGetMaxTransceiveLength; + } + + private CallResult callGetMaxTransceiveLength() { + @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); + int response = isoDep.getMaxTransceiveLength(); + return CallResult.successWithInt(response); + } + // end of wrapper for IsoDep::getMaxTransceiveLength + + + // start of wrapper for IsoDep::getTimeout + private final static String METHOD_NAME_ISO_DEP_GET_TIMEOUT = "getTimeout"; + + private TagTechnologyClosure parseArgsIsoDepGetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callGetTimeout; + } + + private CallResult callGetTimeout() { + @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); + int response = isoDep.getTimeout(); + return CallResult.successWithInt(response); + } + // end of wrapper for IsoDep::getTimeout + + // start of wrapper for IsoDep::isExtendedLengthApduSupported + private final static String METHOD_NAME_ISO_DEP_IS_EXTENDED_LENGTH_APDU_SUPPORTED = "isExtendedLengthApduSupported"; + + private TagTechnologyClosure parseArgsIsoDepIsExtendedLengthApduSupported(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callIsExtendedLengthApduSupported; + } + + private CallResult callIsExtendedLengthApduSupported() { + @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); + boolean response = isoDep.isExtendedLengthApduSupported(); + return CallResult.successWithBool(response); + } + // end of wrapper for IsoDep::isExtendedLengthApduSupported + + // start of wrapper for IsoDep::setTimeout + private final static String METHOD_NAME_ISO_DEP_SET_TIMEOUT = "setTimeout"; + + private TagTechnologyClosure parseArgsIsoDepSetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int timeout = Utils.parseInt(args[args.length-1]); + return () -> callSetTimeout(timeout); + } + + private CallResult callSetTimeout(int timeout) { + @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); + isoDep.setTimeout(timeout); + return CallResult.success(); + } + // end of wrapper for IsoDep::setTimeout + + // start of wrapper for IsoDep::transceive + private final static String METHOD_NAME_ISO_DEP_TRANSCEIVE = "transceive"; + + private TagTechnologyClosure parseArgsIsoDepTransceive(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + + @NonNull byte[] requestData = Utils.parseHex(args[args.length-1]); + return () -> callIsoDepTransceive(requestData); + } + + private CallResult callIsoDepTransceive(@NonNull byte[] requestData) throws NfcException, IOException, IllegalStateException { + @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); + @NonNull byte[] response = isoDep.transceive(requestData); + return CallResult.successWithHex(response); + } + // end of wrapper for IsoDep::transceive + // end of wrappers for class IsoDep + + // start of wrappers for class Ndef + // doc: https://developer.android.com/reference/android/nfc/tech/Ndef + private static final String CLASS_NAME_NDEF = "Ndef"; + + // start of wrapper for Ndef::connect + private final static String METHOD_NAME_NDEF_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsNdefConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefConnect; + } + + private CallResult callNdefConnect() throws IOException { + return callTagTechnologyConnect(Ndef::get); + } + // end of wrapper for Ndef::connect + + // start of wrapper for Ndef::canMakeReadOnly + private final static String METHOD_NAME_NDEF_CAN_MAKE_READ_ONLY = "canMakeReadOnly"; + + private TagTechnologyClosure parseArgsNdefCanMakeReadOnly(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefCanMakeReadOnly; + } + + private CallResult callNdefCanMakeReadOnly() { + @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); + boolean response = ndef.canMakeReadOnly(); + return CallResult.successWithBool(response); + } + // end of wrapper for Ndef::canMakeReadOnly + + // start of wrapper for Ndef::getCachedNdefMessage + private final static String METHOD_NAME_NDEF_GET_CACHED_NDEF_MESSAGE = "getCachedNdefMessage"; + + private TagTechnologyClosure parseArgsNdefGetCachedNdefMessage(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefGetCachedNdefMessage; + } + + private CallResult callNdefGetCachedNdefMessage() { + @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); + NdefMessage msg = ndef.getCachedNdefMessage(); + return CallResult.successWithString(msg.toString()); + } + // end of wrapper for Ndef::getCachedNdefMessage + + // start of wrapper for Ndef::getMaxSize + private final static String METHOD_NAME_NDEF_GET_MAX_SIZE = "getMaxSize"; + + private TagTechnologyClosure parseArgsNdefGetMaxSize(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefGetMaxSize; + } + + private CallResult callNdefGetMaxSize() { + @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); + int response = ndef.getMaxSize(); + return CallResult.successWithInt(response); + } + // end of wrapper for Ndef::getMaxSize + + // start of wrapper for Ndef::getNdefMessage + private final static String METHOD_NAME_NDEF_GET_NDEF_MESSAGE = "getNdefMessage"; + + private TagTechnologyClosure parseArgsNdefGetNdefMessage(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefGetNdefMessage; + } + + private CallResult callNdefGetNdefMessage() throws IOException, android.nfc.FormatException { + @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); + NdefMessage msg = ndef.getNdefMessage(); + return CallResult.successWithString(msg.toString()); + } + // end of wrapper for Ndef::getNdefMessage + + // start of wrapper for Ndef::getType + private final static String METHOD_NAME_NDEF_GET_TYPE = "getType"; + + private TagTechnologyClosure parseArgsNdefGetType(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefGetType; + } + + private CallResult callNdefGetType() { + @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); + String response = ndef.getType(); + return CallResult.successWithString(response); + } + // end of wrapper for Ndef::getType + + // start of wrapper for Ndef::isWritable + private final static String METHOD_NAME_NDEF_IS_WRITABLE = "isWritable"; + + private TagTechnologyClosure parseArgsNdefIsWritable(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefIsWritable; + } + + private CallResult callNdefIsWritable() { + @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); + boolean response = ndef.isWritable(); + return CallResult.successWithBool(response); + } + // end of wrapper for Ndef::isWritable + + // start of wrapper for Ndef::makeReadOnly + private final static String METHOD_NAME_NDEF_MAKE_READ_ONLY = "makeReadOnly"; + + private TagTechnologyClosure parseArgsNdefMakeReadOnly(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefMakeReadOnly; + } + + private CallResult callNdefMakeReadOnly() throws IOException { + @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); + boolean result = ndef.makeReadOnly(); + return CallResult.successWithBool(result); + } + // end of wrapper for Ndef::makeReadOnly + + // start of wrapper for Ndef::writeNdefMessage + private final static String METHOD_NAME_NDEF_WRITE_NDEF_MESSAGE = "writeNdefMessage"; + + private TagTechnologyClosure parseArgsNdefWriteNdefMessage(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] msgHex = Utils.parseHex(args[args.length-1]); + return () -> callNdefWriteNdefMessage(msgHex); + } + + private CallResult callNdefWriteNdefMessage(@NonNull byte[] msgHex) throws FormatException, IOException { + @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); + ndef.writeNdefMessage(new NdefMessage(msgHex)); + return CallResult.success(); + } + // end of wrapper for Ndef::writeNdefMessage + // end of wrappers for class Ndef + + // start of wrappers for class MifareClassic + // doc: https://developer.android.com/reference/android/nfc/tech/MifareClassic + private final static String CLASS_NAME_MIFARE_CLASSIC = "MifareClassic"; + + // start of wrapper for MifareClassic::authenticateSectorWithKeyA + private final static String METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_A = "authenticateSectorWithKeyA"; + + private TagTechnologyClosure parseArgsMifareClassicAuthenticateSectorWithKeyA(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 2, args.length); + int sectorIndex = Utils.parseInt(args[args.length - 2]); + @NonNull byte[] key = Utils.parseHex(args[args.length - 1]); + return () -> callMifareClassicAuthenticateSectorWithKeyA(sectorIndex, key); + } + + private CallResult callMifareClassicAuthenticateSectorWithKeyA(int sectorIndex, @NonNull byte[] key) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + boolean result = mifareClassic.authenticateSectorWithKeyA(sectorIndex, key); + return CallResult.successWithBool(result); + } + // end of wrapper for MifareClassic::authenticateSectorWithKeyA + + // start of wrapper for MifareClassic::authenticateSectorWithKeyB + private final static String METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_B = "authenticateSectorWithKeyB"; + + private TagTechnologyClosure parseArgsMifareClassicAuthenticateSectorWithKeyB(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 2, args.length); + int sectorIndex = Utils.parseInt(args[args.length - 2]); + @NonNull byte[] key = Utils.parseHex(args[args.length - 1]); + return () -> callMifareClassicAuthenticateSectorWithKeyB(sectorIndex, key); + } + + private CallResult callMifareClassicAuthenticateSectorWithKeyB(int sectorIndex, @NonNull byte[] key) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + boolean result = mifareClassic.authenticateSectorWithKeyB(sectorIndex, key); + return CallResult.successWithBool(result); + } + // end of wrapper for MifareClassic::authenticateSectorWithKeyB + + // start of wrapper for MifareClassic::blockToSector + private final static String METHOD_NAME_MIFARE_CLASSIC_BLOCK_TO_SECTOR = "blockToSector"; + + private TagTechnologyClosure parseArgsMifareClassicBlockToSector(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int blockIndex = Utils.parseInt(args[args.length - 1]); + return () -> callMifareClassicBlockToSector(blockIndex); + } + + private CallResult callMifareClassicBlockToSector(int blockIndex) { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.blockToSector(blockIndex); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::blockToSector + + // start of wrapper for MifareClassic::connect (inherited from TagTechnology) + private final static String METHOD_NAME_MIFARE_CLASSIC_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsMifareClassicConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareClassicConnect; + } + + private CallResult callMifareClassicConnect() throws IOException { + return callTagTechnologyConnect(MifareClassic::get); + } + // end of wrapper for MifareClassic::connect + + // start of wrapper for MifareClassic::decrement + private final static String METHOD_NAME_MIFARE_CLASSIC_DECREMENT = "decrement"; + + private TagTechnologyClosure parseArgsMifareClassicDecrement(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 2, args.length); + int blockIndex = Utils.parseInt(args[args.length - 2]); + int value = Utils.parseInt(args[args.length - 1]); + return () -> callMifareClassicDecrement(blockIndex, value); + } + + private CallResult callMifareClassicDecrement(int blockIndex, int value) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + mifareClassic.decrement(blockIndex, value); + return CallResult.success(); + } + // end of wrapper for MifareClassic::decrement + + // start of wrapper for MifareClassic::getBlockCount + private final static String METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT = "getBlockCount"; + + private TagTechnologyClosure parseArgsMifareClassicGetBlockCount(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareClassicGetBlockCount; + } + + private CallResult callMifareClassicGetBlockCount() { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.getBlockCount(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::getBlockCount + + // start of wrapper for MifareClassic::getBlockCountInSector + private final static String METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT_IN_SECTOR = "getBlockCountInSector"; + + private TagTechnologyClosure parseArgsMifareClassicGetBlockCountInSector(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int sectorIndex = Utils.parseInt(args[args.length - 1]); + return () -> callMifareClassicGetBlockCountInSector(sectorIndex); + } + + private CallResult callMifareClassicGetBlockCountInSector(int sectorIndex) { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.getBlockCountInSector(sectorIndex); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::getBlockCountInSector + + // start of wrapper for MifareClassic::getMaxTransceiveLength + private final static String METHOD_NAME_MIFARE_CLASSIC_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; + + private TagTechnologyClosure parseArgsMifareClassicGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareClassicGetMaxTransceiveLength; + } + + private CallResult callMifareClassicGetMaxTransceiveLength() { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.getMaxTransceiveLength(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::getMaxTransceiveLength + + // start of wrapper for MifareClassic::getSectorCount + private final static String METHOD_NAME_MIFARE_CLASSIC_GET_SECTOR_COUNT = "getSectorCount"; + + private TagTechnologyClosure parseArgsMifareClassicGetSectorCount(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareClassicGetSectorCount; + } + + private CallResult callMifareClassicGetSectorCount() { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.getSectorCount(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::getSectorCount + + // start of wrapper for MifareClassic::getSize + private final static String METHOD_NAME_MIFARE_CLASSIC_GET_SIZE = "getSize"; + + private TagTechnologyClosure parseArgsMifareClassicGetSize(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareClassicGetSize; + } + + private CallResult callMifareClassicGetSize() { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.getSize(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::getSize + + // start of wrapper for MifareClassic::getTimeout + private final static String METHOD_NAME_MIFARE_CLASSIC_GET_TIMEOUT = "getTimeout"; - public static void onReceive(final Context context, final Intent intent) { - Logger.logDebug(LOG_TAG, "onReceive"); + private TagTechnologyClosure parseArgsMifareClassicGetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareClassicGetTimeout; + } - context.startActivity(new Intent(context, NfcActivity.class).putExtras(intent.getExtras()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - } + private CallResult callMifareClassicGetTimeout() { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.getTimeout(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::getTimeout + // start of wrapper for MifareClassic::getType + private final static String METHOD_NAME_MIFARE_CLASSIC_GET_TYPE = "getType"; + private TagTechnologyClosure parseArgsMifareClassicGetType(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareClassicGetType; + } - public static class NfcActivity extends AppCompatActivity { + private CallResult callMifareClassicGetType() { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.getType(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::getType - private Intent mIntent; - private NfcAdapter mAdapter; - static String socket_input; - static String socket_output; - String mode; - String param; - String value; + // start of wrapper for MifareClassic::increment + private final static String METHOD_NAME_MIFARE_CLASSIC_INCREMENT = "increment"; - private static final String LOG_TAG = "NfcActivity"; + private TagTechnologyClosure parseArgsMifareClassicIncrement(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 2, args.length); + int blockIndex = Utils.parseInt(args[args.length - 2]); + int value = Utils.parseInt(args[args.length - 1]); + return () -> callMifareClassicIncrement(blockIndex, value); + } - //Check for NFC - protected void errorNfc(final Context context, Intent intent, String error) { - ResultReturner.returnData(context, intent, new ResultReturner.ResultJsonWriter() { - @Override - public void writeJson(JsonWriter out) throws Exception { - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context); - out.beginObject(); - if (error.length() > 0) - out.name("error").value(error); - out.name("nfcPresent").value(null != adapter); - if(null!=adapter) - out.name("nfcActive").value(adapter.isEnabled()); - out.endObject(); + private CallResult callMifareClassicIncrement(int blockIndex, int value) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + mifareClassic.increment(blockIndex, value); + return CallResult.success(); + } + // end of wrapper for MifareClassic::increment + + // start of wrapper for MifareClassic::readBlock + private final static String METHOD_NAME_MIFARE_CLASSIC_READ_BLOCK = "readBlock"; + + private TagTechnologyClosure parseArgsMifareClassicReadBlock(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int blockIndex = Utils.parseInt(args[args.length - 1]); + return () -> callMifareClassicReadBlock(blockIndex); + } + + private CallResult callMifareClassicReadBlock(int blockIndex) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + byte[] response = mifareClassic.readBlock(blockIndex); + return CallResult.successWithHex(response); + } + // end of wrapper for MifareClassic::readBlock + + // start of wrapper for MifareClassic::restore + private final static String METHOD_NAME_MIFARE_CLASSIC_RESTORE = "restore"; + + private TagTechnologyClosure parseArgsMifareClassicRestore(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int blockIndex = Utils.parseInt(args[args.length-1]); + return () -> callMifareClassicRestore(blockIndex); + } + + private CallResult callMifareClassicRestore(int blockIndex) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + mifareClassic.restore(blockIndex); + return CallResult.success(); + } + // end of wrapper for MifareClassic::restore + + // start of wrapper for MifareClassic::sectorToBlock + private final static String METHOD_NAME_MIFARE_CLASSIC_SECTOR_TO_BLOCK = "sectorToBlock"; + + private TagTechnologyClosure parseArgsMifareClassicSectorToBlock(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int sectorIndex = Utils.parseInt(args[args.length - 1]); + return () -> callMifareClassicSectorToBlock(sectorIndex); + } + + private CallResult callMifareClassicSectorToBlock(int sectorIndex) { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + int response = mifareClassic.sectorToBlock(sectorIndex); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareClassic::sectorToBlock + + // start of wrapper for MifareClassic::setTimeout + private final static String METHOD_NAME_MIFARE_CLASSIC_SET_TIMEOUT = "setTimeout"; + + private TagTechnologyClosure parseArgsMifareClassicSetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int timeout = Utils.parseInt(args[args.length - 1]); + return () -> callMifareClassicSetTimeout(timeout); + } + + private CallResult callMifareClassicSetTimeout(int timeout) { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + mifareClassic.setTimeout(timeout); + return CallResult.success(); + } + // end of wrapper for MifareClassic::setTimeout + + // start of wrapper for MifareClassic::transceive + private final static String METHOD_NAME_MIFARE_CLASSIC_TRANSCEIVE = "transceive"; + + private TagTechnologyClosure parseArgsMifareClassicTransceive(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] data = Utils.parseHex(args[args.length - 1]); + return () -> callMifareClassicTransceive(data); + } + + private CallResult callMifareClassicTransceive(@NonNull byte[] data) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + byte[] response = mifareClassic.transceive(data); + return CallResult.successWithHex(response); + } + // end of wrapper for MifareClassic::transceive + + + // start of wrapper for MifareClassic::transfer + private final static String METHOD_NAME_MIFARE_CLASSIC_TRANSFER = "transfer"; + + private TagTechnologyClosure parseArgsMifareClassicTransfer(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int blockIndex = Utils.parseInt(args[args.length - 1]); + return () -> callMifareClassicTransfer(blockIndex); + } + + private CallResult callMifareClassicTransfer(int blockIndex) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + mifareClassic.transfer(blockIndex); + return CallResult.success(); + } + // end of wrapper for MifareClassic::transfer + + // start of wrapper for MifareClassic::writeBlock + private final static String METHOD_NAME_MIFARE_CLASSIC_WRITE_BLOCK = "writeBlock"; + + private TagTechnologyClosure parseArgsMifareClassicWriteBlock(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 2, args.length); + int blockIndex = Utils.parseInt(args[args.length - 2]); + @NonNull byte[] data = Utils.parseHex(args[args.length - 1]); + return () -> callMifareClassicWriteBlock(blockIndex, data); + } + + private CallResult callMifareClassicWriteBlock(int blockIndex, @NonNull byte[] data) throws IOException { + @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); + mifareClassic.writeBlock(blockIndex, data); + return CallResult.success(); + } + // end of wrapper for MifareClassic::writeBlock + // end of wrappers for class MifareClassic + + // start of wrappers for class MifareUltralight + // doc: https://developer.android.com/reference/android/nfc/tech/MifareUltralight + + private final static String CLASS_NAME_MIFARE_ULTRALIGHT = "MifareUltralight"; + + // start of wrapper for MifareUltralight::connect (inherited from TagTechnology) + private final static String METHOD_NAME_MIFARE_ULTRALIGHT_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsMifareUltralightConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareUltralightConnect; + } + + private CallResult callMifareUltralightConnect() throws IOException { + return callTagTechnologyConnect(MifareUltralight::get); + } + // end of wrapper for MifareUltralight::connect + + // start of wrapper for MifareUltralight::getMaxTransceiveLength + private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; + + private TagTechnologyClosure parseArgsMifareUltralightGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareUltralightGetMaxTransceiveLength; + } + + private CallResult callMifareUltralightGetMaxTransceiveLength() { + @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); + int response = mifareUltralight.getMaxTransceiveLength(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareUltralight::getMaxTransceiveLength + + // start of wrapper for MifareUltralight::getTimeout + private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_TIMEOUT = "getTimeout"; + + private TagTechnologyClosure parseArgsMifareUltralightGetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareUltralightGetTimeout; + } + + private CallResult callMifareUltralightGetTimeout() { + @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); + int response = mifareUltralight.getTimeout(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareUltralight::getTimeout + + // start of wrapper for MifareUltralight::getType + private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_TYPE = "getType"; + + private TagTechnologyClosure parseArgsMifareUltralightGetType(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callMifareUltralightGetType; + } + + private CallResult callMifareUltralightGetType() { + @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); + int response = mifareUltralight.getType(); + return CallResult.successWithInt(response); + } + // end of wrapper for MifareUltralight::getType + + // start of wrapper for MifareUltralight::readPages + private final static String METHOD_NAME_MIFARE_ULTRALIGHT_READ_PAGES = "readPages"; + + private TagTechnologyClosure parseArgsMifareUltralightReadPages(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int pageOffset = Utils.parseInt(args[args.length - 1]); + return () -> callMifareUltralightReadPages(pageOffset); + } + + private CallResult callMifareUltralightReadPages(int pageOffset) throws IOException { + @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); + byte[] response = mifareUltralight.readPages(pageOffset); + return CallResult.successWithHex(response); + } + // end of wrapper for MifareUltralight::readPages + + // start of wrapper for MifareUltralight::setTimeout + private final static String METHOD_NAME_MIFARE_ULTRALIGHT_SET_TIMEOUT = "setTimeout"; + + private TagTechnologyClosure parseArgsMifareUltralightSetTimeout(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + int timeout = Utils.parseInt(args[args.length - 1]); + return () -> callMifareUltralightSetTimeout(timeout); + } + + private CallResult callMifareUltralightSetTimeout(int timeout) { + @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); + mifareUltralight.setTimeout(timeout); + return CallResult.success(); + } + // end of wrapper for MifareUltralight::setTimeout + + // start of wrapper for MifareUltralight::transceive + private final static String METHOD_NAME_MIFARE_ULTRALIGHT_TRANSCEIVE = "transceive"; + + private TagTechnologyClosure parseArgsMifareUltralightTransceive(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] data = Utils.parseHex(args[args.length - 1]); + return () -> callMifareUltralightTransceive(data); + } + + private CallResult callMifareUltralightTransceive(@NonNull byte[] data) throws IOException { + @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); + byte[] response = mifareUltralight.transceive(data); + return CallResult.successWithHex(response); + } + // end of wrapper for MifareUltralight::transceive + + // start of wrapper for MifareUltralight::writePage + private final static String METHOD_NAME_MIFARE_ULTRALIGHT_WRITE_PAGE = "writePage"; + + private TagTechnologyClosure parseArgsMifareUltralightWritePage(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 2, args.length); + int pageOffset = Utils.parseInt(args[args.length - 2]); + @NonNull byte[] data = Utils.parseHex(args[args.length - 1]); + return () -> callMifareUltralightWritePage(pageOffset, data); + } + + private CallResult callMifareUltralightWritePage(int pageOffset, @NonNull byte[] data) throws IOException { + @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); + mifareUltralight.writePage(pageOffset, data); + return CallResult.success(); + } + // end of wrapper for MifareUltralight::writePage + // end of wrappers for class MifareUltralight + + + // start of wrappers for class NfcBarcode + // doc: https://developer.android.com/reference/android/nfc/tech/NfcBarcode + private final static String CLASS_NAME_NFC_BARCODE = "NfcBarcode"; + + // start of wrapper for NfcBarcode::connect (inherited from TagTechnology) + private final static String METHOD_NAME_NFC_BARCODE_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsNfcBarcodeConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcBarcodeConnect; + } + + private CallResult callNfcBarcodeConnect() throws IOException { + return callTagTechnologyConnect(NfcBarcode::get); + } + // end of wrapper for NfcBarcode::connect + + // start of wrapper for NfcBarcode::getBarcode + private final static String METHOD_NAME_NFC_BARCODE_GET_BARCODE = "getBarcode"; + + private TagTechnologyClosure parseArgsNfcBarcodeGetBarcode(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcBarcodeGetBarcode; + } + + private CallResult callNfcBarcodeGetBarcode() { + @NonNull NfcBarcode nfcBarcode = Utils.liftTagTechnology(mTechnology, NfcBarcode.class); + byte[] response = nfcBarcode.getBarcode(); + return CallResult.successWithHex(response); + } + // end of wrapper for NfcBarcode::getBarcode + + // start of wrapper for NfcBarcode::getType + private final static String METHOD_NAME_NFC_BARCODE_GET_TYPE = "getType"; + + private TagTechnologyClosure parseArgsNfcBarcodeGetType(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNfcBarcodeGetType; + } + + private CallResult callNfcBarcodeGetType() { + @NonNull NfcBarcode nfcBarcode = Utils.liftTagTechnology(mTechnology, NfcBarcode.class); + int response = nfcBarcode.getType(); + return CallResult.successWithInt(response); + } + // end of wrapper for NfcBarcode::getType + // end of wrappers for class NfcBarcode + + // start of wrappers for class NdefFormatable + // doc: https://developer.android.com/reference/android/nfc/tech/NdefFormatable + private final static String CLASS_NAME_NDEF_FORMATABLE = "NdefFormatable"; + + // start of wrapper for NdefFormatable::connect (inherited from TagTechnology) + private final static String METHOD_NAME_NDEF_FORMATABLE_CONNECT = "connect"; + + private TagTechnologyClosure parseArgsNdefFormatableConnect(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2, args.length); + return this::callNdefFormatableConnect; + } + + private CallResult callNdefFormatableConnect() throws IOException { + return callTagTechnologyConnect(NdefFormatable::get); + } + // end of wrapper for NdefFormatable::connect + + // start of wrapper for NdefFormatable::format + private final static String METHOD_NAME_NDEF_FORMATABLE_FORMAT = "format"; + + private TagTechnologyClosure parseArgsNdefFormatableFormat(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] ndefMessageHex = Utils.parseHex(args[args.length - 1]); + return () -> callNdefFormatableFormat(ndefMessageHex); + } + + private CallResult callNdefFormatableFormat(@NonNull byte[] ndefMessageHex) throws IOException, FormatException { + @NonNull NdefFormatable ndefFormatable = Utils.liftTagTechnology(mTechnology, NdefFormatable.class); + ndefFormatable.format(new NdefMessage(ndefMessageHex)); + return CallResult.success(); + } + // end of wrapper for NdefFormatable::format + + // start of wrapper for NdefFormatable::formatReadOnly + private final static String METHOD_NAME_NDEF_FORMATABLE_FORMAT_READ_ONLY = "formatReadOnly"; + + private TagTechnologyClosure parseArgsNdefFormatableFormatReadOnly(@NonNull String[] args) throws ArgumentException { + Utils.checkIntEqual(2 + 1, args.length); + @NonNull byte[] ndefMessageHex = Utils.parseHex(args[args.length - 1]); + return () -> callNdefFormatableFormatReadOnly(ndefMessageHex); + } + + private CallResult callNdefFormatableFormatReadOnly(@NonNull byte[] ndefMessageHex) throws IOException, FormatException { + @NonNull NdefFormatable ndefFormatable = Utils.liftTagTechnology(mTechnology, NdefFormatable.class); + ndefFormatable.formatReadOnly(new NdefMessage(ndefMessageHex)); + return CallResult.success(); + } + // end of wrapper for NdefFormatable::formatReadOnly + // end of wrappers for class NdefFormatable + + // helper function for subclasses' connect() + private CallResult callTagTechnologyConnect(Function tagGetter) throws IOException { + Utils.checkTagNonNull(mTag); + mTechnology = tagGetter.apply(mTag); + if (mTechnology == null) { + throw new UnsupportedTechnology(); + } + mTechnology.connect(); + return CallResult.success(); + } + + TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws ArgumentException { + if (args.length == 0) { + throw new InvalidCommandException(); + } + + switch (args[0]) { + case METHOD_NAME_QUIT: + return parseArgsQuit(args); + + case CLASS_NAME_TAG_TECHNOLOGY: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_TAG_TECHNOLOGY_CLOSE: + return parseArgsTagTechnologyClose(args); + case METHOD_NAME_TAG_TECHNOLOGY_IS_CONNECTED: + return parseArgsTagTechnologyIsConnected(args); + case METHOD_NAME_TAG_TECHNOLOGY_GET_TAG: + return parseArgsTagTechnologyGetTag(args); + default: + throw new InvalidCommandException(); + } } - }); + + case CLASS_NAME_NFC_A: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_NFC_A_CONNECT: + return parseArgsNfcAConnect(args); + case METHOD_NAME_NFC_A_GET_ATQA: + return parseArgsNfcAGetAtqa(args); + case METHOD_NAME_NFC_A_GET_MAX_TRANSCEIVE_LENGTH: + return parseArgsNfcAGetMaxTransceiveLength(args); + case METHOD_NAME_NFC_A_GET_SAK: + return parseArgsNfcAGetSak(args); + case METHOD_NAME_NFC_A_GET_TIMEOUT: + return parseArgsNfcAGetTimeout(args); + case METHOD_NAME_NFC_A_SET_TIMEOUT: + return parseArgsNfcASetTimeout(args); + case METHOD_NAME_NFC_A_TRANSCEIVE: + return parseArgsNfcATransceive(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_NFC_B: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_NFC_B_CONNECT: + return parseArgsNfcBConnect(args); + case METHOD_NAME_NFC_B_GET_APPLICATION_DATA: + return parseArgsNfcBGetApplicationData(args); + case METHOD_NAME_NFC_B_GET_MAX_TRANSCEIVE_LENGTH: + return parseArgsNfcBGetMaxTransceiveLength(args); + case METHOD_NAME_NFC_B_GET_PROTOCOL_INFO: + case METHOD_NAME_NFC_B_TRANSCEIVE: + return parseArgsNfcBTransceive(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_NFC_F: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_NFC_F_CONNECT: + return parseArgsNfcFConnect(args); + case METHOD_NAME_NFC_F_GET_MANUFACTURER: + return parseArgsNfcFGetManufacturer(args); + case METHOD_NAME_NFC_F_GET_MAX_TRANSCEIVE_LENGTH: + return parseArgsNfcFGetMaxTransceiveLength(args); + case METHOD_NAME_NFC_F_GET_SYSTEM_CODE: + return parseArgsNfcFGetSystemCode(args); + case METHOD_NAME_NFC_F_GET_TIMEOUT: + return parseArgsNfcFGetTimeout(args); + case METHOD_NAME_NFC_F_SET_TIMEOUT: + return parseArgsNfcFSetTimeout(args); + case METHOD_NAME_NFC_F_TRANSCEIVE: + return parseArgsNfcFTransceive(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_NFC_V: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_NFC_V_CONNECT: + return parseArgsNfcVConnect(args); + case METHOD_NAME_NFC_V_GET_DSF_ID: + return parseArgsNfcVGetDsfId(args); + case METHOD_NAME_NFC_V_GET_MAX_TRANSCEIVE_LENGTH: + return parseArgsNfcVGetMaxTransceiveLength(args); + case METHOD_NAME_NFC_V_GET_RESPONSE_FLAGS: + return parseArgsNfcVGetResponseFlags(args); + case METHOD_NAME_NFC_V_TRANSCEIVE: + return parseArgsNfcVTransceive(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_ISO_DEP: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_ISO_DEP_CONNECT: + return parseArgsIsoDepConnect(args); + case METHOD_NAME_ISO_DEP_GET_HI_LAYER_RESPONSE: + return parseArgsIsoDepGetHiLayerResponse(args); + case METHOD_NAME_ISO_DEP_GET_HISTORICAL_BYTES: + return parseArgsIsoDepGetHistoricalBytes(args); + case METHOD_NAME_ISO_DEP_GET_MAX_TRANSCEIVE_LENGTH: + return parseArgsIsoDepGetMaxTransceiveLength(args); + case METHOD_NAME_ISO_DEP_GET_TIMEOUT: + return parseArgsIsoDepGetTimeout(args); + case METHOD_NAME_ISO_DEP_IS_EXTENDED_LENGTH_APDU_SUPPORTED: + return parseArgsIsoDepIsExtendedLengthApduSupported(args); + case METHOD_NAME_ISO_DEP_SET_TIMEOUT: + return parseArgsIsoDepSetTimeout(args); + case METHOD_NAME_ISO_DEP_TRANSCEIVE: + return parseArgsIsoDepTransceive(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_NDEF: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_NDEF_CAN_MAKE_READ_ONLY: + return parseArgsNdefCanMakeReadOnly(args); + case METHOD_NAME_NDEF_CONNECT: + return parseArgsNdefConnect(args); + case METHOD_NAME_NDEF_GET_CACHED_NDEF_MESSAGE: + return parseArgsNdefGetCachedNdefMessage(args); + case METHOD_NAME_NDEF_GET_MAX_SIZE: + return parseArgsNdefGetMaxSize(args); + case METHOD_NAME_NDEF_GET_NDEF_MESSAGE: + return parseArgsNdefGetNdefMessage(args); + case METHOD_NAME_NDEF_GET_TYPE: + return parseArgsNdefGetType(args); + case METHOD_NAME_NDEF_IS_WRITABLE: + return parseArgsNdefIsWritable(args); + case METHOD_NAME_NDEF_MAKE_READ_ONLY: + return parseArgsNdefMakeReadOnly(args); + case METHOD_NAME_NDEF_WRITE_NDEF_MESSAGE: + return parseArgsNdefWriteNdefMessage(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_MIFARE_CLASSIC: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_A: + return parseArgsMifareClassicAuthenticateSectorWithKeyA(args); + case METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_B: + return parseArgsMifareClassicAuthenticateSectorWithKeyB(args); + case METHOD_NAME_MIFARE_CLASSIC_BLOCK_TO_SECTOR: + return parseArgsMifareClassicBlockToSector(args); + case METHOD_NAME_MIFARE_CLASSIC_CONNECT: + return parseArgsMifareClassicConnect(args); + case METHOD_NAME_MIFARE_CLASSIC_DECREMENT: + return parseArgsMifareClassicDecrement(args); + case METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT: + return parseArgsMifareClassicGetBlockCount(args); + case METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT_IN_SECTOR: + return parseArgsMifareClassicGetBlockCountInSector(args); + case METHOD_NAME_MIFARE_CLASSIC_GET_MAX_TRANSCEIVE_LENGTH: + return parseArgsMifareClassicGetMaxTransceiveLength(args); + case METHOD_NAME_MIFARE_CLASSIC_GET_SECTOR_COUNT: + return parseArgsMifareClassicGetSectorCount(args); + case METHOD_NAME_MIFARE_CLASSIC_GET_SIZE: + return parseArgsMifareClassicGetSize(args); + case METHOD_NAME_MIFARE_CLASSIC_GET_TIMEOUT: + return parseArgsMifareClassicGetTimeout(args); + case METHOD_NAME_MIFARE_CLASSIC_GET_TYPE: + return parseArgsMifareClassicGetType(args); + case METHOD_NAME_MIFARE_CLASSIC_INCREMENT: + return parseArgsMifareClassicIncrement(args); + case METHOD_NAME_MIFARE_CLASSIC_READ_BLOCK: + return parseArgsMifareClassicReadBlock(args); + case METHOD_NAME_MIFARE_CLASSIC_RESTORE: + return parseArgsMifareClassicRestore(args); + case METHOD_NAME_MIFARE_CLASSIC_SECTOR_TO_BLOCK: + return parseArgsMifareClassicSectorToBlock(args); + case METHOD_NAME_MIFARE_CLASSIC_SET_TIMEOUT: + return parseArgsMifareClassicSetTimeout(args); + case METHOD_NAME_MIFARE_CLASSIC_TRANSCEIVE: + return parseArgsMifareClassicTransceive(args); + case METHOD_NAME_MIFARE_CLASSIC_TRANSFER: + return parseArgsMifareClassicTransfer(args); + case METHOD_NAME_MIFARE_CLASSIC_WRITE_BLOCK: + return parseArgsMifareClassicWriteBlock(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_MIFARE_ULTRALIGHT: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_MIFARE_ULTRALIGHT_CONNECT: + return parseArgsMifareUltralightConnect(args); + case METHOD_NAME_MIFARE_ULTRALIGHT_GET_MAX_TRANSCEIVE_LENGTH: + return parseArgsMifareUltralightGetMaxTransceiveLength(args); + case METHOD_NAME_MIFARE_ULTRALIGHT_GET_TIMEOUT: + return parseArgsMifareUltralightGetTimeout(args); + case METHOD_NAME_MIFARE_ULTRALIGHT_GET_TYPE: + return parseArgsMifareUltralightGetType(args); + case METHOD_NAME_MIFARE_ULTRALIGHT_READ_PAGES: + return parseArgsMifareUltralightReadPages(args); + case METHOD_NAME_MIFARE_ULTRALIGHT_SET_TIMEOUT: + return parseArgsMifareUltralightSetTimeout(args); + case METHOD_NAME_MIFARE_ULTRALIGHT_TRANSCEIVE: + return parseArgsMifareUltralightTransceive(args); + case METHOD_NAME_MIFARE_ULTRALIGHT_WRITE_PAGE: + return parseArgsMifareUltralightWritePage(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_NFC_BARCODE: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_NFC_BARCODE_CONNECT: + return parseArgsNfcBarcodeConnect(args); + case METHOD_NAME_NFC_BARCODE_GET_BARCODE: + return parseArgsNfcBarcodeGetBarcode(args); + case METHOD_NAME_NFC_BARCODE_GET_TYPE: + return parseArgsNfcBarcodeGetType(args); + default: + throw new InvalidCommandException(); + } + } + + case CLASS_NAME_NDEF_FORMATABLE: { + if (args.length < 2) { + throw new InvalidCommandException(); + } + switch (args[1]) { + case METHOD_NAME_NDEF_FORMATABLE_CONNECT: + return parseArgsNdefFormatableConnect(args); + case METHOD_NAME_NDEF_FORMATABLE_FORMAT: + return parseArgsNdefFormatableFormat(args); + case METHOD_NAME_NDEF_FORMATABLE_FORMAT_READ_ONLY: + return parseArgsNdefFormatableFormatReadOnly(args); + default: + throw new InvalidCommandException(); + } + } + + default: + throw new InvalidCommandException(); + } + + } + + TagTechnologyClosure parseClosureFromIntent(Intent intent) throws ArgumentException { + @NonNull String[] args = Utils.collectNumberedArgs(intent, "arg"); + return parseClosureFromArgs(args); } + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { Logger.logDebug(LOG_TAG, "onCreate"); - super.onCreate(savedInstanceState); + Intent intent = this.getIntent(); - if (intent != null) { - mIntent = intent; - mode = intent.getStringExtra("mode"); - if (null == mode) - mode = "noData"; - param = intent.getStringExtra("param"); - if (null == param) - param = "noData"; - value = intent.getStringExtra("value"); - if (null == socket_input) socket_input = intent.getStringExtra("socket_input"); - if (null == socket_output) socket_output = intent.getStringExtra("socket_output"); - if (mode.equals("noData")) { - errorNfc(this, intent,""); - finish(); - return; - } + if (intent == null) { + finish(); + return; + } + + assert intent.hasExtra("socket_input"); + assert intent.hasExtra("socket_output"); + + try { + mDelayedClosure = parseClosureFromIntent(intent); + } + catch (ArgumentException e) { + postException(e); + finish(); + return; } NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); if (adapter == null || !adapter.isEnabled()) { - errorNfc(this, intent,""); + postException(new NfcUnavailableException()); finish(); + return; } + mAdapter = adapter; } @Override protected void onResume() { Logger.logVerbose(LOG_TAG, "onResume"); - super.onResume(); - mAdapter = NfcAdapter.getDefaultAdapter(this); - if (mAdapter == null || !mAdapter.isEnabled()) { - if (mIntent != null) - errorNfc(this, mIntent,""); - finish(); - return; - } - // - https://developer.android.com/develop/connectivity/nfc/advanced-nfc#foreground-dispatch Intent intentNew = new Intent(this, NfcActivity.class).addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intentNew, @@ -117,28 +1869,62 @@ protected void onResume() { mAdapter.enableForegroundDispatch(this, pendingIntent, intentFilter, null); } + private void invokeClosure(TagTechnologyClosure closure) { + try { + CallResult result = closure.call(); + postResult(result); + } catch (Exception e) { + postException(e); + } + } + + private void consumeClosure() { + assert mDelayedClosure != null; + invokeClosure(mDelayedClosure); + mDelayedClosure = null; + } + @Override protected void onNewIntent(Intent intent) { Logger.logDebug(LOG_TAG, "onNewIntent"); + super.onNewIntent(intent); + + if (isFinishing()) { + postException(new ActivityFinishingException()); + return; + } - intent.putExtra("socket_input", socket_input); - intent.putExtra("socket_output", socket_output); + String action = intent.getAction(); + if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) + || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) + || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) { + Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { - try { - postResult(this, intent); - } catch (Exception e) { - Logger.logStackTraceWithMessage(LOG_TAG, "Error posting result" ,e); + assert tag != null; + mTag = tag; + if (mDelayedClosure != null) { + consumeClosure(); } - finish(); + return; } - super.onNewIntent(intent); + + assert intent.hasExtra("socket_input"); + assert intent.hasExtra("socket_output"); + + setIntent(intent); + TagTechnologyClosure closure; + try { + closure = parseClosureFromIntent(intent); + } catch (ArgumentException e) { + postException(e); + return; + } + invokeClosure(closure); } @Override protected void onPause() { Logger.logDebug(LOG_TAG, "onPause"); - mAdapter.disableForegroundDispatch(this); super.onPause(); } @@ -146,194 +1932,46 @@ protected void onPause() { @Override protected void onDestroy() { Logger.logDebug(LOG_TAG, "onDestroy"); - - socket_input = null; - socket_output = null; super.onDestroy(); } - protected void postResult(final Context context, Intent intent) { - ResultReturner.returnData(context, intent, new ResultReturner.ResultJsonWriter() { + void postResult(final CallResult result) { + ResultReturner.returnData(getApplicationContext(), getIntent(), new ResultReturner.ResultJsonWriter() { @Override public void writeJson(JsonWriter out) throws Exception { Logger.logDebug(LOG_TAG, "postResult"); - try { - switch (mode) { - case "write": - switch (param) { - case "text": - Logger.logVerbose(LOG_TAG, "Write start"); - onReceiveNfcWrite(context, intent); - Logger.logVerbose(LOG_TAG, "Write end"); - break; - default: - onUnexpectedAction(out, "Wrong Params", "Should be text for TAG"); - break; - } - break; - case "read": - switch (param){ - case "short": - readNDEFTag(intent,out); - break; - case "full": - readFullNDEFTag(intent,out); - break; - case "noData": - readNDEFTag(intent,out); - break; - default: - onUnexpectedAction(out, "Wrong Params", "Should be correct param value"); - break; - } - break; - default: - onUnexpectedAction(out, "Wrong Params", "Should be correct mode value "); - break; - } - } catch (Exception e){ - onUnexpectedAction(out, "exception", e.getMessage()); - } - } - }); - } - - public void onReceiveNfcWrite( final Context context, Intent intent) throws Exception { - Logger.logVerbose(LOG_TAG, "onReceiveNfcWrite"); - - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context); - Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - NdefRecord record = NdefRecord.createTextRecord("en", value); - NdefMessage msg = new NdefMessage(new NdefRecord[]{record}); - Ndef ndef = Ndef.get(tag); - ndef.connect(); - ndef.writeNdefMessage(msg); - ndef.close(); - } - - - public void readNDEFTag(Intent intent, JsonWriter out) throws Exception { - Logger.logVerbose(LOG_TAG, "readNDEFTag"); + out.beginObject(); - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); - Parcelable[] msgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); - Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - Ndef ndefTag = Ndef.get(tag); - boolean bNdefPresent = false; - String[] strs = tag.getTechList(); - for (String s: strs){ - if (s.equals("android.nfc.tech.Ndef")) { - bNdefPresent = true; - break; - } - } - if (!bNdefPresent){ - onUnexpectedAction(out, "Wrong Technology","termux API support only NFEF Tag"); - return; - } - NdefMessage[] nmsgs = new NdefMessage[msgs.length]; - if (msgs.length == 1) { - nmsgs[0] = (NdefMessage) msgs[0]; - NdefRecord[] records = nmsgs[0].getRecords(); - out.beginObject(); - if (records.length >0 ) { - { - out.name("Record"); - if (records.length > 1) - out.beginArray(); - for (NdefRecord record: records){ - out.beginObject(); - int pos = 1 + record.getPayload()[0]; - pos = (NdefRecord.TNF_WELL_KNOWN==record.getTnf())?(int)record.getPayload()[0]+1:0; - int len = record.getPayload().length - pos; - byte[] msg = new byte[len]; - System.arraycopy(record.getPayload(), pos, msg, 0, len); - out.name("Payload").value(new String(msg)); - out.endObject(); - } - if (records.length > 1) - out.endArray(); + JsonWriter resultWriter = out.name("result"); + Object obj = result.mData; + if (obj == null) { + resultWriter.nullValue(); + } else if (obj instanceof String) { + resultWriter.value((String)obj); + } else if (obj instanceof Integer) { + resultWriter.value((Integer)obj); + } else if (obj instanceof Boolean) { + resultWriter.value((Boolean)obj); + } else { + assert false : "Unexpected invalid result type: " + obj.getClass().getName(); } + + out.endObject(); } - out.endObject(); - } + }); } - public void readFullNDEFTag(Intent intent, JsonWriter out) throws Exception { - Logger.logVerbose(LOG_TAG, "readFullNDEFTag"); - - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); - Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - Ndef ndefTag = Ndef.get(tag); - Parcelable[] msgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); - - String[] strs = tag.getTechList(); - boolean bNdefPresent = false; - for (String s: strs){ - if (s.equals("android.nfc.tech.Ndef")) { - bNdefPresent = true; - break; - } - } - if (!bNdefPresent){ - onUnexpectedAction(out, "Wrong Technology","termux API support only NFEF Tag"); - return; - } - NdefMessage[] nmsgs = new NdefMessage[msgs.length]; - out.beginObject(); - { - byte[] tagID = tag.getId(); - StringBuilder sp = new StringBuilder(); - for (byte tagIDpart : tagID) { sp.append(String.format("%02x", tagIDpart)); } - out.name("id").value(sp.toString()); - out.name("typeTag").value(ndefTag.getType()); - out.name("maxSize").value(ndefTag.getMaxSize()); - out.name("techList"); - { - out.beginArray(); - String[] tlist = tag.getTechList(); - for (String str : tlist) { - out.value(str); - } - out.endArray(); - } - if (msgs.length == 1) { - Logger.logInfo(LOG_TAG, "-->> readFullNDEFTag - 06"); - nmsgs[0] = (NdefMessage) msgs[0]; - NdefRecord[] records = nmsgs[0].getRecords(); - { - out.name("record"); - if (records.length > 1) - out.beginArray(); - for (NdefRecord record : records) { - out.beginObject(); - out.name("type").value(new String(record.getType())); - out.name("tnf").value(record.getTnf()); - if (records[0].toUri() != null) out.name("URI").value(record.toUri().toString()); - out.name("mime").value(record.toMimeType()); - int pos = 1 + record.getPayload()[0]; - pos = (NdefRecord.TNF_WELL_KNOWN==record.getTnf())?(int)record.getPayload()[0]+1:0; - int len = record.getPayload().length - pos; - byte[] msg = new byte[len]; - System.arraycopy(record.getPayload(), pos, msg, 0, len); - out.name("payload").value(new String(msg)); - out.endObject(); - } - if (records.length > 1) out.endArray(); - } + void postException(final Exception e) { + ResultReturner.returnData(getApplicationContext(), getIntent(), new ResultReturner.ResultJsonWriter() { + @Override + public void writeJson(JsonWriter out) throws Exception { + out.beginObject(); + out.name("exceptionType").value(e.getClass().getSimpleName()); + out.name("exceptionMessage").value(e.getMessage()); + out.endObject(); } - - } - out.endObject(); - } - - protected void onUnexpectedAction(JsonWriter out,String error, String description) throws Exception { - out.beginObject(); - out.name("error").value(error); - out.name("description").value(description); - out.endObject(); - out.flush(); + }); } } -} +} \ No newline at end of file diff --git a/app/src/main/res/xml/nfc_tech_filter.xml b/app/src/main/res/xml/nfc_tech_filter.xml index a81b93b8a..769fc0058 100644 --- a/app/src/main/res/xml/nfc_tech_filter.xml +++ b/app/src/main/res/xml/nfc_tech_filter.xml @@ -1,5 +1,14 @@ + android.nfc.tech.IsoDep + android.nfc.tech.MifareClassic + android.nfc.tech.MifareUltralight android.nfc.tech.Ndef + android.nfc.tech.NdefFormatable + android.nfc.tech.NfcA + android.nfc.tech.NfcB + android.nfc.tech.NfcBarcode + android.nfc.tech.NfcF + android.nfc.tech.NfcV From 21073b25bc3c6322700626781e81e80582a285fe Mon Sep 17 00:00:00 2001 From: champignoom Date: Fri, 15 Aug 2025 02:05:27 +0800 Subject: [PATCH 2/3] ues json format --- app/src/main/AndroidManifest.xml | 1 - .../main/java/com/termux/api/apis/NfcAPI.java | 664 +++++++++--------- 2 files changed, 335 insertions(+), 330 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 651f1d1a7..a6504a49c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -113,7 +113,6 @@ diff --git a/app/src/main/java/com/termux/api/apis/NfcAPI.java b/app/src/main/java/com/termux/api/apis/NfcAPI.java index 4ec3fbc2b..8490b15b6 100644 --- a/app/src/main/java/com/termux/api/apis/NfcAPI.java +++ b/app/src/main/java/com/termux/api/apis/NfcAPI.java @@ -1,9 +1,12 @@ package com.termux.api.apis; +import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.nfc.FormatException; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; @@ -20,21 +23,28 @@ import android.nfc.tech.NfcV; import android.nfc.tech.TagTechnology; import android.os.Bundle; -import android.util.JsonWriter; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.content.res.AppCompatResources; import androidx.core.util.Function; +import com.termux.api.R; import com.termux.api.util.PendingIntentUtils; import com.termux.api.util.ResultReturner; import com.termux.shared.logger.Logger; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.concurrent.Semaphore; class NfcException extends RuntimeException { NfcException() { @@ -46,16 +56,20 @@ class NfcException extends RuntimeException { } } -class UnsupportedTechnology extends NfcException {} -class NoConnectionException extends NfcException {} +class UnsupportedTechnology extends NfcException { +} + +class NoConnectionException extends NfcException { +} + class WrongTechnologyException extends NfcException { WrongTechnologyException(@NonNull String expected, @NonNull String found) { super(String.format("%s expected, but %s found", expected, found)); } } -class NfcUnavailableException extends NfcException {} -class ActivityFinishingException extends NfcException {} -class TagNullException extends NfcException {} + +class TagNullException extends NfcException { +} class ArgumentException extends NfcException { ArgumentException() { @@ -66,20 +80,51 @@ class ArgumentException extends NfcException { super(message); } } + class ArgNumberException extends ArgumentException { ArgNumberException(int expected, int found) { super(String.format("%d expected, found %d", expected, found)); } } + class InvalidHexException extends ArgumentException { InvalidHexException(char c) { super(String.format("%c", c)); } } -class InvalidHexLengthException extends ArgumentException {} -class InvalidCommandException extends ArgumentException {} + +class InvalidHexLengthException extends ArgumentException { +} + +class InvalidCommandException extends ArgumentException { +} class Utils { + + static void printLnFlush(PrintWriter out, @NonNull String s) { + out.println(s); + out.flush(); + } + + static JSONObject exceptionToJson(@NonNull Exception e) { + JSONObject obj = new JSONObject(); + try { + obj.put("exceptionType", e.getClass().getName()); + obj.put("exceptionMessage", e.getMessage()); + } catch (JSONException ex) { + assert false : ex.getMessage(); + } + return obj; + } + + static String[] jsonArrayToStringArray(@NonNull JSONArray jsonArray) throws JSONException { + String[] result = new String[jsonArray.length()]; + for (int i = 0; i < jsonArray.length(); ++i) { + result[i] = jsonArray.getString(i); + } + return result; + } + static void checkTechnologyNonNull(@Nullable TagTechnology technology) throws NoConnectionException { if (technology == null) { throw new NoConnectionException(); @@ -93,16 +138,14 @@ static void checkTagNonNull(@Nullable Tag tag) throws TagNullException { } static @NonNull T liftTagTechnology(@Nullable TagTechnology technology, @NonNull Class tClass) - throws NoConnectionException, WrongTechnologyException - { + throws NoConnectionException, WrongTechnologyException { checkTechnologyNonNull(technology); try { T techLifted = tClass.cast(technology); assert techLifted != null; return techLifted; - } - catch (ClassCastException e) { + } catch (ClassCastException e) { throw new WrongTechnologyException(tClass.getSimpleName(), technology.getClass().getSimpleName()); } } @@ -115,32 +158,33 @@ static void checkIntEqual(int expectedLength, int argLength) throws ArgNumberExc static byte parseHexOne(char c) throws ArgumentException { if ('0' <= c && c <= '9') - return (byte)((byte)c - '0'); + return (byte) ((byte) c - '0'); if ('a' <= c && c <= 'f') - return (byte)((byte)c - 'a' + 10); + return (byte) ((byte) c - 'a' + 10); if ('A' <= c && c <= 'F') - return (byte)((byte)c - 'A' + 10); + return (byte) ((byte) c - 'A' + 10); throw new InvalidHexException(c); } static byte parseHexTwo(char high, char low) throws ArgumentException { - return (byte)((parseHexOne(high) << 4) | parseHexOne(low)); + return (byte) ((parseHexOne(high) << 4) | parseHexOne(low)); } /** * Parse hex representation of byte[] array. + * * @param hex the hex representation, e.g. "DEADBEEF" * @return the byte[] array, e.g. {0xde, 0xad, 0xbe, 0xef}. * @throws ArgumentException if it fails to parse. */ static byte[] parseHex(@NonNull String hex) throws ArgumentException { - if (hex.length()%2 != 0 || hex.isEmpty()) { + if (hex.length() % 2 != 0 || hex.isEmpty()) { throw new InvalidHexLengthException(); } - byte[] result = new byte[hex.length()/2]; - for (int i=0; i "DEADBEEF" + * * @param data the byte array to be formatted * @return the formatted hex representation */ static String formatHex(@NonNull byte[] data) { StringBuilder sb = new StringBuilder(); - for (byte b: data) { + for (byte b : data) { sb.append(Utils.formatHexOne(b)); } return sb.toString(); } - - - /** - * Collect "arg1" "arg2" "arg3" ... from intent extras, - * where the prefix "arg" can be specified. - * - * @param intent the intent - * @param argPrefix the prefix, usually "arg" - * - * @return collected arguments - */ - static String[] collectNumberedArgs(Intent intent, @NonNull String argPrefix) { - List result = new ArrayList<>(); - - for (int index=1; true; ++index) { - String argName = argPrefix + index; - String argValue = intent.getStringExtra(argName); - if (argValue == null) { - break; - } - result.add(argValue); - } - - return result.toArray(new String[0]); - } } /** * Return value of wrapped NFC API call. */ class CallResult { - @Nullable Object mData; + @Nullable + Object mData; CallResult(@Nullable Object data) { mData = data; @@ -227,13 +247,25 @@ static CallResult successWithBool(boolean data) { static CallResult success() { return new CallResult(null); } + + @NonNull + JSONObject toJson() { + JSONObject obj = new JSONObject(); + assert mData == null || mData instanceof String || mData instanceof Integer || mData instanceof Boolean; + try { + obj.put("result", mData == null ? JSONObject.NULL : mData); + } catch (JSONException e) { + assert false : e.getMessage(); + } + return obj; + } } /** * Single abstract method for NFC API invocation. */ interface TagTechnologyClosure { - CallResult call() throws IOException, android.nfc.FormatException, NfcException; + CallResult call() throws IOException, InterruptedException, android.nfc.FormatException, NfcException; } public class NfcAPI { @@ -252,34 +284,17 @@ public static void onReceive(final Context context, final Intent intent) { context.startActivity(newIntent); } - - public static class NfcActivity extends AppCompatActivity { - // Closure for the first API call that is delayed after the discovery of tag. - private TagTechnologyClosure mDelayedClosure; - + static class NfcManager { // The result of connect() private @Nullable TagTechnology mTechnology; // The last discovered tag private Tag mTag; + private final Semaphore mTagSemaphore; - private NfcAdapter mAdapter; + private final Activity mActivity; - private static final String LOG_TAG = "NfcActivity"; - - // start of wrapper for quit - private final static String METHOD_NAME_QUIT = "quit"; - - private TagTechnologyClosure parseArgsQuit(final @NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - return this::callQuit; - } - - private CallResult callQuit() { - finish(); - return CallResult.success(); - } - // end of wrapper for quit + private final Intent mIntent; // start of wrappers for abstract class TagTechnology // doc: https://developer.android.com/reference/android/nfc/tech/TagTechnology @@ -289,7 +304,7 @@ private CallResult callQuit() { private final static String METHOD_NAME_TAG_TECHNOLOGY_IS_CONNECTED = "isConnected"; private TagTechnologyClosure parseArgsTagTechnologyIsConnected(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callTagTechnologyIsConnected; } @@ -304,7 +319,7 @@ private CallResult callTagTechnologyIsConnected() { private final static String METHOD_NAME_TAG_TECHNOLOGY_CLOSE = "close"; private TagTechnologyClosure parseArgsTagTechnologyClose(final @NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callTagTechnologyClose; } @@ -319,7 +334,7 @@ private CallResult callTagTechnologyClose() throws IOException { private final static String METHOD_NAME_TAG_TECHNOLOGY_GET_TAG = "getTag"; private TagTechnologyClosure parseArgsTagTechnologyGetTag(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callTagTechnologyGetTag; } @@ -338,7 +353,7 @@ private CallResult callTagTechnologyGetTag() { private final static String METHOD_NAME_NFC_A_CONNECT = "connect"; private TagTechnologyClosure parseArgsNfcAConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcAConnect; } @@ -351,7 +366,7 @@ private CallResult callNfcAConnect() throws IOException { private final static String METHOD_NAME_NFC_A_GET_ATQA = "getAtqa"; private TagTechnologyClosure parseArgsNfcAGetAtqa(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcAGetAtqa; } @@ -366,7 +381,7 @@ private CallResult callNfcAGetAtqa() { private final static String METHOD_NAME_NFC_A_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; private TagTechnologyClosure parseArgsNfcAGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcAGetMaxTransceiveLength; } @@ -381,7 +396,7 @@ private CallResult callNfcAGetMaxTransceiveLength() { private final static String METHOD_NAME_NFC_A_GET_SAK = "getSak"; private TagTechnologyClosure parseArgsNfcAGetSak(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcAGetSak; } @@ -396,7 +411,7 @@ private CallResult callNfcAGetSak() { private final static String METHOD_NAME_NFC_A_GET_TIMEOUT = "getTimeout"; private TagTechnologyClosure parseArgsNfcAGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcAGetTimeout; } @@ -411,8 +426,8 @@ private CallResult callNfcAGetTimeout() { private final static String METHOD_NAME_NFC_A_SET_TIMEOUT = "setTimeout"; private TagTechnologyClosure parseArgsNfcASetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int timeout = Utils.parseInt(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + int timeout = Utils.parseInt(args[0]); return () -> callNfcASetTimeout(timeout); } @@ -427,8 +442,8 @@ private CallResult callNfcASetTimeout(int timeout) { private final static String METHOD_NAME_NFC_A_TRANSCEIVE = "transceive"; private TagTechnologyClosure parseArgsNfcATransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] data = Utils.parseHex(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] data = Utils.parseHex(args[0]); return () -> callNfcATransceive(data); } @@ -448,7 +463,7 @@ private CallResult callNfcATransceive(byte[] data) throws IOException { private final static String METHOD_NAME_NFC_B_CONNECT = "connect"; private TagTechnologyClosure parseArgsNfcBConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcBConnect; } @@ -461,7 +476,7 @@ private CallResult callNfcBConnect() throws IOException { private final static String METHOD_NAME_NFC_B_GET_APPLICATION_DATA = "getApplicationData"; private TagTechnologyClosure parseArgsNfcBGetApplicationData(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcBGetApplicationData; } @@ -476,7 +491,7 @@ private CallResult callNfcBGetApplicationData() { private final static String METHOD_NAME_NFC_B_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; private TagTechnologyClosure parseArgsNfcBGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcBGetMaxTransceiveLength; } @@ -491,7 +506,7 @@ private CallResult callNfcBGetMaxTransceiveLength() { private final static String METHOD_NAME_NFC_B_GET_PROTOCOL_INFO = "getProtocolInfo"; private TagTechnologyClosure parseArgsNfcBGetProtocolInfo(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcBGetProtocolInfo; } @@ -506,8 +521,8 @@ private CallResult callNfcBGetProtocolInfo() { private final static String METHOD_NAME_NFC_B_TRANSCEIVE = "transceive"; private TagTechnologyClosure parseArgsNfcBTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] data = Utils.parseHex(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] data = Utils.parseHex(args[0]); return () -> callNfcBTransceive(data); } @@ -527,7 +542,7 @@ private CallResult callNfcBTransceive(byte[] data) throws IOException { private final static String METHOD_NAME_NFC_F_CONNECT = "connect"; private TagTechnologyClosure parseArgsNfcFConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcFConnect; } @@ -540,7 +555,7 @@ private CallResult callNfcFConnect() throws IOException { private final static String METHOD_NAME_NFC_F_GET_MANUFACTURER = "getManufacturer"; private TagTechnologyClosure parseArgsNfcFGetManufacturer(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcFGetManufacturer; } @@ -555,7 +570,7 @@ private CallResult callNfcFGetManufacturer() { private final static String METHOD_NAME_NFC_F_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; private TagTechnologyClosure parseArgsNfcFGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcFGetMaxTransceiveLength; } @@ -570,7 +585,7 @@ private CallResult callNfcFGetMaxTransceiveLength() { private final static String METHOD_NAME_NFC_F_GET_SYSTEM_CODE = "getSystemCode"; private TagTechnologyClosure parseArgsNfcFGetSystemCode(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcFGetSystemCode; } @@ -585,7 +600,7 @@ private CallResult callNfcFGetSystemCode() { private final static String METHOD_NAME_NFC_F_GET_TIMEOUT = "getTimeout"; private TagTechnologyClosure parseArgsNfcFGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcFGetTimeout; } @@ -600,8 +615,8 @@ private CallResult callNfcFGetTimeout() { private final static String METHOD_NAME_NFC_F_SET_TIMEOUT = "setTimeout"; private TagTechnologyClosure parseArgsNfcFSetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int timeout = Utils.parseInt(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + int timeout = Utils.parseInt(args[0]); return () -> callNfcFSetTimeout(timeout); } @@ -616,8 +631,8 @@ private CallResult callNfcFSetTimeout(int timeout) { private final static String METHOD_NAME_NFC_F_TRANSCEIVE = "transceive"; private TagTechnologyClosure parseArgsNfcFTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] data = Utils.parseHex(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] data = Utils.parseHex(args[0]); return () -> callNfcFTransceive(data); } @@ -637,7 +652,7 @@ private CallResult callNfcFTransceive(byte[] data) throws IOException { private final static String METHOD_NAME_NFC_V_CONNECT = "connect"; private TagTechnologyClosure parseArgsNfcVConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcVConnect; } @@ -650,7 +665,7 @@ private CallResult callNfcVConnect() throws IOException { private final static String METHOD_NAME_NFC_V_GET_DSF_ID = "getDsfId"; private TagTechnologyClosure parseArgsNfcVGetDsfId(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcVGetDsfId; } @@ -665,7 +680,7 @@ private CallResult callNfcVGetDsfId() { private final static String METHOD_NAME_NFC_V_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; private TagTechnologyClosure parseArgsNfcVGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcVGetMaxTransceiveLength; } @@ -680,7 +695,7 @@ private CallResult callNfcVGetMaxTransceiveLength() { private final static String METHOD_NAME_NFC_V_GET_RESPONSE_FLAGS = "getResponseFlags"; private TagTechnologyClosure parseArgsNfcVGetResponseFlags(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcVGetResponseFlags; } @@ -695,8 +710,8 @@ private CallResult callNfcVGetResponseFlags() { private final static String METHOD_NAME_NFC_V_TRANSCEIVE = "transceive"; private TagTechnologyClosure parseArgsNfcVTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] data = Utils.parseHex(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] data = Utils.parseHex(args[0]); return () -> callNfcVTransceive(data); } @@ -716,7 +731,7 @@ private CallResult callNfcVTransceive(byte[] data) throws IOException { private final static String METHOD_NAME_ISO_DEP_CONNECT = "connect"; private TagTechnologyClosure parseArgsIsoDepConnect(final @NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callIsoDepConnect; } @@ -729,7 +744,7 @@ private CallResult callIsoDepConnect() throws IOException { private final static String METHOD_NAME_ISO_DEP_GET_HI_LAYER_RESPONSE = "getHiLayerResponse"; private TagTechnologyClosure parseArgsIsoDepGetHiLayerResponse(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callIsoDepGetHiLayerResponse; } @@ -744,7 +759,7 @@ private CallResult callIsoDepGetHiLayerResponse() { private final static String METHOD_NAME_ISO_DEP_GET_HISTORICAL_BYTES = "getHistoricalBytes"; private TagTechnologyClosure parseArgsIsoDepGetHistoricalBytes(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callGetHistoricalBytes; } @@ -759,7 +774,7 @@ private CallResult callGetHistoricalBytes() { private final static String METHOD_NAME_ISO_DEP_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; private TagTechnologyClosure parseArgsIsoDepGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callGetMaxTransceiveLength; } @@ -775,7 +790,7 @@ private CallResult callGetMaxTransceiveLength() { private final static String METHOD_NAME_ISO_DEP_GET_TIMEOUT = "getTimeout"; private TagTechnologyClosure parseArgsIsoDepGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callGetTimeout; } @@ -790,7 +805,7 @@ private CallResult callGetTimeout() { private final static String METHOD_NAME_ISO_DEP_IS_EXTENDED_LENGTH_APDU_SUPPORTED = "isExtendedLengthApduSupported"; private TagTechnologyClosure parseArgsIsoDepIsExtendedLengthApduSupported(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callIsExtendedLengthApduSupported; } @@ -805,8 +820,8 @@ private CallResult callIsExtendedLengthApduSupported() { private final static String METHOD_NAME_ISO_DEP_SET_TIMEOUT = "setTimeout"; private TagTechnologyClosure parseArgsIsoDepSetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int timeout = Utils.parseInt(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + int timeout = Utils.parseInt(args[0]); return () -> callSetTimeout(timeout); } @@ -821,9 +836,9 @@ private CallResult callSetTimeout(int timeout) { private final static String METHOD_NAME_ISO_DEP_TRANSCEIVE = "transceive"; private TagTechnologyClosure parseArgsIsoDepTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); + Utils.checkIntEqual(1, args.length); - @NonNull byte[] requestData = Utils.parseHex(args[args.length-1]); + @NonNull byte[] requestData = Utils.parseHex(args[0]); return () -> callIsoDepTransceive(requestData); } @@ -843,7 +858,7 @@ private CallResult callIsoDepTransceive(@NonNull byte[] requestData) throws NfcE private final static String METHOD_NAME_NDEF_CONNECT = "connect"; private TagTechnologyClosure parseArgsNdefConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefConnect; } @@ -856,7 +871,7 @@ private CallResult callNdefConnect() throws IOException { private final static String METHOD_NAME_NDEF_CAN_MAKE_READ_ONLY = "canMakeReadOnly"; private TagTechnologyClosure parseArgsNdefCanMakeReadOnly(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefCanMakeReadOnly; } @@ -871,7 +886,7 @@ private CallResult callNdefCanMakeReadOnly() { private final static String METHOD_NAME_NDEF_GET_CACHED_NDEF_MESSAGE = "getCachedNdefMessage"; private TagTechnologyClosure parseArgsNdefGetCachedNdefMessage(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefGetCachedNdefMessage; } @@ -886,7 +901,7 @@ private CallResult callNdefGetCachedNdefMessage() { private final static String METHOD_NAME_NDEF_GET_MAX_SIZE = "getMaxSize"; private TagTechnologyClosure parseArgsNdefGetMaxSize(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefGetMaxSize; } @@ -901,7 +916,7 @@ private CallResult callNdefGetMaxSize() { private final static String METHOD_NAME_NDEF_GET_NDEF_MESSAGE = "getNdefMessage"; private TagTechnologyClosure parseArgsNdefGetNdefMessage(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefGetNdefMessage; } @@ -916,7 +931,7 @@ private CallResult callNdefGetNdefMessage() throws IOException, android.nfc.Form private final static String METHOD_NAME_NDEF_GET_TYPE = "getType"; private TagTechnologyClosure parseArgsNdefGetType(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefGetType; } @@ -931,7 +946,7 @@ private CallResult callNdefGetType() { private final static String METHOD_NAME_NDEF_IS_WRITABLE = "isWritable"; private TagTechnologyClosure parseArgsNdefIsWritable(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefIsWritable; } @@ -946,7 +961,7 @@ private CallResult callNdefIsWritable() { private final static String METHOD_NAME_NDEF_MAKE_READ_ONLY = "makeReadOnly"; private TagTechnologyClosure parseArgsNdefMakeReadOnly(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefMakeReadOnly; } @@ -961,8 +976,8 @@ private CallResult callNdefMakeReadOnly() throws IOException { private final static String METHOD_NAME_NDEF_WRITE_NDEF_MESSAGE = "writeNdefMessage"; private TagTechnologyClosure parseArgsNdefWriteNdefMessage(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] msgHex = Utils.parseHex(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] msgHex = Utils.parseHex(args[0]); return () -> callNdefWriteNdefMessage(msgHex); } @@ -982,9 +997,9 @@ private CallResult callNdefWriteNdefMessage(@NonNull byte[] msgHex) throws Forma private final static String METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_A = "authenticateSectorWithKeyA"; private TagTechnologyClosure parseArgsMifareClassicAuthenticateSectorWithKeyA(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 2, args.length); - int sectorIndex = Utils.parseInt(args[args.length - 2]); - @NonNull byte[] key = Utils.parseHex(args[args.length - 1]); + Utils.checkIntEqual(2, args.length); + int sectorIndex = Utils.parseInt(args[0]); + @NonNull byte[] key = Utils.parseHex(args[1]); return () -> callMifareClassicAuthenticateSectorWithKeyA(sectorIndex, key); } @@ -999,9 +1014,9 @@ private CallResult callMifareClassicAuthenticateSectorWithKeyA(int sectorIndex, private final static String METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_B = "authenticateSectorWithKeyB"; private TagTechnologyClosure parseArgsMifareClassicAuthenticateSectorWithKeyB(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 2, args.length); - int sectorIndex = Utils.parseInt(args[args.length - 2]); - @NonNull byte[] key = Utils.parseHex(args[args.length - 1]); + Utils.checkIntEqual(2, args.length); + int sectorIndex = Utils.parseInt(args[0]); + @NonNull byte[] key = Utils.parseHex(args[1]); return () -> callMifareClassicAuthenticateSectorWithKeyB(sectorIndex, key); } @@ -1016,8 +1031,8 @@ private CallResult callMifareClassicAuthenticateSectorWithKeyB(int sectorIndex, private final static String METHOD_NAME_MIFARE_CLASSIC_BLOCK_TO_SECTOR = "blockToSector"; private TagTechnologyClosure parseArgsMifareClassicBlockToSector(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int blockIndex = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + int blockIndex = Utils.parseInt(args[0]); return () -> callMifareClassicBlockToSector(blockIndex); } @@ -1032,7 +1047,7 @@ private CallResult callMifareClassicBlockToSector(int blockIndex) { private final static String METHOD_NAME_MIFARE_CLASSIC_CONNECT = "connect"; private TagTechnologyClosure parseArgsMifareClassicConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareClassicConnect; } @@ -1045,9 +1060,9 @@ private CallResult callMifareClassicConnect() throws IOException { private final static String METHOD_NAME_MIFARE_CLASSIC_DECREMENT = "decrement"; private TagTechnologyClosure parseArgsMifareClassicDecrement(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 2, args.length); - int blockIndex = Utils.parseInt(args[args.length - 2]); - int value = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(2, args.length); + int blockIndex = Utils.parseInt(args[0]); + int value = Utils.parseInt(args[1]); return () -> callMifareClassicDecrement(blockIndex, value); } @@ -1062,7 +1077,7 @@ private CallResult callMifareClassicDecrement(int blockIndex, int value) throws private final static String METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT = "getBlockCount"; private TagTechnologyClosure parseArgsMifareClassicGetBlockCount(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareClassicGetBlockCount; } @@ -1077,8 +1092,8 @@ private CallResult callMifareClassicGetBlockCount() { private final static String METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT_IN_SECTOR = "getBlockCountInSector"; private TagTechnologyClosure parseArgsMifareClassicGetBlockCountInSector(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int sectorIndex = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + int sectorIndex = Utils.parseInt(args[0]); return () -> callMifareClassicGetBlockCountInSector(sectorIndex); } @@ -1093,7 +1108,7 @@ private CallResult callMifareClassicGetBlockCountInSector(int sectorIndex) { private final static String METHOD_NAME_MIFARE_CLASSIC_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; private TagTechnologyClosure parseArgsMifareClassicGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareClassicGetMaxTransceiveLength; } @@ -1108,7 +1123,7 @@ private CallResult callMifareClassicGetMaxTransceiveLength() { private final static String METHOD_NAME_MIFARE_CLASSIC_GET_SECTOR_COUNT = "getSectorCount"; private TagTechnologyClosure parseArgsMifareClassicGetSectorCount(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareClassicGetSectorCount; } @@ -1123,7 +1138,7 @@ private CallResult callMifareClassicGetSectorCount() { private final static String METHOD_NAME_MIFARE_CLASSIC_GET_SIZE = "getSize"; private TagTechnologyClosure parseArgsMifareClassicGetSize(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareClassicGetSize; } @@ -1138,7 +1153,7 @@ private CallResult callMifareClassicGetSize() { private final static String METHOD_NAME_MIFARE_CLASSIC_GET_TIMEOUT = "getTimeout"; private TagTechnologyClosure parseArgsMifareClassicGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareClassicGetTimeout; } @@ -1153,7 +1168,7 @@ private CallResult callMifareClassicGetTimeout() { private final static String METHOD_NAME_MIFARE_CLASSIC_GET_TYPE = "getType"; private TagTechnologyClosure parseArgsMifareClassicGetType(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareClassicGetType; } @@ -1168,9 +1183,9 @@ private CallResult callMifareClassicGetType() { private final static String METHOD_NAME_MIFARE_CLASSIC_INCREMENT = "increment"; private TagTechnologyClosure parseArgsMifareClassicIncrement(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 2, args.length); - int blockIndex = Utils.parseInt(args[args.length - 2]); - int value = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(2, args.length); + int blockIndex = Utils.parseInt(args[0]); + int value = Utils.parseInt(args[1]); return () -> callMifareClassicIncrement(blockIndex, value); } @@ -1185,8 +1200,8 @@ private CallResult callMifareClassicIncrement(int blockIndex, int value) throws private final static String METHOD_NAME_MIFARE_CLASSIC_READ_BLOCK = "readBlock"; private TagTechnologyClosure parseArgsMifareClassicReadBlock(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int blockIndex = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + int blockIndex = Utils.parseInt(args[0]); return () -> callMifareClassicReadBlock(blockIndex); } @@ -1201,8 +1216,8 @@ private CallResult callMifareClassicReadBlock(int blockIndex) throws IOException private final static String METHOD_NAME_MIFARE_CLASSIC_RESTORE = "restore"; private TagTechnologyClosure parseArgsMifareClassicRestore(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int blockIndex = Utils.parseInt(args[args.length-1]); + Utils.checkIntEqual(1, args.length); + int blockIndex = Utils.parseInt(args[0]); return () -> callMifareClassicRestore(blockIndex); } @@ -1217,8 +1232,8 @@ private CallResult callMifareClassicRestore(int blockIndex) throws IOException { private final static String METHOD_NAME_MIFARE_CLASSIC_SECTOR_TO_BLOCK = "sectorToBlock"; private TagTechnologyClosure parseArgsMifareClassicSectorToBlock(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int sectorIndex = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + int sectorIndex = Utils.parseInt(args[0]); return () -> callMifareClassicSectorToBlock(sectorIndex); } @@ -1233,8 +1248,8 @@ private CallResult callMifareClassicSectorToBlock(int sectorIndex) { private final static String METHOD_NAME_MIFARE_CLASSIC_SET_TIMEOUT = "setTimeout"; private TagTechnologyClosure parseArgsMifareClassicSetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int timeout = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + int timeout = Utils.parseInt(args[0]); return () -> callMifareClassicSetTimeout(timeout); } @@ -1249,8 +1264,8 @@ private CallResult callMifareClassicSetTimeout(int timeout) { private final static String METHOD_NAME_MIFARE_CLASSIC_TRANSCEIVE = "transceive"; private TagTechnologyClosure parseArgsMifareClassicTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] data = Utils.parseHex(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] data = Utils.parseHex(args[0]); return () -> callMifareClassicTransceive(data); } @@ -1266,8 +1281,8 @@ private CallResult callMifareClassicTransceive(@NonNull byte[] data) throws IOEx private final static String METHOD_NAME_MIFARE_CLASSIC_TRANSFER = "transfer"; private TagTechnologyClosure parseArgsMifareClassicTransfer(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int blockIndex = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + int blockIndex = Utils.parseInt(args[0]); return () -> callMifareClassicTransfer(blockIndex); } @@ -1282,9 +1297,9 @@ private CallResult callMifareClassicTransfer(int blockIndex) throws IOException private final static String METHOD_NAME_MIFARE_CLASSIC_WRITE_BLOCK = "writeBlock"; private TagTechnologyClosure parseArgsMifareClassicWriteBlock(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 2, args.length); - int blockIndex = Utils.parseInt(args[args.length - 2]); - @NonNull byte[] data = Utils.parseHex(args[args.length - 1]); + Utils.checkIntEqual(2, args.length); + int blockIndex = Utils.parseInt(args[0]); + @NonNull byte[] data = Utils.parseHex(args[1]); return () -> callMifareClassicWriteBlock(blockIndex, data); } @@ -1305,7 +1320,7 @@ private CallResult callMifareClassicWriteBlock(int blockIndex, @NonNull byte[] d private final static String METHOD_NAME_MIFARE_ULTRALIGHT_CONNECT = "connect"; private TagTechnologyClosure parseArgsMifareUltralightConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareUltralightConnect; } @@ -1318,7 +1333,7 @@ private CallResult callMifareUltralightConnect() throws IOException { private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; private TagTechnologyClosure parseArgsMifareUltralightGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareUltralightGetMaxTransceiveLength; } @@ -1333,7 +1348,7 @@ private CallResult callMifareUltralightGetMaxTransceiveLength() { private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_TIMEOUT = "getTimeout"; private TagTechnologyClosure parseArgsMifareUltralightGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareUltralightGetTimeout; } @@ -1348,7 +1363,7 @@ private CallResult callMifareUltralightGetTimeout() { private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_TYPE = "getType"; private TagTechnologyClosure parseArgsMifareUltralightGetType(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callMifareUltralightGetType; } @@ -1363,8 +1378,8 @@ private CallResult callMifareUltralightGetType() { private final static String METHOD_NAME_MIFARE_ULTRALIGHT_READ_PAGES = "readPages"; private TagTechnologyClosure parseArgsMifareUltralightReadPages(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int pageOffset = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + int pageOffset = Utils.parseInt(args[0]); return () -> callMifareUltralightReadPages(pageOffset); } @@ -1379,8 +1394,8 @@ private CallResult callMifareUltralightReadPages(int pageOffset) throws IOExcept private final static String METHOD_NAME_MIFARE_ULTRALIGHT_SET_TIMEOUT = "setTimeout"; private TagTechnologyClosure parseArgsMifareUltralightSetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - int timeout = Utils.parseInt(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + int timeout = Utils.parseInt(args[0]); return () -> callMifareUltralightSetTimeout(timeout); } @@ -1395,8 +1410,8 @@ private CallResult callMifareUltralightSetTimeout(int timeout) { private final static String METHOD_NAME_MIFARE_ULTRALIGHT_TRANSCEIVE = "transceive"; private TagTechnologyClosure parseArgsMifareUltralightTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] data = Utils.parseHex(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] data = Utils.parseHex(args[0]); return () -> callMifareUltralightTransceive(data); } @@ -1411,9 +1426,9 @@ private CallResult callMifareUltralightTransceive(@NonNull byte[] data) throws I private final static String METHOD_NAME_MIFARE_ULTRALIGHT_WRITE_PAGE = "writePage"; private TagTechnologyClosure parseArgsMifareUltralightWritePage(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 2, args.length); - int pageOffset = Utils.parseInt(args[args.length - 2]); - @NonNull byte[] data = Utils.parseHex(args[args.length - 1]); + Utils.checkIntEqual(2, args.length); + int pageOffset = Utils.parseInt(args[0]); + @NonNull byte[] data = Utils.parseHex(args[1]); return () -> callMifareUltralightWritePage(pageOffset, data); } @@ -1434,7 +1449,7 @@ private CallResult callMifareUltralightWritePage(int pageOffset, @NonNull byte[] private final static String METHOD_NAME_NFC_BARCODE_CONNECT = "connect"; private TagTechnologyClosure parseArgsNfcBarcodeConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcBarcodeConnect; } @@ -1447,7 +1462,7 @@ private CallResult callNfcBarcodeConnect() throws IOException { private final static String METHOD_NAME_NFC_BARCODE_GET_BARCODE = "getBarcode"; private TagTechnologyClosure parseArgsNfcBarcodeGetBarcode(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcBarcodeGetBarcode; } @@ -1462,7 +1477,7 @@ private CallResult callNfcBarcodeGetBarcode() { private final static String METHOD_NAME_NFC_BARCODE_GET_TYPE = "getType"; private TagTechnologyClosure parseArgsNfcBarcodeGetType(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNfcBarcodeGetType; } @@ -1482,7 +1497,7 @@ private CallResult callNfcBarcodeGetType() { private final static String METHOD_NAME_NDEF_FORMATABLE_CONNECT = "connect"; private TagTechnologyClosure parseArgsNdefFormatableConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); + Utils.checkIntEqual(0, args.length); return this::callNdefFormatableConnect; } @@ -1495,8 +1510,8 @@ private CallResult callNdefFormatableConnect() throws IOException { private final static String METHOD_NAME_NDEF_FORMATABLE_FORMAT = "format"; private TagTechnologyClosure parseArgsNdefFormatableFormat(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] ndefMessageHex = Utils.parseHex(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] ndefMessageHex = Utils.parseHex(args[0]); return () -> callNdefFormatableFormat(ndefMessageHex); } @@ -1511,8 +1526,8 @@ private CallResult callNdefFormatableFormat(@NonNull byte[] ndefMessageHex) thro private final static String METHOD_NAME_NDEF_FORMATABLE_FORMAT_READ_ONLY = "formatReadOnly"; private TagTechnologyClosure parseArgsNdefFormatableFormatReadOnly(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2 + 1, args.length); - @NonNull byte[] ndefMessageHex = Utils.parseHex(args[args.length - 1]); + Utils.checkIntEqual(1, args.length); + @NonNull byte[] ndefMessageHex = Utils.parseHex(args[0]); return () -> callNdefFormatableFormatReadOnly(ndefMessageHex); } @@ -1526,7 +1541,9 @@ private CallResult callNdefFormatableFormatReadOnly(@NonNull byte[] ndefMessageH // helper function for subclasses' connect() private CallResult callTagTechnologyConnect(Function tagGetter) throws IOException { - Utils.checkTagNonNull(mTag); + if (mTag == null) { + throw new TagNullException(); + } mTechnology = tagGetter.apply(mTag); if (mTechnology == null) { throw new UnsupportedTechnology(); @@ -1535,20 +1552,24 @@ private CallResult callTagTechnologyConnect(Function mActivity.moveTaskToBack(true)); + return CallResult.success(); + } + + TagTechnologyClosure parseClosureFromClassMethodArgs(@NonNull String className, @NonNull String methodName, @NonNull String[] args) { + switch (className) { case CLASS_NAME_TAG_TECHNOLOGY: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_TAG_TECHNOLOGY_CLOSE: return parseArgsTagTechnologyClose(args); case METHOD_NAME_TAG_TECHNOLOGY_IS_CONNECTED: @@ -1561,10 +1582,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_NFC_A: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_NFC_A_CONNECT: return parseArgsNfcAConnect(args); case METHOD_NAME_NFC_A_GET_ATQA: @@ -1585,10 +1603,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_NFC_B: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_NFC_B_CONNECT: return parseArgsNfcBConnect(args); case METHOD_NAME_NFC_B_GET_APPLICATION_DATA: @@ -1596,6 +1611,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen case METHOD_NAME_NFC_B_GET_MAX_TRANSCEIVE_LENGTH: return parseArgsNfcBGetMaxTransceiveLength(args); case METHOD_NAME_NFC_B_GET_PROTOCOL_INFO: + return parseArgsNfcBGetProtocolInfo(args); case METHOD_NAME_NFC_B_TRANSCEIVE: return parseArgsNfcBTransceive(args); default: @@ -1604,10 +1620,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_NFC_F: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_NFC_F_CONNECT: return parseArgsNfcFConnect(args); case METHOD_NAME_NFC_F_GET_MANUFACTURER: @@ -1628,10 +1641,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_NFC_V: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_NFC_V_CONNECT: return parseArgsNfcVConnect(args); case METHOD_NAME_NFC_V_GET_DSF_ID: @@ -1648,10 +1658,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_ISO_DEP: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_ISO_DEP_CONNECT: return parseArgsIsoDepConnect(args); case METHOD_NAME_ISO_DEP_GET_HI_LAYER_RESPONSE: @@ -1674,10 +1681,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_NDEF: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_NDEF_CAN_MAKE_READ_ONLY: return parseArgsNdefCanMakeReadOnly(args); case METHOD_NAME_NDEF_CONNECT: @@ -1702,10 +1706,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_MIFARE_CLASSIC: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_A: return parseArgsMifareClassicAuthenticateSectorWithKeyA(args); case METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_B: @@ -1752,10 +1753,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_MIFARE_ULTRALIGHT: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_MIFARE_ULTRALIGHT_CONNECT: return parseArgsMifareUltralightConnect(args); case METHOD_NAME_MIFARE_ULTRALIGHT_GET_MAX_TRANSCEIVE_LENGTH: @@ -1778,10 +1776,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_NFC_BARCODE: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_NFC_BARCODE_CONNECT: return parseArgsNfcBarcodeConnect(args); case METHOD_NAME_NFC_BARCODE_GET_BARCODE: @@ -1794,10 +1789,7 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen } case CLASS_NAME_NDEF_FORMATABLE: { - if (args.length < 2) { - throw new InvalidCommandException(); - } - switch (args[1]) { + switch (methodName) { case METHOD_NAME_NDEF_FORMATABLE_CONNECT: return parseArgsNdefFormatableConnect(args); case METHOD_NAME_NDEF_FORMATABLE_FORMAT: @@ -1812,20 +1804,118 @@ TagTechnologyClosure parseClosureFromArgs(@NonNull String[] args) throws Argumen default: throw new InvalidCommandException(); } + } + + private final String JSON_KEY_OP_NAME = "op"; + private final String JSON_VALUE_OP_API = "api"; + private final String JSON_VALUE_OP_DISCOVER_TAG = "discoverTag"; + private final String JSON_KEY_CLASS_NAME = "class"; + private final String JSON_KEY_METHOD_NAME = "method"; + private final String JSON_KEY_ARGS = "args"; + + TagTechnologyClosure parseClosureFromLine(@NonNull String line) throws ArgumentException { + JSONObject jsonObject; + String operationName; + + try { + jsonObject = new JSONObject(line); + operationName = jsonObject.getString(JSON_KEY_OP_NAME); + } catch (JSONException e) { + throw new ArgumentException(e.getMessage()); + } + switch (operationName) { + case JSON_VALUE_OP_DISCOVER_TAG: + return this::discoverTag; + case JSON_VALUE_OP_API: + return parseClosureFromJsonApi(jsonObject); + default: + throw new InvalidCommandException(); + } } - TagTechnologyClosure parseClosureFromIntent(Intent intent) throws ArgumentException { - @NonNull String[] args = Utils.collectNumberedArgs(intent, "arg"); - return parseClosureFromArgs(args); + TagTechnologyClosure parseClosureFromJsonApi(@NonNull JSONObject jsonObject) throws ArgumentException { + String className; + String methodName; + String[] args; + + try { + className = jsonObject.getString(JSON_KEY_CLASS_NAME); + methodName = jsonObject.getString(JSON_KEY_METHOD_NAME); + JSONArray jsonArray = jsonObject.getJSONArray(JSON_KEY_ARGS); + args = Utils.jsonArrayToStringArray(jsonArray); + } catch (JSONException e) { + throw new ArgumentException(e.getMessage()); + } + + return parseClosureFromClassMethodArgs(className, methodName, args); + } + + NfcManager(Activity activity, Intent intent) { + mActivity = activity; + mIntent = intent; + mTagSemaphore = new Semaphore(0); + } + + void listenAsync() { + ResultReturner.returnData(mActivity, mIntent, new ResultReturner.WithInput() { + @Override + public void writeResult(PrintWriter out) throws Exception { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + TagTechnologyClosure closure; + try { + closure = parseClosureFromLine(line); + } catch (ArgumentException e) { + Utils.printLnFlush(out, Utils.exceptionToJson(e).toString()); + continue; + } + + CallResult result; + try { + result = closure.call(); + } catch (Exception e) { + Utils.printLnFlush(out, Utils.exceptionToJson(e).toString()); + continue; + } + + Utils.printLnFlush(out, result.toJson().toString()); + } + + mActivity.runOnUiThread(mActivity::finishAndRemoveTask); + } + }); } + private synchronized void setTag(@NonNull Tag tag) { + mTag = tag; + mTagSemaphore.release(); + } + + private synchronized void clearTag() { + mTag = null; + mTagSemaphore.drainPermits(); + } + } + + public static class NfcActivity extends AppCompatActivity { + private static final String LOG_TAG = "NfcActivity"; + NfcManager mNfcManager; + NfcAdapter mAdapter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { Logger.logDebug(LOG_TAG, "onCreate"); super.onCreate(savedInstanceState); + View view = new View(this); + Drawable drawable = AppCompatResources.getDrawable(this, R.drawable.ic_nfc_black_24dp); + view.setBackground(drawable); + getWindow().getDecorView().setBackgroundColor(Color.argb(128, 255, 255, 255)); + setContentView(view); + moveTaskToBack(true); + Intent intent = this.getIntent(); if (intent == null) { finish(); @@ -1835,22 +1925,14 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { assert intent.hasExtra("socket_input"); assert intent.hasExtra("socket_output"); - try { - mDelayedClosure = parseClosureFromIntent(intent); - } - catch (ArgumentException e) { - postException(e); + mAdapter = NfcAdapter.getDefaultAdapter(this); + if (mAdapter == null || !mAdapter.isEnabled()) { finish(); return; } - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); - if (adapter == null || !adapter.isEnabled()) { - postException(new NfcUnavailableException()); - finish(); - return; - } - mAdapter = adapter; + mNfcManager = new NfcManager(this, intent); + mNfcManager.listenAsync(); } @Override @@ -1869,57 +1951,20 @@ protected void onResume() { mAdapter.enableForegroundDispatch(this, pendingIntent, intentFilter, null); } - private void invokeClosure(TagTechnologyClosure closure) { - try { - CallResult result = closure.call(); - postResult(result); - } catch (Exception e) { - postException(e); - } - } - - private void consumeClosure() { - assert mDelayedClosure != null; - invokeClosure(mDelayedClosure); - mDelayedClosure = null; - } - @Override protected void onNewIntent(Intent intent) { Logger.logDebug(LOG_TAG, "onNewIntent"); super.onNewIntent(intent); - if (isFinishing()) { - postException(new ActivityFinishingException()); - return; - } - String action = intent.getAction(); if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - assert tag != null; - mTag = tag; - if (mDelayedClosure != null) { - consumeClosure(); - } - return; - } - - assert intent.hasExtra("socket_input"); - assert intent.hasExtra("socket_output"); - - setIntent(intent); - TagTechnologyClosure closure; - try { - closure = parseClosureFromIntent(intent); - } catch (ArgumentException e) { - postException(e); + mNfcManager.setTag(tag); return; } - invokeClosure(closure); } @Override @@ -1934,44 +1979,5 @@ protected void onDestroy() { Logger.logDebug(LOG_TAG, "onDestroy"); super.onDestroy(); } - - void postResult(final CallResult result) { - ResultReturner.returnData(getApplicationContext(), getIntent(), new ResultReturner.ResultJsonWriter() { - @Override - public void writeJson(JsonWriter out) throws Exception { - Logger.logDebug(LOG_TAG, "postResult"); - out.beginObject(); - - JsonWriter resultWriter = out.name("result"); - Object obj = result.mData; - if (obj == null) { - resultWriter.nullValue(); - } else if (obj instanceof String) { - resultWriter.value((String)obj); - } else if (obj instanceof Integer) { - resultWriter.value((Integer)obj); - } else if (obj instanceof Boolean) { - resultWriter.value((Boolean)obj); - } else { - assert false : "Unexpected invalid result type: " + obj.getClass().getName(); - } - - out.endObject(); - } - }); - } - - void postException(final Exception e) { - ResultReturner.returnData(getApplicationContext(), getIntent(), new ResultReturner.ResultJsonWriter() { - @Override - public void writeJson(JsonWriter out) throws Exception { - out.beginObject(); - out.name("exceptionType").value(e.getClass().getSimpleName()); - out.name("exceptionMessage").value(e.getMessage()); - out.endObject(); - } - }); - } } - } \ No newline at end of file From 2cc1cc693635cd481171e124d5110f69479c821d Mon Sep 17 00:00:00 2001 From: champignoom Date: Wed, 20 Aug 2025 02:23:27 +0800 Subject: [PATCH 3/3] use reflection to generate arg parser and result formatter --- .../main/java/com/termux/api/apis/NfcAPI.java | 2179 +++++------------ 1 file changed, 599 insertions(+), 1580 deletions(-) diff --git a/app/src/main/java/com/termux/api/apis/NfcAPI.java b/app/src/main/java/com/termux/api/apis/NfcAPI.java index 8490b15b6..b2d68cdc6 100644 --- a/app/src/main/java/com/termux/api/apis/NfcAPI.java +++ b/app/src/main/java/com/termux/api/apis/NfcAPI.java @@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable; import android.nfc.FormatException; import android.nfc.NdefMessage; +import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.IsoDep; @@ -29,7 +30,6 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.util.Function; import com.termux.api.R; import com.termux.api.util.PendingIntentUtils; @@ -41,9 +41,13 @@ import org.json.JSONObject; import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Semaphore; class NfcException extends RuntimeException { @@ -67,7 +71,6 @@ class WrongTechnologyException extends NfcException { super(String.format("%s expected, but %s found", expected, found)); } } - class TagNullException extends NfcException { } @@ -100,59 +103,53 @@ class InvalidCommandException extends ArgumentException { } class Utils { - - static void printLnFlush(PrintWriter out, @NonNull String s) { - out.println(s); - out.flush(); - } - - static JSONObject exceptionToJson(@NonNull Exception e) { - JSONObject obj = new JSONObject(); - try { - obj.put("exceptionType", e.getClass().getName()); - obj.put("exceptionMessage", e.getMessage()); - } catch (JSONException ex) { - assert false : ex.getMessage(); + static Map getOrCreate2dMap(@NonNull Map> map, @NonNull K0 key) { + Map result = map.get(key); + if (result != null) { + return result; } - return obj; - } - static String[] jsonArrayToStringArray(@NonNull JSONArray jsonArray) throws JSONException { - String[] result = new String[jsonArray.length()]; - for (int i = 0; i < jsonArray.length(); ++i) { - result[i] = jsonArray.getString(i); - } + result = new HashMap<>(); + map.put(key, result); return result; } - static void checkTechnologyNonNull(@Nullable TagTechnology technology) throws NoConnectionException { - if (technology == null) { - throw new NoConnectionException(); + static @Nullable V get2dMap(@NonNull Map> map, @NonNull K0 k0, @NonNull K1 k1) { + Map map1 = map.get(k0); + if (map1 == null) { + return null; } + return map1.get(k1); } - static void checkTagNonNull(@Nullable Tag tag) throws TagNullException { - if (tag == null) { - throw new TagNullException(); + static Object invokePublicMethod(@NonNull Method method, @Nullable Object obj, @Nullable Object... args) throws Throwable { + try { + return method.invoke(obj, args); + } catch (IllegalAccessException e) { + assert false : "Unexpected exception: " + e; + return null; } } - static @NonNull T liftTagTechnology(@Nullable TagTechnology technology, @NonNull Class tClass) - throws NoConnectionException, WrongTechnologyException { - checkTechnologyNonNull(technology); + static void printLnFlush(PrintWriter out, @NonNull String s) { + out.println(s); + out.flush(); + } + static JSONObject throwableToJson(@NonNull Throwable e) { + JSONObject obj = new JSONObject(); try { - T techLifted = tClass.cast(technology); - assert techLifted != null; - return techLifted; - } catch (ClassCastException e) { - throw new WrongTechnologyException(tClass.getSimpleName(), technology.getClass().getSimpleName()); + obj.put(JSONConstant.KEY_OP_STATUS, JSONConstant.VALUE_OP_ERROR); + obj.put(JSONConstant.KEY_OP_ERROR_TYPE, e.getClass().getName()); + obj.put(JSONConstant.KEY_OP_ERROR_MESSAGE, e.getMessage()); + } catch (JSONException ex) { + assert false : ex; } + return obj; } - - static void checkIntEqual(int expectedLength, int argLength) throws ArgNumberException { - if (expectedLength != argLength) { - throw new ArgNumberException(expectedLength, argLength); + static void checkTechnologyNonNull(@Nullable TagTechnology technology) throws NoConnectionException { + if (technology == null) { + throw new NoConnectionException(); } } @@ -190,14 +187,6 @@ static byte[] parseHex(@NonNull String hex) throws ArgumentException { return result; } - static int parseInt(String number) throws ArgumentException { - try { - return Integer.parseInt(number); - } catch (NumberFormatException e) { - throw new ArgumentException(e.getMessage()); - } - } - static String formatHexOne(byte b) { return String.format("%02X", b); } @@ -220,1683 +209,714 @@ static String formatHex(@NonNull byte[] data) { /** * Return value of wrapped NFC API call. */ -class CallResult { - @Nullable - Object mData; - CallResult(@Nullable Object data) { - mData = data; - } - static CallResult successWithString(@NonNull String data) { - return new CallResult(data); - } +class JSONConstant { + static final String KEY_OP_STATUS = "status"; + static final String VALUE_OP_SUCCESS = "success"; + static final String KEY_OP_RESULT = "result"; + static final String VALUE_OP_ERROR = "error"; + static final String KEY_OP_ERROR_TYPE = "exceptionType"; + static final String KEY_OP_ERROR_MESSAGE = "exceptionMessage"; - static CallResult successWithHex(@NonNull byte[] data) { - return new CallResult(Utils.formatHex(data)); - } + static final String KEY_OP_NAME = "op"; + static final String VALUE_OP_API = "api"; + static final String VALUE_OP_DISCOVER_TAG = "discoverTag"; + + static final String KEY_CLASS_NAME = "class"; + static final String KEY_METHOD_NAME = "method"; + static final String KEY_ARGS = "args"; + + static final String KEY_BYTES_FORMAT = "format"; + static final String VALUE_BYTES_FORMAT_HEX = "hex"; + static final String VALUE_BYTES_FORMAT_RAW = "raw"; + + static final String KEY_BYTES_VALUE = "value"; - static CallResult successWithInt(int data) { - return new CallResult(data); + static final String KEY_TAG_ID = "id"; + static final String KEY_TAG_TECH_LIST = "techList"; + + static final String KEY_NDEF_RECORD_ID = "id"; + static final String KEY_NDEF_RECORD_PAYLOAD = "payload"; + static final String KEY_NDEF_RECORD_TNF = "tnf"; + static final String KEY_NDEF_RECORD_TYPE = "type"; +} + +class MaybeVoidResult { + final boolean mIsVoid; + final Object mResult; + + MaybeVoidResult(boolean isVoid, Object result) { + assert (!isVoid) || (result == null); + mIsVoid = isVoid; + mResult = result; } - static CallResult successWithBool(boolean data) { - return new CallResult(data); + static MaybeVoidResult theVoid() { + return new MaybeVoidResult(true, null); } - static CallResult success() { - return new CallResult(null); + static MaybeVoidResult nonVoid(@Nullable Object result) { + return new MaybeVoidResult(false, result); } - @NonNull JSONObject toJson() { + assert (!mIsVoid) || (mResult == null); + assert mResult == null || mResult instanceof String || mResult instanceof Integer || mResult instanceof Boolean || mResult instanceof JSONObject || mResult instanceof JSONArray : mResult.getClass().getName(); + JSONObject obj = new JSONObject(); - assert mData == null || mData instanceof String || mData instanceof Integer || mData instanceof Boolean; try { - obj.put("result", mData == null ? JSONObject.NULL : mData); + obj.put(JSONConstant.KEY_OP_STATUS, JSONConstant.VALUE_OP_SUCCESS); + if (!mIsVoid) { + obj.put(JSONConstant.KEY_OP_RESULT, mResult == null ? JSONObject.NULL : mResult); + } } catch (JSONException e) { - assert false : e.getMessage(); + assert false : "Unexpected JSON exception: " + e; } return obj; } } -/** - * Single abstract method for NFC API invocation. - */ -interface TagTechnologyClosure { - CallResult call() throws IOException, InterruptedException, android.nfc.FormatException, NfcException; +interface ApiInvoker { + // no argument, return JSON type + MaybeVoidResult invoke(@NonNull Object[] args) throws Throwable; } -public class NfcAPI { - - private static final String LOG_TAG = "NfcAPI"; - - public static void onReceive(final Context context, final Intent intent) { - Logger.logDebug(LOG_TAG, "onReceive"); - - Intent newIntent = new Intent(context, NfcActivity.class); - Bundle extras = intent.getExtras(); - if (extras == null) { - return; - } - newIntent.putExtras(extras).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(newIntent); +class Parser { + interface t { + // parse JSON types to Java types + Object parse(Object arg) throws ArgumentException; } - static class NfcManager { - // The result of connect() - private @Nullable TagTechnology mTechnology; - - // The last discovered tag - private Tag mTag; - private final Semaphore mTagSemaphore; - - private final Activity mActivity; - - private final Intent mIntent; - - // start of wrappers for abstract class TagTechnology - // doc: https://developer.android.com/reference/android/nfc/tech/TagTechnology - final static String CLASS_NAME_TAG_TECHNOLOGY = "TagTechnology"; - - // start of wrapper for TagTechnology::isConnected - private final static String METHOD_NAME_TAG_TECHNOLOGY_IS_CONNECTED = "isConnected"; - - private TagTechnologyClosure parseArgsTagTechnologyIsConnected(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callTagTechnologyIsConnected; - } - - private CallResult callTagTechnologyIsConnected() { - Utils.checkTechnologyNonNull(mTechnology); - boolean response = mTechnology.isConnected(); - return CallResult.successWithBool(response); - } - // end of wrapper for TagTechnology::isConnected - - // start of wrapper for TagTechnology::close - private final static String METHOD_NAME_TAG_TECHNOLOGY_CLOSE = "close"; - - private TagTechnologyClosure parseArgsTagTechnologyClose(final @NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callTagTechnologyClose; - } - - private CallResult callTagTechnologyClose() throws IOException { - Utils.checkTechnologyNonNull(mTechnology); - mTechnology.close(); - return CallResult.success(); - } - // end of wrapper for TagTechnology::close - - // start of wrapper for TagTechnology::getTag - private final static String METHOD_NAME_TAG_TECHNOLOGY_GET_TAG = "getTag"; - - private TagTechnologyClosure parseArgsTagTechnologyGetTag(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callTagTechnologyGetTag; - } - - private CallResult callTagTechnologyGetTag() { - Utils.checkTagNonNull(mTag); - String description = mTag.toString(); - return CallResult.successWithString(description); - } - // end of wrappers for abstract class TagTechnology - - // start of wrappers for class NfcA - // doc: https://developer.android.com/reference/android/nfc/tech/NfcA - private static final String CLASS_NAME_NFC_A = "NfcA"; - - // start of wrapper for NfcA::connect - private final static String METHOD_NAME_NFC_A_CONNECT = "connect"; - - private TagTechnologyClosure parseArgsNfcAConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcAConnect; - } - - private CallResult callNfcAConnect() throws IOException { - return callTagTechnologyConnect(NfcA::get); - } - // end of wrapper for NfcA::connect - - // start of wrapper for NfcA::getAtqa - private final static String METHOD_NAME_NFC_A_GET_ATQA = "getAtqa"; - - private TagTechnologyClosure parseArgsNfcAGetAtqa(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcAGetAtqa; - } - - private CallResult callNfcAGetAtqa() { - @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); - byte[] response = nfcA.getAtqa(); - return CallResult.successWithHex(response); - } - // end of wrapper for NfcA::getAtqa - - // start of wrapper for NfcA::getMaxTransceiveLength - private final static String METHOD_NAME_NFC_A_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; - - private TagTechnologyClosure parseArgsNfcAGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcAGetMaxTransceiveLength; - } - - private CallResult callNfcAGetMaxTransceiveLength() { - @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); - int response = nfcA.getMaxTransceiveLength(); - return CallResult.successWithInt(response); - } - // end of wrapper for NfcA::getMaxTransceiveLength - - // start of wrapper for NfcA::getSak - private final static String METHOD_NAME_NFC_A_GET_SAK = "getSak"; - - private TagTechnologyClosure parseArgsNfcAGetSak(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcAGetSak; - } - - private CallResult callNfcAGetSak() { - @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); - short response = nfcA.getSak(); - return CallResult.successWithInt(response); - } - // end of wrapper for NfcA::getSak - - // start of wrapper for NfcA::getTimeout - private final static String METHOD_NAME_NFC_A_GET_TIMEOUT = "getTimeout"; - - private TagTechnologyClosure parseArgsNfcAGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcAGetTimeout; - } - - private CallResult callNfcAGetTimeout() { - @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); - int response = nfcA.getTimeout(); - return CallResult.successWithInt(response); - } - // end of wrapper for NfcA::getTimeout - - // start of wrapper for NfcA::setTimeout - private final static String METHOD_NAME_NFC_A_SET_TIMEOUT = "setTimeout"; - - private TagTechnologyClosure parseArgsNfcASetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int timeout = Utils.parseInt(args[0]); - return () -> callNfcASetTimeout(timeout); - } - - private CallResult callNfcASetTimeout(int timeout) { - @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); - nfcA.setTimeout(timeout); - return CallResult.success(); - } - // end of wrapper for NfcA::setTimeout - - // start of wrapper for NfcA::transceive - private final static String METHOD_NAME_NFC_A_TRANSCEIVE = "transceive"; - - private TagTechnologyClosure parseArgsNfcATransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] data = Utils.parseHex(args[0]); - return () -> callNfcATransceive(data); - } - - private CallResult callNfcATransceive(byte[] data) throws IOException { - @NonNull NfcA nfcA = Utils.liftTagTechnology(mTechnology, NfcA.class); - byte[] response = nfcA.transceive(data); - return CallResult.successWithHex(response); - } - // end of wrapper for NfcA::transceive - // end of wrappers for class NfcA - - // start of wrappers for class NfcB - // doc: https://developer.android.com/reference/android/nfc/tech/NfcB - private static final String CLASS_NAME_NFC_B = "NfcB"; - - // start of wrapper for NfcB::connect - private final static String METHOD_NAME_NFC_B_CONNECT = "connect"; - - private TagTechnologyClosure parseArgsNfcBConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcBConnect; - } - - private CallResult callNfcBConnect() throws IOException { - return callTagTechnologyConnect(NfcB::get); - } - // end of wrapper for NfcB::connect - - // start of wrapper for NfcB::getApplicationData - private final static String METHOD_NAME_NFC_B_GET_APPLICATION_DATA = "getApplicationData"; - - private TagTechnologyClosure parseArgsNfcBGetApplicationData(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcBGetApplicationData; - } - - private CallResult callNfcBGetApplicationData() { - @NonNull NfcB nfcB = Utils.liftTagTechnology(mTechnology, NfcB.class); - byte[] response = nfcB.getApplicationData(); - return CallResult.successWithHex(response); + static private T checkedCast(Object obj, Class cls) { + if (cls.isInstance(obj)) { + return (T) obj; } - // end of wrapper for NfcB::getApplicationData - - // start of wrapper for NfcB::getMaxTransceiveLength - private final static String METHOD_NAME_NFC_B_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; - - private TagTechnologyClosure parseArgsNfcBGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcBGetMaxTransceiveLength; - } - - private CallResult callNfcBGetMaxTransceiveLength() { - @NonNull NfcB nfcB = Utils.liftTagTechnology(mTechnology, NfcB.class); - int response = nfcB.getMaxTransceiveLength(); - return CallResult.successWithInt(response); - } - // end of wrapper for NfcB::getMaxTransceiveLength - - // start of wrapper for NfcB::getProtocolInfo - private final static String METHOD_NAME_NFC_B_GET_PROTOCOL_INFO = "getProtocolInfo"; - - private TagTechnologyClosure parseArgsNfcBGetProtocolInfo(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcBGetProtocolInfo; - } - - private CallResult callNfcBGetProtocolInfo() { - @NonNull NfcB nfcB = Utils.liftTagTechnology(mTechnology, NfcB.class); - byte[] response = nfcB.getProtocolInfo(); - return CallResult.successWithHex(response); - } - // end of wrapper for NfcB::getProtocolInfo - - // start of wrapper for NfcB::transceive - private final static String METHOD_NAME_NFC_B_TRANSCEIVE = "transceive"; - - private TagTechnologyClosure parseArgsNfcBTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] data = Utils.parseHex(args[0]); - return () -> callNfcBTransceive(data); - } - - private CallResult callNfcBTransceive(byte[] data) throws IOException { - @NonNull NfcB nfcB = Utils.liftTagTechnology(mTechnology, NfcB.class); - byte[] response = nfcB.transceive(data); - return CallResult.successWithHex(response); - } - // end of wrapper for NfcB::transceive - // end of wrappers for class NfcB - - // start of wrappers for class NfcF - // doc: https://developer.android.com/reference/android/nfc/tech/NfcF - private static final String CLASS_NAME_NFC_F = "NfcF"; - - // start of wrapper for NfcF::connect - private final static String METHOD_NAME_NFC_F_CONNECT = "connect"; - - private TagTechnologyClosure parseArgsNfcFConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcFConnect; - } - - private CallResult callNfcFConnect() throws IOException { - return callTagTechnologyConnect(NfcF::get); - } - // end of wrapper for NfcF::connect - - // start of wrapper for NfcF::getManufacturer - private final static String METHOD_NAME_NFC_F_GET_MANUFACTURER = "getManufacturer"; - - private TagTechnologyClosure parseArgsNfcFGetManufacturer(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcFGetManufacturer; - } - - private CallResult callNfcFGetManufacturer() { - @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); - byte[] response = nfcF.getManufacturer(); - return CallResult.successWithHex(response); - } - // end of wrapper for NfcF::getManufacturer - - // start of wrapper for NfcF::getMaxTransceiveLength - private final static String METHOD_NAME_NFC_F_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; - - private TagTechnologyClosure parseArgsNfcFGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcFGetMaxTransceiveLength; - } - - private CallResult callNfcFGetMaxTransceiveLength() { - @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); - int response = nfcF.getMaxTransceiveLength(); - return CallResult.successWithInt(response); - } - // end of wrapper for NfcF::getMaxTransceiveLength - - // start of wrapper for NfcF::getSystemCode - private final static String METHOD_NAME_NFC_F_GET_SYSTEM_CODE = "getSystemCode"; - - private TagTechnologyClosure parseArgsNfcFGetSystemCode(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcFGetSystemCode; - } - - private CallResult callNfcFGetSystemCode() { - @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); - byte[] response = nfcF.getSystemCode(); - return CallResult.successWithHex(response); - } - // end of wrapper for NfcF::getSystemCode - - // start of wrapper for NfcF::getTimeout - private final static String METHOD_NAME_NFC_F_GET_TIMEOUT = "getTimeout"; - - private TagTechnologyClosure parseArgsNfcFGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcFGetTimeout; - } - - private CallResult callNfcFGetTimeout() { - @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); - int response = nfcF.getTimeout(); - return CallResult.successWithInt(response); - } - // end of wrapper for NfcF::getTimeout - - // start of wrapper for NfcF::setTimeout - private final static String METHOD_NAME_NFC_F_SET_TIMEOUT = "setTimeout"; - - private TagTechnologyClosure parseArgsNfcFSetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int timeout = Utils.parseInt(args[0]); - return () -> callNfcFSetTimeout(timeout); - } - - private CallResult callNfcFSetTimeout(int timeout) { - @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); - nfcF.setTimeout(timeout); - return CallResult.success(); - } - // end of wrapper for NfcF::setTimeout - - // start of wrapper for NfcF::transceive - private final static String METHOD_NAME_NFC_F_TRANSCEIVE = "transceive"; - - private TagTechnologyClosure parseArgsNfcFTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] data = Utils.parseHex(args[0]); - return () -> callNfcFTransceive(data); - } - - private CallResult callNfcFTransceive(byte[] data) throws IOException { - @NonNull NfcF nfcF = Utils.liftTagTechnology(mTechnology, NfcF.class); - byte[] response = nfcF.transceive(data); - return CallResult.successWithHex(response); - } - // end of wrapper for NfcF::transceive - // end of wrappers for class NfcF - - // start of wrappers for class NfcV - // doc: https://developer.android.com/reference/android/nfc/tech/NfcV - private static final String CLASS_NAME_NFC_V = "NfcV"; - - // start of wrapper for NfcV::connect - private final static String METHOD_NAME_NFC_V_CONNECT = "connect"; - - private TagTechnologyClosure parseArgsNfcVConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcVConnect; - } - - private CallResult callNfcVConnect() throws IOException { - return callTagTechnologyConnect(NfcV::get); - } - // end of wrapper for NfcV::connect - - // start of wrapper for NfcV::getDsfId - private final static String METHOD_NAME_NFC_V_GET_DSF_ID = "getDsfId"; - - private TagTechnologyClosure parseArgsNfcVGetDsfId(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcVGetDsfId; - } - - private CallResult callNfcVGetDsfId() { - @NonNull NfcV nfcV = Utils.liftTagTechnology(mTechnology, NfcV.class); - byte response = nfcV.getDsfId(); - return CallResult.successWithString(Utils.formatHexOne(response)); - } - // end of wrapper for NfcV::getDsfId - - // start of wrapper for NfcV::getMaxTransceiveLength - private final static String METHOD_NAME_NFC_V_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; - - private TagTechnologyClosure parseArgsNfcVGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcVGetMaxTransceiveLength; - } - - private CallResult callNfcVGetMaxTransceiveLength() { - @NonNull NfcV nfcV = Utils.liftTagTechnology(mTechnology, NfcV.class); - int response = nfcV.getMaxTransceiveLength(); - return CallResult.successWithInt(response); - } - // end of wrapper for NfcV::getMaxTransceiveLength - - // start of wrapper for NfcV::getResponseFlags - private final static String METHOD_NAME_NFC_V_GET_RESPONSE_FLAGS = "getResponseFlags"; - - private TagTechnologyClosure parseArgsNfcVGetResponseFlags(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcVGetResponseFlags; - } - - private CallResult callNfcVGetResponseFlags() { - @NonNull NfcV nfcV = Utils.liftTagTechnology(mTechnology, NfcV.class); - byte response = nfcV.getResponseFlags(); - return CallResult.successWithString(Utils.formatHexOne(response)); - } - // end of wrapper for NfcV::getResponseFlags - - // start of wrapper for NfcV::transceive - private final static String METHOD_NAME_NFC_V_TRANSCEIVE = "transceive"; - - private TagTechnologyClosure parseArgsNfcVTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] data = Utils.parseHex(args[0]); - return () -> callNfcVTransceive(data); - } - - private CallResult callNfcVTransceive(byte[] data) throws IOException { - @NonNull NfcV nfcV = Utils.liftTagTechnology(mTechnology, NfcV.class); - byte[] response = nfcV.transceive(data); - return CallResult.successWithHex(response); - } - // end of wrapper for NfcV::transceive - // end of wrappers for class NfcV - - // start of wrappers for class IsoDep - // doc: https://developer.android.com/reference/android/nfc/tech/IsoDep - private static final String CLASS_NAME_ISO_DEP = "IsoDep"; - - // start of wrapper for IsoDep::connect - private final static String METHOD_NAME_ISO_DEP_CONNECT = "connect"; - - private TagTechnologyClosure parseArgsIsoDepConnect(final @NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callIsoDepConnect; - } - - private CallResult callIsoDepConnect() throws IOException { - return callTagTechnologyConnect(IsoDep::get); - } - // end of wrapper for IsoDep::connect - - // start of wrapper for IsoDep::getHiLayerResponse - private final static String METHOD_NAME_ISO_DEP_GET_HI_LAYER_RESPONSE = "getHiLayerResponse"; - - private TagTechnologyClosure parseArgsIsoDepGetHiLayerResponse(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callIsoDepGetHiLayerResponse; - } - - private CallResult callIsoDepGetHiLayerResponse() { - @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); - byte[] response = isoDep.getHiLayerResponse(); - return CallResult.successWithHex(response); - } - // end of wrapper for IsoDep::getHiLayerResponse - - // start of wrapper for IsoDep::getHistoricalBytes - private final static String METHOD_NAME_ISO_DEP_GET_HISTORICAL_BYTES = "getHistoricalBytes"; - - private TagTechnologyClosure parseArgsIsoDepGetHistoricalBytes(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callGetHistoricalBytes; - } - - private CallResult callGetHistoricalBytes() { - @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); - byte[] response = isoDep.getHistoricalBytes(); - return CallResult.successWithHex(response); - } - // end of wrapper for IsoDep::getHistoricalBytes - - // start of wrapper for IsoDep::getMaxTransceiveLength - private final static String METHOD_NAME_ISO_DEP_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; - - private TagTechnologyClosure parseArgsIsoDepGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callGetMaxTransceiveLength; - } - - private CallResult callGetMaxTransceiveLength() { - @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); - int response = isoDep.getMaxTransceiveLength(); - return CallResult.successWithInt(response); - } - // end of wrapper for IsoDep::getMaxTransceiveLength - - - // start of wrapper for IsoDep::getTimeout - private final static String METHOD_NAME_ISO_DEP_GET_TIMEOUT = "getTimeout"; - - private TagTechnologyClosure parseArgsIsoDepGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callGetTimeout; - } - - private CallResult callGetTimeout() { - @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); - int response = isoDep.getTimeout(); - return CallResult.successWithInt(response); - } - // end of wrapper for IsoDep::getTimeout - - // start of wrapper for IsoDep::isExtendedLengthApduSupported - private final static String METHOD_NAME_ISO_DEP_IS_EXTENDED_LENGTH_APDU_SUPPORTED = "isExtendedLengthApduSupported"; - - private TagTechnologyClosure parseArgsIsoDepIsExtendedLengthApduSupported(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callIsExtendedLengthApduSupported; - } - - private CallResult callIsExtendedLengthApduSupported() { - @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); - boolean response = isoDep.isExtendedLengthApduSupported(); - return CallResult.successWithBool(response); - } - // end of wrapper for IsoDep::isExtendedLengthApduSupported - - // start of wrapper for IsoDep::setTimeout - private final static String METHOD_NAME_ISO_DEP_SET_TIMEOUT = "setTimeout"; - - private TagTechnologyClosure parseArgsIsoDepSetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int timeout = Utils.parseInt(args[0]); - return () -> callSetTimeout(timeout); - } - - private CallResult callSetTimeout(int timeout) { - @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); - isoDep.setTimeout(timeout); - return CallResult.success(); - } - // end of wrapper for IsoDep::setTimeout - - // start of wrapper for IsoDep::transceive - private final static String METHOD_NAME_ISO_DEP_TRANSCEIVE = "transceive"; - - private TagTechnologyClosure parseArgsIsoDepTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - - @NonNull byte[] requestData = Utils.parseHex(args[0]); - return () -> callIsoDepTransceive(requestData); - } - - private CallResult callIsoDepTransceive(@NonNull byte[] requestData) throws NfcException, IOException, IllegalStateException { - @NonNull IsoDep isoDep = Utils.liftTagTechnology(mTechnology, IsoDep.class); - @NonNull byte[] response = isoDep.transceive(requestData); - return CallResult.successWithHex(response); - } - // end of wrapper for IsoDep::transceive - // end of wrappers for class IsoDep - - // start of wrappers for class Ndef - // doc: https://developer.android.com/reference/android/nfc/tech/Ndef - private static final String CLASS_NAME_NDEF = "Ndef"; - - // start of wrapper for Ndef::connect - private final static String METHOD_NAME_NDEF_CONNECT = "connect"; - - private TagTechnologyClosure parseArgsNdefConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefConnect; - } - - private CallResult callNdefConnect() throws IOException { - return callTagTechnologyConnect(Ndef::get); - } - // end of wrapper for Ndef::connect - - // start of wrapper for Ndef::canMakeReadOnly - private final static String METHOD_NAME_NDEF_CAN_MAKE_READ_ONLY = "canMakeReadOnly"; - - private TagTechnologyClosure parseArgsNdefCanMakeReadOnly(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefCanMakeReadOnly; - } - - private CallResult callNdefCanMakeReadOnly() { - @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); - boolean response = ndef.canMakeReadOnly(); - return CallResult.successWithBool(response); - } - // end of wrapper for Ndef::canMakeReadOnly - - // start of wrapper for Ndef::getCachedNdefMessage - private final static String METHOD_NAME_NDEF_GET_CACHED_NDEF_MESSAGE = "getCachedNdefMessage"; - - private TagTechnologyClosure parseArgsNdefGetCachedNdefMessage(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefGetCachedNdefMessage; - } - - private CallResult callNdefGetCachedNdefMessage() { - @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); - NdefMessage msg = ndef.getCachedNdefMessage(); - return CallResult.successWithString(msg.toString()); - } - // end of wrapper for Ndef::getCachedNdefMessage - - // start of wrapper for Ndef::getMaxSize - private final static String METHOD_NAME_NDEF_GET_MAX_SIZE = "getMaxSize"; - - private TagTechnologyClosure parseArgsNdefGetMaxSize(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefGetMaxSize; - } - - private CallResult callNdefGetMaxSize() { - @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); - int response = ndef.getMaxSize(); - return CallResult.successWithInt(response); - } - // end of wrapper for Ndef::getMaxSize - - // start of wrapper for Ndef::getNdefMessage - private final static String METHOD_NAME_NDEF_GET_NDEF_MESSAGE = "getNdefMessage"; - - private TagTechnologyClosure parseArgsNdefGetNdefMessage(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefGetNdefMessage; - } - - private CallResult callNdefGetNdefMessage() throws IOException, android.nfc.FormatException { - @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); - NdefMessage msg = ndef.getNdefMessage(); - return CallResult.successWithString(msg.toString()); - } - // end of wrapper for Ndef::getNdefMessage - - // start of wrapper for Ndef::getType - private final static String METHOD_NAME_NDEF_GET_TYPE = "getType"; - - private TagTechnologyClosure parseArgsNdefGetType(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefGetType; - } - - private CallResult callNdefGetType() { - @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); - String response = ndef.getType(); - return CallResult.successWithString(response); - } - // end of wrapper for Ndef::getType - - // start of wrapper for Ndef::isWritable - private final static String METHOD_NAME_NDEF_IS_WRITABLE = "isWritable"; - - private TagTechnologyClosure parseArgsNdefIsWritable(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefIsWritable; - } - - private CallResult callNdefIsWritable() { - @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); - boolean response = ndef.isWritable(); - return CallResult.successWithBool(response); - } - // end of wrapper for Ndef::isWritable - - // start of wrapper for Ndef::makeReadOnly - private final static String METHOD_NAME_NDEF_MAKE_READ_ONLY = "makeReadOnly"; - - private TagTechnologyClosure parseArgsNdefMakeReadOnly(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefMakeReadOnly; - } - - private CallResult callNdefMakeReadOnly() throws IOException { - @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); - boolean result = ndef.makeReadOnly(); - return CallResult.successWithBool(result); - } - // end of wrapper for Ndef::makeReadOnly - - // start of wrapper for Ndef::writeNdefMessage - private final static String METHOD_NAME_NDEF_WRITE_NDEF_MESSAGE = "writeNdefMessage"; - - private TagTechnologyClosure parseArgsNdefWriteNdefMessage(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] msgHex = Utils.parseHex(args[0]); - return () -> callNdefWriteNdefMessage(msgHex); - } - - private CallResult callNdefWriteNdefMessage(@NonNull byte[] msgHex) throws FormatException, IOException { - @NonNull Ndef ndef = Utils.liftTagTechnology(mTechnology, Ndef.class); - ndef.writeNdefMessage(new NdefMessage(msgHex)); - return CallResult.success(); - } - // end of wrapper for Ndef::writeNdefMessage - // end of wrappers for class Ndef - - // start of wrappers for class MifareClassic - // doc: https://developer.android.com/reference/android/nfc/tech/MifareClassic - private final static String CLASS_NAME_MIFARE_CLASSIC = "MifareClassic"; - - // start of wrapper for MifareClassic::authenticateSectorWithKeyA - private final static String METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_A = "authenticateSectorWithKeyA"; - - private TagTechnologyClosure parseArgsMifareClassicAuthenticateSectorWithKeyA(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); - int sectorIndex = Utils.parseInt(args[0]); - @NonNull byte[] key = Utils.parseHex(args[1]); - return () -> callMifareClassicAuthenticateSectorWithKeyA(sectorIndex, key); - } - - private CallResult callMifareClassicAuthenticateSectorWithKeyA(int sectorIndex, @NonNull byte[] key) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - boolean result = mifareClassic.authenticateSectorWithKeyA(sectorIndex, key); - return CallResult.successWithBool(result); - } - // end of wrapper for MifareClassic::authenticateSectorWithKeyA - - // start of wrapper for MifareClassic::authenticateSectorWithKeyB - private final static String METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_B = "authenticateSectorWithKeyB"; - - private TagTechnologyClosure parseArgsMifareClassicAuthenticateSectorWithKeyB(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); - int sectorIndex = Utils.parseInt(args[0]); - @NonNull byte[] key = Utils.parseHex(args[1]); - return () -> callMifareClassicAuthenticateSectorWithKeyB(sectorIndex, key); - } - - private CallResult callMifareClassicAuthenticateSectorWithKeyB(int sectorIndex, @NonNull byte[] key) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - boolean result = mifareClassic.authenticateSectorWithKeyB(sectorIndex, key); - return CallResult.successWithBool(result); - } - // end of wrapper for MifareClassic::authenticateSectorWithKeyB - - // start of wrapper for MifareClassic::blockToSector - private final static String METHOD_NAME_MIFARE_CLASSIC_BLOCK_TO_SECTOR = "blockToSector"; - - private TagTechnologyClosure parseArgsMifareClassicBlockToSector(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int blockIndex = Utils.parseInt(args[0]); - return () -> callMifareClassicBlockToSector(blockIndex); - } - - private CallResult callMifareClassicBlockToSector(int blockIndex) { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.blockToSector(blockIndex); - return CallResult.successWithInt(response); - } - // end of wrapper for MifareClassic::blockToSector - - // start of wrapper for MifareClassic::connect (inherited from TagTechnology) - private final static String METHOD_NAME_MIFARE_CLASSIC_CONNECT = "connect"; + throw new ArgumentException(); + } - private TagTechnologyClosure parseArgsMifareClassicConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareClassicConnect; - } + static private Boolean parseBoolean(Object arg) throws ArgumentException { + return checkedCast(arg, Boolean.class); + } - private CallResult callMifareClassicConnect() throws IOException { - return callTagTechnologyConnect(MifareClassic::get); + static private Short parseShort(Object arg) throws ArgumentException { + int val = checkedCast(arg, Integer.class); + if (!(Short.MIN_VALUE <= val && val <= Short.MAX_VALUE)) { + throw new ArgumentException(); } - // end of wrapper for MifareClassic::connect - // start of wrapper for MifareClassic::decrement - private final static String METHOD_NAME_MIFARE_CLASSIC_DECREMENT = "decrement"; - - private TagTechnologyClosure parseArgsMifareClassicDecrement(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); - int blockIndex = Utils.parseInt(args[0]); - int value = Utils.parseInt(args[1]); - return () -> callMifareClassicDecrement(blockIndex, value); - } + return (short) val; + } - private CallResult callMifareClassicDecrement(int blockIndex, int value) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - mifareClassic.decrement(blockIndex, value); - return CallResult.success(); - } - // end of wrapper for MifareClassic::decrement + static private Integer parseInt(Object arg) throws ArgumentException { + return checkedCast(arg, Integer.class); + } - // start of wrapper for MifareClassic::getBlockCount - private final static String METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT = "getBlockCount"; + static private byte[] parseByteArray(Object arg) throws ArgumentException { + JSONObject jsonObject = checkedCast(arg, JSONObject.class); + String format; + String value; - private TagTechnologyClosure parseArgsMifareClassicGetBlockCount(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareClassicGetBlockCount; + try { + format = jsonObject.getString(JSONConstant.KEY_BYTES_FORMAT); + value = jsonObject.getString(JSONConstant.KEY_BYTES_VALUE); + } catch (JSONException e) { + throw new ArgumentException(e.getMessage()); } - private CallResult callMifareClassicGetBlockCount() { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.getBlockCount(); - return CallResult.successWithInt(response); + switch (format) { + case JSONConstant.VALUE_BYTES_FORMAT_HEX: + return Utils.parseHex(value); + case JSONConstant.VALUE_BYTES_FORMAT_RAW: + return value.getBytes(StandardCharsets.ISO_8859_1); + default: + throw new ArgumentException(); } - // end of wrapper for MifareClassic::getBlockCount - - // start of wrapper for MifareClassic::getBlockCountInSector - private final static String METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT_IN_SECTOR = "getBlockCountInSector"; + } - private TagTechnologyClosure parseArgsMifareClassicGetBlockCountInSector(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int sectorIndex = Utils.parseInt(args[0]); - return () -> callMifareClassicGetBlockCountInSector(sectorIndex); + static private NdefMessage parseNdefMessage(Object arg) throws ArgumentException { + byte[] bytes = parseByteArray(arg); + try { + return new NdefMessage(bytes); + } catch (FormatException e) { + throw new ArgumentException(e.getMessage()); } + } - private CallResult callMifareClassicGetBlockCountInSector(int sectorIndex) { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.getBlockCountInSector(sectorIndex); - return CallResult.successWithInt(response); + static t getArgParser(@NonNull Class argType) { + if (argType == boolean.class) { + return Parser::parseBoolean; } - // end of wrapper for MifareClassic::getBlockCountInSector - - // start of wrapper for MifareClassic::getMaxTransceiveLength - private final static String METHOD_NAME_MIFARE_CLASSIC_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; - - private TagTechnologyClosure parseArgsMifareClassicGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareClassicGetMaxTransceiveLength; + if (argType == short.class) { + return Parser::parseShort; } - - private CallResult callMifareClassicGetMaxTransceiveLength() { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.getMaxTransceiveLength(); - return CallResult.successWithInt(response); + if (argType == int.class) { + return Parser::parseInt; } - // end of wrapper for MifareClassic::getMaxTransceiveLength - - // start of wrapper for MifareClassic::getSectorCount - private final static String METHOD_NAME_MIFARE_CLASSIC_GET_SECTOR_COUNT = "getSectorCount"; - - private TagTechnologyClosure parseArgsMifareClassicGetSectorCount(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareClassicGetSectorCount; + if (argType == byte[].class) { + return Parser::parseByteArray; } - - private CallResult callMifareClassicGetSectorCount() { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.getSectorCount(); - return CallResult.successWithInt(response); + if (argType == NdefMessage.class) { + return Parser::parseNdefMessage; } - // end of wrapper for MifareClassic::getSectorCount - - // start of wrapper for MifareClassic::getSize - private final static String METHOD_NAME_MIFARE_CLASSIC_GET_SIZE = "getSize"; - private TagTechnologyClosure parseArgsMifareClassicGetSize(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareClassicGetSize; - } + assert false : "unexpected arg type: " + argType.getName(); + return null; + } +} - private CallResult callMifareClassicGetSize() { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.getSize(); - return CallResult.successWithInt(response); - } - // end of wrapper for MifareClassic::getSize - // start of wrapper for MifareClassic::getTimeout - private final static String METHOD_NAME_MIFARE_CLASSIC_GET_TIMEOUT = "getTimeout"; +class Formatter { + interface t { + @Nullable + Object format(Object ret); + } - private TagTechnologyClosure parseArgsMifareClassicGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareClassicGetTimeout; - } + static Integer formatInt(int ret) { + return ret; + } - private CallResult callMifareClassicGetTimeout() { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.getTimeout(); - return CallResult.successWithInt(response); - } - // end of wrapper for MifareClassic::getTimeout + static Integer formatByte(byte ret) { + return (int) ret; + } - // start of wrapper for MifareClassic::getType - private final static String METHOD_NAME_MIFARE_CLASSIC_GET_TYPE = "getType"; + static Integer formatShort(short ret) { + return (int) ret; + } - private TagTechnologyClosure parseArgsMifareClassicGetType(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareClassicGetType; - } + static Boolean formatBoolean(boolean ret) { + return ret; + } - private CallResult callMifareClassicGetType() { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.getType(); - return CallResult.successWithInt(response); + static JSONObject formatByteArray(@NonNull byte[] ret) { + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put(JSONConstant.KEY_BYTES_FORMAT, JSONConstant.VALUE_BYTES_FORMAT_HEX); + jsonObject.put(JSONConstant.KEY_BYTES_VALUE, Utils.formatHex(ret)); + } catch (JSONException e) { + assert false : e.getMessage(); } - // end of wrapper for MifareClassic::getType - - // start of wrapper for MifareClassic::increment - private final static String METHOD_NAME_MIFARE_CLASSIC_INCREMENT = "increment"; + return jsonObject; + } - private TagTechnologyClosure parseArgsMifareClassicIncrement(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); - int blockIndex = Utils.parseInt(args[0]); - int value = Utils.parseInt(args[1]); - return () -> callMifareClassicIncrement(blockIndex, value); + static JSONObject formatTag(@NonNull Tag ret) { + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put(JSONConstant.KEY_TAG_ID, formatByteArray(ret.getId())); + jsonObject.put(JSONConstant.KEY_TAG_TECH_LIST, new JSONArray(ret.getTechList())); + } catch (JSONException e) { + assert false : e.getMessage(); } + return jsonObject; + } - private CallResult callMifareClassicIncrement(int blockIndex, int value) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - mifareClassic.increment(blockIndex, value); - return CallResult.success(); + static String formatTnf(short tnf) { + switch (tnf) { + case NdefRecord.TNF_EMPTY: + return "TNF_EMPTY"; + case NdefRecord.TNF_WELL_KNOWN: + return "TNF_WELL_KNOWN"; + case NdefRecord.TNF_MIME_MEDIA: + return "TNF_MIME_MEDIA"; + case NdefRecord.TNF_ABSOLUTE_URI: + return "TNF_ABSOLUTE_URI"; + case NdefRecord.TNF_EXTERNAL_TYPE: + return "TNF_EXTERNAL_TYPE"; + case NdefRecord.TNF_UNKNOWN: + return "TNF_UNKNOWN"; + case NdefRecord.TNF_UNCHANGED: + return "TNF_UNCHANGED"; + default: + return "Unexpected TNF: " + tnf; } - // end of wrapper for MifareClassic::increment - - // start of wrapper for MifareClassic::readBlock - private final static String METHOD_NAME_MIFARE_CLASSIC_READ_BLOCK = "readBlock"; + } - private TagTechnologyClosure parseArgsMifareClassicReadBlock(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int blockIndex = Utils.parseInt(args[0]); - return () -> callMifareClassicReadBlock(blockIndex); + static JSONObject formatNdefRecord(@NonNull NdefRecord ret) { + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put(JSONConstant.KEY_NDEF_RECORD_ID, formatByteArray(ret.getId())); + jsonObject.put(JSONConstant.KEY_NDEF_RECORD_PAYLOAD, formatByteArray(ret.getPayload())); + jsonObject.put(JSONConstant.KEY_NDEF_RECORD_TNF, formatTnf(ret.getTnf())); + jsonObject.put(JSONConstant.KEY_NDEF_RECORD_TYPE, formatByteArray(ret.getType())); + } catch (JSONException e) { + assert false : e.getMessage(); } + return jsonObject; + } - private CallResult callMifareClassicReadBlock(int blockIndex) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - byte[] response = mifareClassic.readBlock(blockIndex); - return CallResult.successWithHex(response); + static JSONArray formatNdefMessage(@NonNull NdefMessage ret) { + JSONArray jsonArray = new JSONArray(); + for (NdefRecord record : ret.getRecords()) { + jsonArray.put(formatNdefRecord(record)); } - // end of wrapper for MifareClassic::readBlock - - // start of wrapper for MifareClassic::restore - private final static String METHOD_NAME_MIFARE_CLASSIC_RESTORE = "restore"; + return jsonArray; + } - private TagTechnologyClosure parseArgsMifareClassicRestore(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int blockIndex = Utils.parseInt(args[0]); - return () -> callMifareClassicRestore(blockIndex); + static String formatMifareClassicSize(int size) { + switch (size) { + case MifareClassic.SIZE_1K: + return "SIZE_1K"; + case MifareClassic.SIZE_2K: + return "SIZE_2K"; + case MifareClassic.SIZE_4K: + return "SIZE_4K"; + case MifareClassic.SIZE_MINI: + return "SIZE_MINI"; + default: + return "Unexpected size: " + size; } + } - private CallResult callMifareClassicRestore(int blockIndex) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - mifareClassic.restore(blockIndex); - return CallResult.success(); + static String formatMifareClassicType(int type) { + switch (type) { + case MifareClassic.TYPE_CLASSIC: + return "TYPE_CLASSIC"; + case MifareClassic.TYPE_PLUS: + return "TYPE_PLUS"; + case MifareClassic.TYPE_PRO: + return "TYPE_PRO"; + case MifareClassic.TYPE_UNKNOWN: + return "TYPE_UNKNOWN"; + default: + return "Unexpected type: " + type; } - // end of wrapper for MifareClassic::restore - - // start of wrapper for MifareClassic::sectorToBlock - private final static String METHOD_NAME_MIFARE_CLASSIC_SECTOR_TO_BLOCK = "sectorToBlock"; + } - private TagTechnologyClosure parseArgsMifareClassicSectorToBlock(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int sectorIndex = Utils.parseInt(args[0]); - return () -> callMifareClassicSectorToBlock(sectorIndex); + static String formatMifareUltralightType(int type) { + switch (type) { + case MifareUltralight.TYPE_ULTRALIGHT: + return "TYPE_ULTRALIGHT"; + case MifareUltralight.TYPE_ULTRALIGHT_C: + return "TYPE_ULTRAILGHT_C"; + case MifareUltralight.TYPE_UNKNOWN: + return "TYPE_UNKNOWN"; + default: + return "Unexpected type: " + type; } + } - private CallResult callMifareClassicSectorToBlock(int sectorIndex) { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - int response = mifareClassic.sectorToBlock(sectorIndex); - return CallResult.successWithInt(response); + static String formatNfcBarcodeType(int type) { + switch (type) { + case NfcBarcode.TYPE_KOVIO: + return "TYPE_KOVIO"; + case NfcBarcode.TYPE_UNKNOWN: + return "TYPE_UNKNOWN"; + default: + return "Unexpected type: " + type; } - // end of wrapper for MifareClassic::sectorToBlock - - // start of wrapper for MifareClassic::setTimeout - private final static String METHOD_NAME_MIFARE_CLASSIC_SET_TIMEOUT = "setTimeout"; + } - private TagTechnologyClosure parseArgsMifareClassicSetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int timeout = Utils.parseInt(args[0]); - return () -> callMifareClassicSetTimeout(timeout); - } + static @NonNull t getDefaultFormatter(Class retType) { + assert retType != void.class; - private CallResult callMifareClassicSetTimeout(int timeout) { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - mifareClassic.setTimeout(timeout); - return CallResult.success(); + if (retType == int.class) { + return ret -> formatInt((Integer) ret); } - // end of wrapper for MifareClassic::setTimeout - - // start of wrapper for MifareClassic::transceive - private final static String METHOD_NAME_MIFARE_CLASSIC_TRANSCEIVE = "transceive"; - - private TagTechnologyClosure parseArgsMifareClassicTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] data = Utils.parseHex(args[0]); - return () -> callMifareClassicTransceive(data); + if (retType == byte.class) { + return ret -> formatByte((Byte) ret); } - - private CallResult callMifareClassicTransceive(@NonNull byte[] data) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - byte[] response = mifareClassic.transceive(data); - return CallResult.successWithHex(response); + if (retType == short.class) { + return ret -> formatShort((Short) ret); } - // end of wrapper for MifareClassic::transceive - - - // start of wrapper for MifareClassic::transfer - private final static String METHOD_NAME_MIFARE_CLASSIC_TRANSFER = "transfer"; - - private TagTechnologyClosure parseArgsMifareClassicTransfer(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int blockIndex = Utils.parseInt(args[0]); - return () -> callMifareClassicTransfer(blockIndex); + if (retType == boolean.class) { + return ret -> formatBoolean((Boolean) ret); } - - private CallResult callMifareClassicTransfer(int blockIndex) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - mifareClassic.transfer(blockIndex); - return CallResult.success(); + if (retType == byte[].class) { + return ret -> formatByteArray((byte[]) ret); } - // end of wrapper for MifareClassic::transfer - - // start of wrapper for MifareClassic::writeBlock - private final static String METHOD_NAME_MIFARE_CLASSIC_WRITE_BLOCK = "writeBlock"; - - private TagTechnologyClosure parseArgsMifareClassicWriteBlock(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); - int blockIndex = Utils.parseInt(args[0]); - @NonNull byte[] data = Utils.parseHex(args[1]); - return () -> callMifareClassicWriteBlock(blockIndex, data); + if (retType == Tag.class) { + return ret -> formatTag((Tag) ret); } - - private CallResult callMifareClassicWriteBlock(int blockIndex, @NonNull byte[] data) throws IOException { - @NonNull MifareClassic mifareClassic = Utils.liftTagTechnology(mTechnology, MifareClassic.class); - mifareClassic.writeBlock(blockIndex, data); - return CallResult.success(); + if (retType == NdefMessage.class) { + return ret -> formatNdefMessage((NdefMessage) ret); } - // end of wrapper for MifareClassic::writeBlock - // end of wrappers for class MifareClassic - - // start of wrappers for class MifareUltralight - // doc: https://developer.android.com/reference/android/nfc/tech/MifareUltralight - - private final static String CLASS_NAME_MIFARE_ULTRALIGHT = "MifareUltralight"; - - // start of wrapper for MifareUltralight::connect (inherited from TagTechnology) - private final static String METHOD_NAME_MIFARE_ULTRALIGHT_CONNECT = "connect"; - - private TagTechnologyClosure parseArgsMifareUltralightConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareUltralightConnect; + if (retType == String.class) { + return ret -> ret; } - private CallResult callMifareUltralightConnect() throws IOException { - return callTagTechnologyConnect(MifareUltralight::get); - } - // end of wrapper for MifareUltralight::connect - - // start of wrapper for MifareUltralight::getMaxTransceiveLength - private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_MAX_TRANSCEIVE_LENGTH = "getMaxTransceiveLength"; - - private TagTechnologyClosure parseArgsMifareUltralightGetMaxTransceiveLength(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareUltralightGetMaxTransceiveLength; - } - - private CallResult callMifareUltralightGetMaxTransceiveLength() { - @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); - int response = mifareUltralight.getMaxTransceiveLength(); - return CallResult.successWithInt(response); - } - // end of wrapper for MifareUltralight::getMaxTransceiveLength - - // start of wrapper for MifareUltralight::getTimeout - private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_TIMEOUT = "getTimeout"; - - private TagTechnologyClosure parseArgsMifareUltralightGetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareUltralightGetTimeout; - } + assert false : "Unexpected return type: " + retType.getName(); + return null; + } +} - private CallResult callMifareUltralightGetTimeout() { - @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); - int response = mifareUltralight.getTimeout(); - return CallResult.successWithInt(response); - } - // end of wrapper for MifareUltralight::getTimeout +class NfcManager { + // The result of connect() + private @Nullable TagTechnology mTechnology; - // start of wrapper for MifareUltralight::getType - private final static String METHOD_NAME_MIFARE_ULTRALIGHT_GET_TYPE = "getType"; + // The last discovered tag + private Tag mTag; + private final Semaphore mTagSemaphore; - private TagTechnologyClosure parseArgsMifareUltralightGetType(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callMifareUltralightGetType; - } + private final Activity mActivity; - private CallResult callMifareUltralightGetType() { - @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); - int response = mifareUltralight.getType(); - return CallResult.successWithInt(response); - } - // end of wrapper for MifareUltralight::getType + private final Intent mIntent; - // start of wrapper for MifareUltralight::readPages - private final static String METHOD_NAME_MIFARE_ULTRALIGHT_READ_PAGES = "readPages"; + private final Map> mInvokerMap; + private final Map> mMethodMap; - private TagTechnologyClosure parseArgsMifareUltralightReadPages(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int pageOffset = Utils.parseInt(args[0]); - return () -> callMifareUltralightReadPages(pageOffset); - } + NfcManager(Activity activity, Intent intent) { + mTag = null; + mTechnology = null; + mActivity = activity; + mIntent = intent; + mTagSemaphore = new Semaphore(0); - private CallResult callMifareUltralightReadPages(int pageOffset) throws IOException { - @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); - byte[] response = mifareUltralight.readPages(pageOffset); - return CallResult.successWithHex(response); - } - // end of wrapper for MifareUltralight::readPages + mMethodMap = new HashMap<>(); + populateMethods(); - // start of wrapper for MifareUltralight::setTimeout - private final static String METHOD_NAME_MIFARE_ULTRALIGHT_SET_TIMEOUT = "setTimeout"; + mInvokerMap = new HashMap<>(); + populateInvokers(); + } - private TagTechnologyClosure parseArgsMifareUltralightSetTimeout(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - int timeout = Utils.parseInt(args[0]); - return () -> callMifareUltralightSetTimeout(timeout); + // helper function for subclasses' connect() + private void callTagTechnologyConnect(@NonNull Method techGetMethod, @NonNull Method techConnectMethod, @NonNull Object[] args) throws Throwable { + if (args.length != 0) { + throw new ArgNumberException(0, args.length); } - private CallResult callMifareUltralightSetTimeout(int timeout) { - @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); - mifareUltralight.setTimeout(timeout); - return CallResult.success(); + if (mTag == null) { + throw new TagNullException(); } - // end of wrapper for MifareUltralight::setTimeout - // start of wrapper for MifareUltralight::transceive - private final static String METHOD_NAME_MIFARE_ULTRALIGHT_TRANSCEIVE = "transceive"; - - private TagTechnologyClosure parseArgsMifareUltralightTransceive(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] data = Utils.parseHex(args[0]); - return () -> callMifareUltralightTransceive(data); + mTechnology = (TagTechnology) Utils.invokePublicMethod(techGetMethod, null, mTag); + if (mTechnology == null) { + throw new UnsupportedTechnology(); } - private CallResult callMifareUltralightTransceive(@NonNull byte[] data) throws IOException { - @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); - byte[] response = mifareUltralight.transceive(data); - return CallResult.successWithHex(response); - } - // end of wrapper for MifareUltralight::transceive + Utils.invokePublicMethod(techConnectMethod, mTechnology); + } - // start of wrapper for MifareUltralight::writePage - private final static String METHOD_NAME_MIFARE_ULTRALIGHT_WRITE_PAGE = "writePage"; + private void discoverTag() throws ArgumentException, InterruptedException { + clearTag(); - private TagTechnologyClosure parseArgsMifareUltralightWritePage(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(2, args.length); - int pageOffset = Utils.parseInt(args[0]); - @NonNull byte[] data = Utils.parseHex(args[1]); - return () -> callMifareUltralightWritePage(pageOffset, data); - } + Intent intent = new Intent(mActivity, NfcAPI.NfcActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + mActivity.startActivity(intent); - private CallResult callMifareUltralightWritePage(int pageOffset, @NonNull byte[] data) throws IOException { - @NonNull MifareUltralight mifareUltralight = Utils.liftTagTechnology(mTechnology, MifareUltralight.class); - mifareUltralight.writePage(pageOffset, data); - return CallResult.success(); - } - // end of wrapper for MifareUltralight::writePage - // end of wrappers for class MifareUltralight + mTagSemaphore.acquire(); + assert mTag != null; + mActivity.runOnUiThread(() -> mActivity.moveTaskToBack(true)); + } - // start of wrappers for class NfcBarcode - // doc: https://developer.android.com/reference/android/nfc/tech/NfcBarcode - private final static String CLASS_NAME_NFC_BARCODE = "NfcBarcode"; + private MaybeVoidResult invokeDiscoverTag() throws ArgumentException, InterruptedException { + discoverTag(); + assert mTag != null; + return MaybeVoidResult.nonVoid(Formatter.formatTag(mTag)); + } - // start of wrapper for NfcBarcode::connect (inherited from TagTechnology) - private final static String METHOD_NAME_NFC_BARCODE_CONNECT = "connect"; + MaybeVoidResult invokeByLine(@NonNull String line) throws Throwable { + JSONObject jsonObject; + String operationName; - private TagTechnologyClosure parseArgsNfcBarcodeConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcBarcodeConnect; + try { + jsonObject = new JSONObject(line); + operationName = jsonObject.getString(JSONConstant.KEY_OP_NAME); + } catch (JSONException e) { + throw new ArgumentException(e.toString()); } - private CallResult callNfcBarcodeConnect() throws IOException { - return callTagTechnologyConnect(NfcBarcode::get); + try { + switch (operationName) { + case JSONConstant.VALUE_OP_DISCOVER_TAG: + return invokeDiscoverTag(); + case JSONConstant.VALUE_OP_API: + return invokeJsonApi(jsonObject); + default: + throw new InvalidCommandException(); + } + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + throw cause==null ? e : cause; } - // end of wrapper for NfcBarcode::connect + } - // start of wrapper for NfcBarcode::getBarcode - private final static String METHOD_NAME_NFC_BARCODE_GET_BARCODE = "getBarcode"; + MaybeVoidResult invokeJsonApi(@NonNull JSONObject jsonObject) throws Throwable { + String className; + String methodName; + JSONArray jsonArray; - private TagTechnologyClosure parseArgsNfcBarcodeGetBarcode(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcBarcodeGetBarcode; + try { + className = jsonObject.getString(JSONConstant.KEY_CLASS_NAME); + methodName = jsonObject.getString(JSONConstant.KEY_METHOD_NAME); + jsonArray = jsonObject.getJSONArray(JSONConstant.KEY_ARGS); + } catch (JSONException e) { + throw new ArgumentException(e.getMessage()); } - private CallResult callNfcBarcodeGetBarcode() { - @NonNull NfcBarcode nfcBarcode = Utils.liftTagTechnology(mTechnology, NfcBarcode.class); - byte[] response = nfcBarcode.getBarcode(); - return CallResult.successWithHex(response); + Object[] args = new Object[jsonArray.length()]; + try { + for (int i = 0; i < args.length; ++i) { + args[i] = jsonArray.get(i); + } + } catch (JSONException e) { + assert false : e.getMessage(); } - // end of wrapper for NfcBarcode::getBarcode - // start of wrapper for NfcBarcode::getType - private final static String METHOD_NAME_NFC_BARCODE_GET_TYPE = "getType"; - - private TagTechnologyClosure parseArgsNfcBarcodeGetType(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNfcBarcodeGetType; - } + return getInvoker(className, methodName).invoke(args); + } - private CallResult callNfcBarcodeGetType() { - @NonNull NfcBarcode nfcBarcode = Utils.liftTagTechnology(mTechnology, NfcBarcode.class); - int response = nfcBarcode.getType(); - return CallResult.successWithInt(response); + ApiInvoker getInvoker(@NonNull String className, @NonNull String methodName) throws InvalidCommandException { + ApiInvoker invoker = Utils.get2dMap(mInvokerMap, className, methodName); + if (invoker == null) { + throw new InvalidCommandException(); } - // end of wrapper for NfcBarcode::getType - // end of wrappers for class NfcBarcode + return invoker; + } - // start of wrappers for class NdefFormatable - // doc: https://developer.android.com/reference/android/nfc/tech/NdefFormatable - private final static String CLASS_NAME_NDEF_FORMATABLE = "NdefFormatable"; + void listenAsync() { + ResultReturner.returnData(mActivity, mIntent, new ResultReturner.WithInput() { + @Override + public void writeResult(PrintWriter out) throws Exception { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + MaybeVoidResult result; + try { + result = invokeByLine(line); + } catch (Throwable e) { + Utils.printLnFlush(out, Utils.throwableToJson(e).toString()); + continue; + } - // start of wrapper for NdefFormatable::connect (inherited from TagTechnology) - private final static String METHOD_NAME_NDEF_FORMATABLE_CONNECT = "connect"; + Utils.printLnFlush(out, result.toJson().toString()); + } - private TagTechnologyClosure parseArgsNdefFormatableConnect(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(0, args.length); - return this::callNdefFormatableConnect; - } + mActivity.runOnUiThread(mActivity::finishAndRemoveTask); + } + }); + } - private CallResult callNdefFormatableConnect() throws IOException { - return callTagTechnologyConnect(NdefFormatable::get); - } - // end of wrapper for NdefFormatable::connect + synchronized void setTag(@NonNull Tag tag) { + mTag = tag; + mTagSemaphore.release(); + } - // start of wrapper for NdefFormatable::format - private final static String METHOD_NAME_NDEF_FORMATABLE_FORMAT = "format"; + private synchronized void clearTag() { + mTag = null; + mTagSemaphore.drainPermits(); + } - private TagTechnologyClosure parseArgsNdefFormatableFormat(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] ndefMessageHex = Utils.parseHex(args[0]); - return () -> callNdefFormatableFormat(ndefMessageHex); - } + MaybeVoidResult invokeWithArgs(@NonNull Class cls, @NonNull Method method, @Nullable Formatter.t retFormatter, @NonNull Parser.t[] argParsers, @NonNull Object[] args) throws Throwable { + Utils.checkTechnologyNonNull(mTechnology); - private CallResult callNdefFormatableFormat(@NonNull byte[] ndefMessageHex) throws IOException, FormatException { - @NonNull NdefFormatable ndefFormatable = Utils.liftTagTechnology(mTechnology, NdefFormatable.class); - ndefFormatable.format(new NdefMessage(ndefMessageHex)); - return CallResult.success(); + if (!cls.isInstance(mTechnology)) { + throw new WrongTechnologyException(cls.getSimpleName(), mTechnology.getClass().getSimpleName()); } - // end of wrapper for NdefFormatable::format - - // start of wrapper for NdefFormatable::formatReadOnly - private final static String METHOD_NAME_NDEF_FORMATABLE_FORMAT_READ_ONLY = "formatReadOnly"; - private TagTechnologyClosure parseArgsNdefFormatableFormatReadOnly(@NonNull String[] args) throws ArgumentException { - Utils.checkIntEqual(1, args.length); - @NonNull byte[] ndefMessageHex = Utils.parseHex(args[0]); - return () -> callNdefFormatableFormatReadOnly(ndefMessageHex); + int nArgs = args.length; + if (nArgs != argParsers.length) { + throw new ArgNumberException(argParsers.length, nArgs); } - private CallResult callNdefFormatableFormatReadOnly(@NonNull byte[] ndefMessageHex) throws IOException, FormatException { - @NonNull NdefFormatable ndefFormatable = Utils.liftTagTechnology(mTechnology, NdefFormatable.class); - ndefFormatable.formatReadOnly(new NdefMessage(ndefMessageHex)); - return CallResult.success(); + Object[] parsedArgs = new Object[nArgs]; + for (int i = 0; i < nArgs; ++i) { + parsedArgs[i] = argParsers[i].parse(args[i]); } - // end of wrapper for NdefFormatable::formatReadOnly - // end of wrappers for class NdefFormatable - // helper function for subclasses' connect() - private CallResult callTagTechnologyConnect(Function tagGetter) throws IOException { - if (mTag == null) { - throw new TagNullException(); - } - mTechnology = tagGetter.apply(mTag); - if (mTechnology == null) { - throw new UnsupportedTechnology(); + Object result; + try { + result = method.invoke(mTechnology, parsedArgs); + } catch (IllegalAccessException e) { + assert false : e.getMessage(); + return null; + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause != null) { + throw cause; + } else { + throw e; } - mTechnology.connect(); - return CallResult.success(); } - private CallResult discoverTag() throws InterruptedException { - clearTag(); - - Intent intent = new Intent(mActivity, NfcActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - mActivity.startActivity(intent); - - mTagSemaphore.acquire(); - assert mTag != null; - - mActivity.runOnUiThread(() -> mActivity.moveTaskToBack(true)); - return CallResult.success(); + if (retFormatter == null) { + return MaybeVoidResult.theVoid(); + } else { + return MaybeVoidResult.nonVoid(retFormatter.format(result)); } + } - TagTechnologyClosure parseClosureFromClassMethodArgs(@NonNull String className, @NonNull String methodName, @NonNull String[] args) { - switch (className) { - case CLASS_NAME_TAG_TECHNOLOGY: { - switch (methodName) { - case METHOD_NAME_TAG_TECHNOLOGY_CLOSE: - return parseArgsTagTechnologyClose(args); - case METHOD_NAME_TAG_TECHNOLOGY_IS_CONNECTED: - return parseArgsTagTechnologyIsConnected(args); - case METHOD_NAME_TAG_TECHNOLOGY_GET_TAG: - return parseArgsTagTechnologyGetTag(args); - default: - throw new InvalidCommandException(); - } - } - - case CLASS_NAME_NFC_A: { - switch (methodName) { - case METHOD_NAME_NFC_A_CONNECT: - return parseArgsNfcAConnect(args); - case METHOD_NAME_NFC_A_GET_ATQA: - return parseArgsNfcAGetAtqa(args); - case METHOD_NAME_NFC_A_GET_MAX_TRANSCEIVE_LENGTH: - return parseArgsNfcAGetMaxTransceiveLength(args); - case METHOD_NAME_NFC_A_GET_SAK: - return parseArgsNfcAGetSak(args); - case METHOD_NAME_NFC_A_GET_TIMEOUT: - return parseArgsNfcAGetTimeout(args); - case METHOD_NAME_NFC_A_SET_TIMEOUT: - return parseArgsNfcASetTimeout(args); - case METHOD_NAME_NFC_A_TRANSCEIVE: - return parseArgsNfcATransceive(args); - default: - throw new InvalidCommandException(); - } - } - - case CLASS_NAME_NFC_B: { - switch (methodName) { - case METHOD_NAME_NFC_B_CONNECT: - return parseArgsNfcBConnect(args); - case METHOD_NAME_NFC_B_GET_APPLICATION_DATA: - return parseArgsNfcBGetApplicationData(args); - case METHOD_NAME_NFC_B_GET_MAX_TRANSCEIVE_LENGTH: - return parseArgsNfcBGetMaxTransceiveLength(args); - case METHOD_NAME_NFC_B_GET_PROTOCOL_INFO: - return parseArgsNfcBGetProtocolInfo(args); - case METHOD_NAME_NFC_B_TRANSCEIVE: - return parseArgsNfcBTransceive(args); - default: - throw new InvalidCommandException(); - } - } - - case CLASS_NAME_NFC_F: { - switch (methodName) { - case METHOD_NAME_NFC_F_CONNECT: - return parseArgsNfcFConnect(args); - case METHOD_NAME_NFC_F_GET_MANUFACTURER: - return parseArgsNfcFGetManufacturer(args); - case METHOD_NAME_NFC_F_GET_MAX_TRANSCEIVE_LENGTH: - return parseArgsNfcFGetMaxTransceiveLength(args); - case METHOD_NAME_NFC_F_GET_SYSTEM_CODE: - return parseArgsNfcFGetSystemCode(args); - case METHOD_NAME_NFC_F_GET_TIMEOUT: - return parseArgsNfcFGetTimeout(args); - case METHOD_NAME_NFC_F_SET_TIMEOUT: - return parseArgsNfcFSetTimeout(args); - case METHOD_NAME_NFC_F_TRANSCEIVE: - return parseArgsNfcFTransceive(args); - default: - throw new InvalidCommandException(); - } - } - - case CLASS_NAME_NFC_V: { - switch (methodName) { - case METHOD_NAME_NFC_V_CONNECT: - return parseArgsNfcVConnect(args); - case METHOD_NAME_NFC_V_GET_DSF_ID: - return parseArgsNfcVGetDsfId(args); - case METHOD_NAME_NFC_V_GET_MAX_TRANSCEIVE_LENGTH: - return parseArgsNfcVGetMaxTransceiveLength(args); - case METHOD_NAME_NFC_V_GET_RESPONSE_FLAGS: - return parseArgsNfcVGetResponseFlags(args); - case METHOD_NAME_NFC_V_TRANSCEIVE: - return parseArgsNfcVTransceive(args); - default: - throw new InvalidCommandException(); - } - } + ApiInvoker newDefaultInvoker(@NonNull Class cls, @NonNull Method method, @Nullable Formatter.t retFormatter, @NonNull Parser.t... argParsers) { + assert method.getParameterCount() == argParsers.length; + assert TagTechnology.class.isAssignableFrom(method.getDeclaringClass()); - case CLASS_NAME_ISO_DEP: { - switch (methodName) { - case METHOD_NAME_ISO_DEP_CONNECT: - return parseArgsIsoDepConnect(args); - case METHOD_NAME_ISO_DEP_GET_HI_LAYER_RESPONSE: - return parseArgsIsoDepGetHiLayerResponse(args); - case METHOD_NAME_ISO_DEP_GET_HISTORICAL_BYTES: - return parseArgsIsoDepGetHistoricalBytes(args); - case METHOD_NAME_ISO_DEP_GET_MAX_TRANSCEIVE_LENGTH: - return parseArgsIsoDepGetMaxTransceiveLength(args); - case METHOD_NAME_ISO_DEP_GET_TIMEOUT: - return parseArgsIsoDepGetTimeout(args); - case METHOD_NAME_ISO_DEP_IS_EXTENDED_LENGTH_APDU_SUPPORTED: - return parseArgsIsoDepIsExtendedLengthApduSupported(args); - case METHOD_NAME_ISO_DEP_SET_TIMEOUT: - return parseArgsIsoDepSetTimeout(args); - case METHOD_NAME_ISO_DEP_TRANSCEIVE: - return parseArgsIsoDepTransceive(args); - default: - throw new InvalidCommandException(); - } - } + return args -> invokeWithArgs(cls, method, retFormatter, argParsers, args); + } - case CLASS_NAME_NDEF: { - switch (methodName) { - case METHOD_NAME_NDEF_CAN_MAKE_READ_ONLY: - return parseArgsNdefCanMakeReadOnly(args); - case METHOD_NAME_NDEF_CONNECT: - return parseArgsNdefConnect(args); - case METHOD_NAME_NDEF_GET_CACHED_NDEF_MESSAGE: - return parseArgsNdefGetCachedNdefMessage(args); - case METHOD_NAME_NDEF_GET_MAX_SIZE: - return parseArgsNdefGetMaxSize(args); - case METHOD_NAME_NDEF_GET_NDEF_MESSAGE: - return parseArgsNdefGetNdefMessage(args); - case METHOD_NAME_NDEF_GET_TYPE: - return parseArgsNdefGetType(args); - case METHOD_NAME_NDEF_IS_WRITABLE: - return parseArgsNdefIsWritable(args); - case METHOD_NAME_NDEF_MAKE_READ_ONLY: - return parseArgsNdefMakeReadOnly(args); - case METHOD_NAME_NDEF_WRITE_NDEF_MESSAGE: - return parseArgsNdefWriteNdefMessage(args); - default: - throw new InvalidCommandException(); - } - } + static Parser.t[] getArgParsers(Class[] parameterTypes) { + Parser.t[] argParsers = new Parser.t[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; ++i) { + argParsers[i] = Parser.getArgParser(parameterTypes[i]); + } + return argParsers; + } - case CLASS_NAME_MIFARE_CLASSIC: { - switch (methodName) { - case METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_A: - return parseArgsMifareClassicAuthenticateSectorWithKeyA(args); - case METHOD_NAME_MIFARE_CLASSIC_AUTHENTICATE_SECTOR_WITH_KEY_B: - return parseArgsMifareClassicAuthenticateSectorWithKeyB(args); - case METHOD_NAME_MIFARE_CLASSIC_BLOCK_TO_SECTOR: - return parseArgsMifareClassicBlockToSector(args); - case METHOD_NAME_MIFARE_CLASSIC_CONNECT: - return parseArgsMifareClassicConnect(args); - case METHOD_NAME_MIFARE_CLASSIC_DECREMENT: - return parseArgsMifareClassicDecrement(args); - case METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT: - return parseArgsMifareClassicGetBlockCount(args); - case METHOD_NAME_MIFARE_CLASSIC_GET_BLOCK_COUNT_IN_SECTOR: - return parseArgsMifareClassicGetBlockCountInSector(args); - case METHOD_NAME_MIFARE_CLASSIC_GET_MAX_TRANSCEIVE_LENGTH: - return parseArgsMifareClassicGetMaxTransceiveLength(args); - case METHOD_NAME_MIFARE_CLASSIC_GET_SECTOR_COUNT: - return parseArgsMifareClassicGetSectorCount(args); - case METHOD_NAME_MIFARE_CLASSIC_GET_SIZE: - return parseArgsMifareClassicGetSize(args); - case METHOD_NAME_MIFARE_CLASSIC_GET_TIMEOUT: - return parseArgsMifareClassicGetTimeout(args); - case METHOD_NAME_MIFARE_CLASSIC_GET_TYPE: - return parseArgsMifareClassicGetType(args); - case METHOD_NAME_MIFARE_CLASSIC_INCREMENT: - return parseArgsMifareClassicIncrement(args); - case METHOD_NAME_MIFARE_CLASSIC_READ_BLOCK: - return parseArgsMifareClassicReadBlock(args); - case METHOD_NAME_MIFARE_CLASSIC_RESTORE: - return parseArgsMifareClassicRestore(args); - case METHOD_NAME_MIFARE_CLASSIC_SECTOR_TO_BLOCK: - return parseArgsMifareClassicSectorToBlock(args); - case METHOD_NAME_MIFARE_CLASSIC_SET_TIMEOUT: - return parseArgsMifareClassicSetTimeout(args); - case METHOD_NAME_MIFARE_CLASSIC_TRANSCEIVE: - return parseArgsMifareClassicTransceive(args); - case METHOD_NAME_MIFARE_CLASSIC_TRANSFER: - return parseArgsMifareClassicTransfer(args); - case METHOD_NAME_MIFARE_CLASSIC_WRITE_BLOCK: - return parseArgsMifareClassicWriteBlock(args); - default: - throw new InvalidCommandException(); - } - } + @NonNull Method getMethod(Class cls, @NonNull String methodName) { + Method method = Utils.get2dMap(mMethodMap, cls.getSimpleName(), methodName); + assert method != null : String.format("Method %s not found in %s", methodName, cls.getName()); + return method; + } - case CLASS_NAME_MIFARE_ULTRALIGHT: { - switch (methodName) { - case METHOD_NAME_MIFARE_ULTRALIGHT_CONNECT: - return parseArgsMifareUltralightConnect(args); - case METHOD_NAME_MIFARE_ULTRALIGHT_GET_MAX_TRANSCEIVE_LENGTH: - return parseArgsMifareUltralightGetMaxTransceiveLength(args); - case METHOD_NAME_MIFARE_ULTRALIGHT_GET_TIMEOUT: - return parseArgsMifareUltralightGetTimeout(args); - case METHOD_NAME_MIFARE_ULTRALIGHT_GET_TYPE: - return parseArgsMifareUltralightGetType(args); - case METHOD_NAME_MIFARE_ULTRALIGHT_READ_PAGES: - return parseArgsMifareUltralightReadPages(args); - case METHOD_NAME_MIFARE_ULTRALIGHT_SET_TIMEOUT: - return parseArgsMifareUltralightSetTimeout(args); - case METHOD_NAME_MIFARE_ULTRALIGHT_TRANSCEIVE: - return parseArgsMifareUltralightTransceive(args); - case METHOD_NAME_MIFARE_ULTRALIGHT_WRITE_PAGE: - return parseArgsMifareUltralightWritePage(args); - default: - throw new InvalidCommandException(); - } - } + void addInvokerByName(Class cls, @NonNull String methodName) { + Method method = getMethod(cls, methodName); + Class retType = method.getReturnType(); + Formatter.t retFormatter = retType==void.class ? null : Formatter.getDefaultFormatter(retType); + Parser.t[] argParsers = getArgParsers(method.getParameterTypes()); + addInvokerBySig(cls, method, retFormatter, argParsers); + } - case CLASS_NAME_NFC_BARCODE: { - switch (methodName) { - case METHOD_NAME_NFC_BARCODE_CONNECT: - return parseArgsNfcBarcodeConnect(args); - case METHOD_NAME_NFC_BARCODE_GET_BARCODE: - return parseArgsNfcBarcodeGetBarcode(args); - case METHOD_NAME_NFC_BARCODE_GET_TYPE: - return parseArgsNfcBarcodeGetType(args); - default: - throw new InvalidCommandException(); - } - } + void addInvokerBySig(@NonNull Class cls, @NonNull String methodName, @Nullable Formatter.t retFormatter, @NonNull Parser.t... argParsers) { + addInvokerBySig(cls, getMethod(cls, methodName), retFormatter, argParsers); + } - case CLASS_NAME_NDEF_FORMATABLE: { - switch (methodName) { - case METHOD_NAME_NDEF_FORMATABLE_CONNECT: - return parseArgsNdefFormatableConnect(args); - case METHOD_NAME_NDEF_FORMATABLE_FORMAT: - return parseArgsNdefFormatableFormat(args); - case METHOD_NAME_NDEF_FORMATABLE_FORMAT_READ_ONLY: - return parseArgsNdefFormatableFormatReadOnly(args); - default: - throw new InvalidCommandException(); - } - } + void addInvokerBySig(@NonNull Class cls, @NonNull Method method, @Nullable Formatter.t retFormatter, @NonNull Parser.t... argParsers) { + assert (method.getReturnType() == void.class) == (retFormatter == null); + addInvoker(cls, method.getName(), newDefaultInvoker(cls, method, retFormatter, argParsers)); + } - default: - throw new InvalidCommandException(); - } - } + void addInvoker(@NonNull Class cls, @NonNull String methodName, @NonNull ApiInvoker invoker) { + Utils.getOrCreate2dMap(mInvokerMap, cls.getSimpleName()).put(methodName, invoker); + } - private final String JSON_KEY_OP_NAME = "op"; - private final String JSON_VALUE_OP_API = "api"; - private final String JSON_VALUE_OP_DISCOVER_TAG = "discoverTag"; - private final String JSON_KEY_CLASS_NAME = "class"; - private final String JSON_KEY_METHOD_NAME = "method"; - private final String JSON_KEY_ARGS = "args"; - - TagTechnologyClosure parseClosureFromLine(@NonNull String line) throws ArgumentException { - JSONObject jsonObject; - String operationName; - - try { - jsonObject = new JSONObject(line); - operationName = jsonObject.getString(JSON_KEY_OP_NAME); - } catch (JSONException e) { - throw new ArgumentException(e.getMessage()); - } + static final String METHOD_NAME_GET = "get"; + static final String METHOD_NAME_CONNECT = "connect"; - switch (operationName) { - case JSON_VALUE_OP_DISCOVER_TAG: - return this::discoverTag; - case JSON_VALUE_OP_API: - return parseClosureFromJsonApi(jsonObject); - default: - throw new InvalidCommandException(); - } - } + void addConnectInvoker(@NonNull Class cls) { + Method techGetMethod = getMethod(cls, METHOD_NAME_GET); + Method techConnectMethod = getMethod(cls, METHOD_NAME_CONNECT); - TagTechnologyClosure parseClosureFromJsonApi(@NonNull JSONObject jsonObject) throws ArgumentException { - String className; - String methodName; - String[] args; - - try { - className = jsonObject.getString(JSON_KEY_CLASS_NAME); - methodName = jsonObject.getString(JSON_KEY_METHOD_NAME); - JSONArray jsonArray = jsonObject.getJSONArray(JSON_KEY_ARGS); - args = Utils.jsonArrayToStringArray(jsonArray); - } catch (JSONException e) { - throw new ArgumentException(e.getMessage()); - } + addInvoker(cls, METHOD_NAME_CONNECT, args -> { + callTagTechnologyConnect(techGetMethod, techConnectMethod, args); + return MaybeVoidResult.theVoid(); + }); + } - return parseClosureFromClassMethodArgs(className, methodName, args); + void populateClassMethods(Class cls) { + Map map = Utils.getOrCreate2dMap(mMethodMap, cls.getSimpleName()); + for (Method method : cls.getDeclaredMethods()) { + map.put(method.getName(), method); } + } - NfcManager(Activity activity, Intent intent) { - mActivity = activity; - mIntent = intent; - mTagSemaphore = new Semaphore(0); - } + void populateMethods() { + populateClassMethods(TagTechnology.class); + populateClassMethods(NfcA.class); + populateClassMethods(NfcB.class); + populateClassMethods(NfcF.class); + populateClassMethods(NfcV.class); + populateClassMethods(IsoDep.class); + populateClassMethods(Ndef.class); + populateClassMethods(MifareClassic.class); + populateClassMethods(MifareUltralight.class); + populateClassMethods(NfcBarcode.class); + populateClassMethods(NdefFormatable.class); + } - void listenAsync() { - ResultReturner.returnData(mActivity, mIntent, new ResultReturner.WithInput() { - @Override - public void writeResult(PrintWriter out) throws Exception { - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String line; - while ((line = reader.readLine()) != null) { - TagTechnologyClosure closure; - try { - closure = parseClosureFromLine(line); - } catch (ArgumentException e) { - Utils.printLnFlush(out, Utils.exceptionToJson(e).toString()); - continue; - } - - CallResult result; - try { - result = closure.call(); - } catch (Exception e) { - Utils.printLnFlush(out, Utils.exceptionToJson(e).toString()); - continue; - } - - Utils.printLnFlush(out, result.toJson().toString()); - } + void populateInvokers() { + addInvokerByName(TagTechnology.class, "isConnected"); + addInvokerByName(TagTechnology.class, "close"); + addInvokerByName(TagTechnology.class, "getTag"); + + addConnectInvoker(NfcA.class); + addInvokerByName(NfcA.class, "getAtqa"); + addInvokerByName(NfcA.class, "getMaxTransceiveLength"); + addInvokerByName(NfcA.class, "getSak"); + addInvokerByName(NfcA.class, "getTimeout"); + addInvokerByName(NfcA.class, "setTimeout"); + addInvokerByName(NfcA.class, "transceive"); + + addConnectInvoker(NfcB.class); + addInvokerByName(NfcB.class, "getApplicationData"); + addInvokerByName(NfcB.class, "getMaxTransceiveLength"); + addInvokerByName(NfcB.class, "getProtocolInfo"); + addInvokerByName(NfcB.class, "transceive"); + + addConnectInvoker(NfcF.class); + addInvokerByName(NfcF.class, "getManufacturer"); + addInvokerByName(NfcF.class, "getMaxTransceiveLength"); + addInvokerByName(NfcF.class, "getSystemCode"); + addInvokerByName(NfcF.class, "getTimeout"); + addInvokerByName(NfcF.class, "setTimeout"); + addInvokerByName(NfcF.class, "transceive"); + + addConnectInvoker(NfcV.class); + addInvokerByName(NfcV.class, "getDsfId"); + addInvokerByName(NfcV.class, "getMaxTransceiveLength"); + addInvokerByName(NfcV.class, "getResponseFlags"); + addInvokerByName(NfcV.class, "transceive"); + + addConnectInvoker(IsoDep.class); + addInvokerByName(IsoDep.class, "getHiLayerResponse"); + addInvokerByName(IsoDep.class, "getHistoricalBytes"); + addInvokerByName(IsoDep.class, "getMaxTransceiveLength"); + addInvokerByName(IsoDep.class, "getTimeout"); + addInvokerByName(IsoDep.class, "isExtendedLengthApduSupported"); + addInvokerByName(IsoDep.class, "setTimeout"); + addInvokerByName(IsoDep.class, "transceive"); + + addConnectInvoker(Ndef.class); + addInvokerByName(Ndef.class, "canMakeReadOnly"); + addInvokerByName(Ndef.class, "getCachedNdefMessage"); + addInvokerByName(Ndef.class, "getMaxSize"); + addInvokerByName(Ndef.class, "getNdefMessage"); + addInvokerByName(Ndef.class, "getType"); + addInvokerByName(Ndef.class, "isWritable"); + addInvokerByName(Ndef.class, "makeReadOnly"); + addInvokerByName(Ndef.class, "writeNdefMessage"); + + addConnectInvoker(MifareClassic.class); + addInvokerByName(MifareClassic.class, "authenticateSectorWithKeyA"); + addInvokerByName(MifareClassic.class, "authenticateSectorWithKeyB"); + addInvokerByName(MifareClassic.class, "blockToSector"); + addInvokerByName(MifareClassic.class, "decrement"); + addInvokerByName(MifareClassic.class, "getBlockCount"); + addInvokerByName(MifareClassic.class, "getBlockCountInSector"); + addInvokerByName(MifareClassic.class, "getMaxTransceiveLength"); + addInvokerByName(MifareClassic.class, "getSectorCount"); + addInvokerBySig(MifareClassic.class, "getSize", ret -> Formatter.formatMifareClassicSize((Integer) ret)); + addInvokerByName(MifareClassic.class, "getTimeout"); + addInvokerBySig(MifareClassic.class, "getType", ret -> Formatter.formatMifareClassicType((Integer) ret)); + addInvokerByName(MifareClassic.class, "increment"); + addInvokerByName(MifareClassic.class, "readBlock"); + addInvokerByName(MifareClassic.class, "restore"); + addInvokerByName(MifareClassic.class, "sectorToBlock"); + addInvokerByName(MifareClassic.class, "setTimeout"); + addInvokerByName(MifareClassic.class, "transceive"); + addInvokerByName(MifareClassic.class, "transfer"); + addInvokerByName(MifareClassic.class, "writeBlock"); + + addConnectInvoker(MifareUltralight.class); + addInvokerByName(MifareUltralight.class, "getMaxTransceiveLength"); + addInvokerByName(MifareUltralight.class, "getTimeout"); + addInvokerBySig(MifareUltralight.class, "getType", ret -> Formatter.formatMifareUltralightType((Integer) ret)); + addInvokerByName(MifareUltralight.class, "readPages"); + addInvokerByName(MifareUltralight.class, "setTimeout"); + addInvokerByName(MifareUltralight.class, "transceive"); + addInvokerByName(MifareUltralight.class, "writePage"); + + addConnectInvoker(NfcBarcode.class); + addInvokerByName(NfcBarcode.class, "getBarcode"); + addInvokerBySig(NfcBarcode.class, "getType", ret -> Formatter.formatNfcBarcodeType((Integer) ret)); + + addConnectInvoker(NdefFormatable.class); + addInvokerByName(NdefFormatable.class, "format"); + addInvokerByName(NdefFormatable.class, "formatReadOnly"); + } +} - mActivity.runOnUiThread(mActivity::finishAndRemoveTask); - } - }); - } +public class NfcAPI { + private static final String LOG_TAG = "NfcAPI"; - private synchronized void setTag(@NonNull Tag tag) { - mTag = tag; - mTagSemaphore.release(); - } + public static void onReceive(final Context context, final Intent intent) { + Logger.logDebug(LOG_TAG, "onReceive"); - private synchronized void clearTag() { - mTag = null; - mTagSemaphore.drainPermits(); + Intent newIntent = new Intent(context, NfcActivity.class); + Bundle extras = intent.getExtras(); + if (extras == null) { + return; } + newIntent.putExtras(extras).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(newIntent); } public static class NfcActivity extends AppCompatActivity { @@ -1963,7 +983,6 @@ protected void onNewIntent(Intent intent) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); assert tag != null; mNfcManager.setTag(tag); - return; } }