在安卓系统中读取多个NFC标签。当屏幕解锁时,IsoDep标签无法读取。

6

我一直在尝试创建一个可以读取两种不同类型NFC标签的应用程序。一种应该是HCE-IsoDep,在Nexus 5上模拟,而另一种则是Ndef标签。然而,我遇到了一个小问题:

我设法读取了这两种类型的标签,但不是我想要的方式。Ndef标签完全没有问题。但是当我尝试读取HCE标签时,就遇到了问题。只有在手机处于开启状态(模拟标签的屏幕亮着,但是已锁定)时,才能读取标签。无论我怎么解锁屏幕,它都无法再进行交互,我认为它试图进行数据传输。

如果我尝试不使用onNewIntent方法,而直接使用onTagDiscovered方法,则可以在HCE设备被锁定和未锁定时都能读取标签,但是那样我就无法读取Ndef标签了。在logcat中,当我解锁后读取HCE标签时,会收到消息:NfcService LLCP Activation Message

当锁定时,我会收到消息:NativeNfcTag Connect to a tag with a different handle(在此之前,我会得到:audio_hw_primary select_devices: out_snd_device(2: speaker) in_snd_device(0: ))。

我的代码如下:

Main:

public class NfcReader extends Activity implements OnMessageReceived {

private static String TAG = NfcReader.class.getSimpleName();

private Button sendButton;
private ProgressBar callProgress;


private NfcAdapter nfcAdapter;
private PendingIntent pIntent;
private IntentFilter[] writeTagFilters;
private String[][] mTechLists;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TextView dateView = (TextView) findViewById(R.id.dateTextView);

    nfcAdapter = NfcAdapter.getDefaultAdapter(this);
    pIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

    IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
    writeTagFilters = new IntentFilter[] { tagDetected };

    mTechLists = new String[][] {new String[] {
            Ndef.class.getName(),
            IsoDep.class.getName()
    }};
}

@Override
protected void onPause() {
    super.onPause();
    disableForegroundMode();
}

@Override
protected void onResume() {
    super.onResume();
    enableForegroundMode();
}

public void enableForegroundMode() {
    Log.d(TAG, "onResume");
    nfcAdapter.enableForegroundDispatch(this, pIntent, writeTagFilters, mTechLists);
}

public void disableForegroundMode() {
    Log.d(TAG, "onPause");
    nfcAdapter.disableForegroundDispatch(this);
}

@Override
public void onNewIntent(Intent intent) {
    Log.d(TAG, "onNewIntent");

    if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){

        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        Ndef nDef = Ndef.get(tag);

        if (nDef != null) {
            onNdefDiscovered(tag);
        }
        else {
            onTagDiscovered(tag);
        }
    }
}

public void onNdefDiscovered(Tag tag) {
    Log.d(TAG, "Ndef found");
    new ReadTag().execute(tag);
}

public void onTagDiscovered(Tag tag) {
    Log.d(TAG, "HCEfound"); 
    IsoDep isoDep = IsoDep.get(tag);
    IsoDepTransceiver transceiver = new IsoDepTransceiver(isoDep, this);
    transceiver.run();

}

@Override
public void onMessage(final byte[] message) {
    runOnUiThread(new Runnable() {

        @Override
        public void run() {
            String readFromHce = new String(message);
            TextView result = (TextView) findViewById(R.id.refTextView);
            result.setText(readFromHce);

        }
    });
}

@Override
public void onError(Exception exception) {
    onMessage(exception.getMessage().getBytes());
}
}

清单:

<uses-sdk
    android:minSdkVersion="19"
    android:targetSdkVersion="19" />

<uses-permission 
    android:name="android.permission.INTERNET" />

<uses-permission 
    android:name="android.permission.NFC" />

<uses-feature
    android:name="android.hardware.nfc"
    android:required="true" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:name=".HceReader"
        android:label="@string/app_name" >
        <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.NDEF_DISCOVERED"/>
            <action android:name="android.nfc.action.TECH_DISCOVERED"/>
            <action android:name="android.nfc.action.TAG_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>

        <meta-data
            android:name="android.nfc.action.TECH_DISCOVERED"
            android:resource="@xml/filter_nfc"/>

filter_nfc.xml

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">

    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
    </tech-list>

    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
</resources>

有人知道我做错了什么吗?我已经搜索了很多,但没有找到解决方法。

所以,我可以无问题地读取Ndef标签。只有当HCE设备屏幕锁定时,我才能读取emultated IsoDep标签。

非常感谢任何帮助
祝好

编辑:下面的代码可以工作

public class NfcReader extends Activity implements OnMessageReceived, ReaderCallback {

private static String TAG = NfcReader.class.getSimpleName();

private NfcAdapter nfcAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TextView result = (TextView) findViewById(R.id.refTextView);

    nfcAdapter = NfcAdapter.getDefaultAdapter(this);
}

@Override
protected void onPause() {
    super.onPause();
    nfcAdapter.disableReaderMode(this);
}

@Override
protected void onResume() {
    super.onResume();
    nfcAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A, null);
}

public void onTagDiscovered(Tag tag) {
    Log.d(TAG, "Tag Found"); 

    Ndef nDef = Ndef.get(tag);
    IsoDep isoDep = IsoDep.get(tag);

    if (nDef != null) {
        new ReadTag().execute(tag);
    }
    else if (isoDep != null){
        IsoDepTransceiver transceiver = new IsoDepTransceiver(isoDep, this);
        transceiver.run();      
    }
}

@Override
public void onMessage(final byte[] message) {
    runOnUiThread(new Runnable() {

        @Override
        public void run() {
            String readFromHce = new String(message);
            TextView result = (TextView) findViewById(R.id.refTextView);
            result.setText(readFromHce);
        }
    });
}

@Override
public void onError(Exception exception) {
    onMessage(exception.getMessage().getBytes());
}
}

感谢NFC专家的提示。

2个回答

8
在Android 4.4及以上版本中,您应该使用enableReaderMode()

在此模式下,NFC控制器仅充当NFC标签读写器,从而禁用了此设备上NFC适配器的任何点对点(Android Beam)和卡模拟模式。

对于与使用Android的基于主机的卡模拟在另一台Android设备上模拟的标签进行交互,建议使用FLAG_READER_NFC_A和FLAG_READER_SKIP_NDEF_CHECK标志。

谢谢你的回答。我知道在早期阶段我用的就是这个。然而,我当时并没有成功实现Ndef读取。我会再仔细看一下的。 - Markus
你可能想要省略标志FLAG_READER_SKIP_NDEF_CHECK。 - NFC guy
这个方法很妙!一切都按照我的意愿正常运行,而且额外的好处是代码量更少了 :) - Markus

1
你没有做错任何事情。不幸的是,你尝试的方法不会起作用。
如果你的手机同时运行卡模拟和点对点(Android Beam),并且读卡器(你的另一台手机)也支持点对点技术,点对点技术将优先于卡模拟
如果你考虑这个问题,这是完全有道理的:如果你把一个支持NFC的SIM卡放进你的手机,你将会有一些基于SIM的卡片模拟运行在外部主机上。如果点对点不能优先于卡模拟运行,Android Beam将停止工作,你将会看到与IsoDep标签的连接。
如果你的手机被锁定,在这种情况下点对点将被禁用,卡模拟将获得优先权。因此,在这种状态下,你可以访问卡模拟。
如果你想即使在屏幕解锁状态下也要访问卡模拟,你唯一的选择是使用不激活点对点协议的读卡器设备(例如独立的支付终端)。
在Android上,没有办法禁用点对点。在设置中禁用Android Beam也无济于事,因为只有高级别的Beam协议将被禁用。点对点协议仍将运行,以积极防止你意外访问其他人的卡模拟。这种行为是有意的,因为Google不希望人们因安全原因而意外访问支付卡模拟。

你的回答非常有道理。但是,我想知道为什么当我不使用onNewIntent函数而直接使用onTagDiscovery时它也可以工作。我可以在未激活beam功能的情况下,在另一个设备上从Nexus 5模拟标签进行扫描,而在模拟设备上解锁。我的想法是整个“pendingIntent”部分会确保我的活动将覆盖它。然而,显然我错了。 - Markus
2
随着 API19 的推出,引入了两种新的方法:enableReaderMode()disableReaderMode()。也许这可以帮助使仿真手机保持在仿真模式下。 - corvairjo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接