I have written an Android app to connect to an NFC Card (NFC type A) via APDUs. I connect and send select command to card successfully on debug mode when I have a few breakpoints. But when I install the app on the phone and run it, the application crashes. when I add a delay to the routine by Thread.sleep
, after connecting to tag and before selecting the applet, the problem is solved. Is this delay required? and what is that for? If I place 1500ms of delay, it still crashes and it surely needs 2000ms delay!!!
In addition, I don't know how to handle single detection of NFC card in each touch an Android card detection sound is buzzed multiple times.
我写了一个Android应用程序,通过APDU连接到NFC卡(NFC类型A)。当我有几个断点时,我在调试模式下成功地连接并发送选择命令到卡。但是当我在手机上安装应用程序并运行它时,应用程序崩溃了。当我通过Thread.sleep向例程添加延迟时,在连接到tag之后和选择applet之前,问题就解决了。是否需要延迟?那是为了什么如果我放置1500毫秒的延迟,它仍然崩溃,它肯定需要2000毫秒的延迟!此外,我不知道如何处理NFC卡的单次检测,在每次触摸Android卡检测声音是重复多次.
It would be great if somebody can help me on this.
These are the codes I have used:
如果有人能在这件事上帮我,那就太好了。以下是我使用过的代码:
MainActivity.java:
MainActivity.java:
package nfcapp;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.NfcA;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.google.common.primitives.Bytes;
import java.io.IOException;
import java.lang.reflect.Array;
public class MainActivity extends AppCompatActivity {
byte[] resp;
private NfcAdapter nfcAdapter;
static final byte[] APPLET_AID = {(byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06};
static final byte[] APPLET_PIN = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04};
static final byte[] APDU_SELECT = {(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x00};
TextView tv_status;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_status = findViewById(R.id.tv_status);
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
Toast.makeText(this, "NFC is not supported", Toast.LENGTH_LONG).show();
finish();
} else if (!nfcAdapter.isEnabled()) {
Toast.makeText(this, "Turn on device NFC", Toast.LENGTH_LONG).show();
}
}
@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE);
nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
}
@Override
protected void onPause() {
super.onPause();
nfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if ( NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction()) ) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (NfcA.get(tag) != null) {
NfcA nfcaTag = NfcA.get(tag);
try {
nfcaTag = SCConnect(nfcaTag);
if (nfcaTag == null) {
finish();
}
tv_status.append("Connected To NFC Card Successfully!\n");
Thread.sleep(2000);
resp = SCSelectApplet(nfcaTag, APPLET_AID);
tv_status.append("SCSelectApplet: " + byteArrayToHexString(resp) + "\n");
} catch (IOException e) {
tv_status.append("IOException : "+ e.getMessage().toString() + "\n");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
Toast.makeText(this, "Unsupported tag technology", Toast.LENGTH_LONG).show();
}
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
}
private NfcA SCConnect(NfcA nfcA) throws IOException {
nfcA.connect();
if (!nfcA.isConnected()) {
return null;
}
return nfcA;
}
private static byte[] SCSelectApplet(NfcA nfcA, byte[] AID) throws IOException {
if(!nfcA.isConnected()) {
return null;
}
byte[] APDU = new byte[13];
System.arraycopy(APDU_SELECT,0,APDU,0,5);
if (AID.length != 0) {
Array.setByte(APDU,4,(byte)AID.length);
System.arraycopy(AID,0,APDU,5,AID.length);
}
byte[] resp = nfcA.transceive(APDU);
return resp;
}
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String byteArrayToHexString(byte[] bytes) {
if (bytes == null) return null;
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
}
AndroidManifest.xml:
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<uses-permission android:name="android.permission.NFC" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/Theme.NFCApp"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml:
Activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_status"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_marginTop="100dp"
android:layout_marginStart="5dp"
android:textSize="18dp"
android:justificationMode="inter_word"
android:hint="Status" />
</LinearLayout>
更多回答
application crashes
means nothing really. Post the stacktrace
应用程序崩溃实际上并不意味着什么。发布堆栈跟踪
@MarcinOrlowski Thank you for your answer Marcin. I solved the crash problem. That was due to trying to find the length of an empty byte array happening when card does not respond. I read few related posts about Android NFC. I think that delay is needed to help balance card power consumption from mobile NFC. What do you think?
@MarcinOrlowski谢谢你的回答,Marin。我解决了撞车问题。这是因为当卡片没有响应时,试图找出空字节数组的长度。我很少读到关于Android NFC的相关文章。我认为延迟是必要的,以帮助平衡移动NFC对卡的功耗。你认为如何?
There are obvious problems with your code, you are doing NFC comms on the main thread which is against recommendations and then adding a sleep on the main thread which might cause an ANR. I would recommend that you don't use enableForegroundDispatch
and use enableReaderMode
instead see an example at stackoverflow.com/a/64921434/2373819
您的代码有明显的问题,您正在主线程上进行NFC通信,这违反了建议,然后在主线程上添加了一个睡眠,这可能会导致ANR。我建议您不要使用enableForeground Dispatch,而使用enableReaderMode,请参阅stackoverflow.com/a/64921434/2373819上的示例
我是一名优秀的程序员,十分优秀!