I am trying to access an ISO-14443-4 NFC tag to read a rather substantial amount of data from the NFC tag and I am getting ANR frequently.
My Activity Code:
public class CardPairingActivity extends AppCompatActivity implements InfoDialogCopyDialog.InfoDialogCopyDialogListener, AlertCallbackDialog.AlertDialogListener {
private static String username = "";
private static String password = "";
private static final byte role = Constants.USER_KEYMAN;
private NfcAdapter nfcAdapter = null;
private IntentFilter[] intentFiltersArray = null;
private String[][] techListsArray = null;
private PendingIntent pendingIntent = null;
private APDUResult apduRes = null;
private static Handler handler = new Handler();
private static Runnable runnable = null;
private static Context myContext = null;
private static IsoDep iso14443 = null;
private static Intent _intent = null;
public static int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;
private static PairingCallBack callBack = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_card_pairing);
myContext = this;
callBack = new PairingCallBack();
callBack.setContext(myContext);
NfcManager nfcManager = (NfcManager) getSystemService(Context.NFC_SERVICE);
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
nfcAdapter.enableReaderMode(this, callBack, READER_FLAGS, null);
if (nfcManager != null) {
System.out.println("NFC Manager ready ...");
}
if (nfcAdapter != null) {
System.out.println("NFC Adapter ready ...");
}
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE);
intentFiltersArray = new IntentFilter[]{new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)};
techListsArray = new String[][]{new String[]{NfcA.class.getName()}, new String[]{NfcB.class.getName()}, new String[]{IsoDep.class.getName()}};
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
}
@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE);
if (nfcAdapter != null) {
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
nfcAdapter.enableReaderMode(this, callBack, READER_FLAGS, null);
}
}
protected void onPause() {
super.onPause();
if (nfcAdapter != null) {
try {
nfcAdapter.disableForegroundDispatch(this);
nfcAdapter.disableReaderMode(this);
} catch (IllegalStateException ex) {
Log.e("ATHTAG", "Error disabling NFC foreground dispatch", ex);
}
}
}
@Override
public void infoDialogCopyDialog() {
}
@RequiresApi(api = Build.VERSION_CODES.O)
public static void continueExec() throws InvalidAlgorithmParameterException, InvalidSizeException, NoSuchPaddingException, IllegalBlockSizeException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, ShortBufferException, InvalidParameterSpecException, InvalidKeySpecException, NoSuchProviderException, IllegalFormatException {
handler.post(new Runnable() {
@Override
public void run() {
System.out.println("Authenticating to card ...");
System.out.println("Card User Role: " + (int) role);
System.out.println("Card Username: " + username);
System.out.println("Card Secret: " + password);
try {
if (API.cardAuthenticate(Constants.USER_KEYMAN, username, password.toCharArray())) {
// Login success
// DO VERY LONG CARD READING
API.readAndStoreNFCUserProfileData();
} else {
// Login fail
System.out.println("[ERR] Card Login FAILED ...");
int tries = API.adminCardLoginTriesRemaining(role, null);
if (tries != -1) {
SharedResource.showNotificationDialog("Card Pairing Failed", "Card Login Failed. \r\nLogin Retries Left: " + tries, "OK", myContext, MainActivity.class);
} else {
SharedResource.showNotificationDialog("Card Pairing Failed", "Card Login Failed.", "OK", myContext, MainActivity.class);
}
}
// SharedResource.getIsoDep().close();
// SharedResource.setIsoDep(null);
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch (InvalidSizeException e) {
throw new RuntimeException(e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException(e);
} catch (ShortBufferException e) {
throw new RuntimeException(e);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidParameterSpecException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException(e);
} catch (BadPaddingException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
} catch (IllegalFormatException e) {
throw new RuntimeException(e);
}
}
});
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void alertCallbackDialog() {
System.out.println("AlertCallbackDialog runs ...");
}
}
NFC Callback
public class PairingCallBack implements NfcAdapter.ReaderCallback {
Context myContext = null;
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onTagDiscovered(Tag tag) {
new Thread(new Runnable() {
@Override
public void run() {
boolean hasIsoDep = false;
if (tag != null) {
System.out.println("New tag found !!!");
System.out.println("Tag ID: " + BinUtils.toHexString(tag.getId()));
System.out.println("\r\n");
for (String tagTech : tag.getTechList()) {
System.out.println("Tech: " + tagTech);
if (tagTech.equals("android.nfc.tech.IsoDep")) {
hasIsoDep = true;
}
}
} else {
System.out.println("No new tag found !!!");
}
if (hasIsoDep) {
try {
IsoDep iso14443 = IsoDep.get(tag);
iso14443.connect();
iso14443.setTimeout(120000);
SharedResource.setIsoDep(iso14443);
System.out.println("Selecting KeyVault applet ...");
boolean isSuccess = API.adminInit();
boolean isContinue = true;
System.out.println("-- PairingCallBack :: IsSuccess: " + isSuccess);
System.out.println("-- PairingCallBack :: IsContinue: " + isContinue);
if (isSuccess) {
if (isContinue) {
if ((byte) (SharedResource.getHwInfo().getInteractiveCapabilities() & Constants.INTERACT_SCREEN) == Constants.INTERACT_SCREEN) {
System.out.println("[INF] Found interactive card ...");
String toShow = "Please verify OTP Code:\n" + SharedResource.getSessionData().getOtpCode();
String title = "Secure Card Session";
AlertCallbackDialog dialog = new AlertCallbackDialog(title, toShow, myContext);
dialog.show(((AppCompatActivity) myContext).getSupportFragmentManager(), "aldialog");
} else {
System.out.println("[INF] Not interactive card ...");
CardPairingActivity.continueExec();
}
}
} else {
System.out.println("[ERR] Failed to select applet !!!");
}
} catch (IOException | InvalidSizeException | InvalidAlgorithmParameterException |
NoSuchPaddingException | ShortBufferException | IllegalBlockSizeException |
NoSuchAlgorithmException | InvalidParameterSpecException |
InvalidKeySpecException | BadPaddingException | InvalidKeyException |
NoSuchProviderException ex) {
Log.e("ATHTAG", "Error found during tag comms", ex);
} catch (IllegalFormatException e) {
throw new RuntimeException(e);
}
}
}
}).start();
}
public void setContext(Context context) {
myContext = context;
}
}
Alert dialog box to display an OTP secure channel code before downloading user profile from card
public class AlertCallbackDialog extends AppCompatDialogFragment {
private AlertCallbackDialog.AlertDialogListener listener;
private String title = "";
private String message = "";
private View view = null;
public AlertCallbackDialog(String title, String message, Context context) {
this.title = title;
this.message = message;
}
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
System.out.println("[INF] Creating AlertCallbackDialog ...");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(title);
builder.setMessage(message);
return builder.create();
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
super.onDismiss(dialog);
System.out.println("[INF] Dismissed AlertCallbackDialog ...");
try {
CardPairingActivity.continueExec();
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch (InvalidSizeException e) {
throw new RuntimeException(e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException(e);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (BadPaddingException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (ShortBufferException e) {
throw new RuntimeException(e);
} catch (InvalidParameterSpecException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
} catch (IllegalFormatException e) {
throw new RuntimeException(e);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
System.out.println("[INF] Attaching AlertCallbackDialog ...");
listener = (AlertCallbackDialog.AlertDialogListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement AlertDialogListener");
}
}
public interface AlertDialogListener {
void alertCallbackDialog();
}
}
I am wondering if there's something that is causing the ANR to as I have attempted to spawn off a new thread in handler and tried to remove as many things in the activity.