Reading multiple NFC-tags in Android. IsoDep-tag wont read while screen is unlocked
Asked Answered
I

2

6

I've been trying to create an app that can read two different types of NFC-tags. One is supposed to be HCE-IsoDep, emulated on a Nexus 5, and one is a Ndef-tag. I have encountered a slight problem however:

I manage to read both types of tags, but not the way I want to do it. The Ndef tag is no problem at all. It is when I try to read the HCE tag that I encounter my problem. I can only read the tag when the phone is on, which I emulate the tag on is locked (screen on, but lock is on). Whenever I unlock the screen it won't interact anymore, and as far as I can understand it tries to beam instead.

If I try to do it without the onNewIntent and just go straight to a onTagDiscovered, it works both while the HCE-device is locked and unlocked, but then I can't read the Ndef-tag. In logcat I receive the message: NfcService LLCP Activation Message when I read the HCE-tag when unlocked.

When locked I receive the message: NativeNfcTag Connect to a tag with a different handle (and prior to that I get: audio_hw_primary select_devices: out_snd_device(2: speaker) in_snd_device(0: ))

My code looks as follows:

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());
}
}

Manifest:

<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>

Do anyone have an idea what I´m doing wrong? I´ve searched around quite a bit without finding a solution to this. So again. I can read the Ndef-tag without problems. I can only read the emultated IsoDep-tag when the screen on the HCE-device is locked.

Thankful for any help
Regards

Edit: Code below is working

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());
}
}

Big thanks to NFC guy for the tip.

Ingamar answered 20/12, 2013 at 7:21 Comment(0)
C
8

On Android 4.4 and above, you should use enableReaderMode() for this.

In this mode the NFC controller will only act as an NFC tag reader/writer, thus disabling any peer-to-peer (Android Beam) and card-emulation modes of the NFC adapter on this device.

For interacting with tags that are emulated on another Android device using Android's host-based card-emulation, the recommended flags are FLAG_READER_NFC_A and FLAG_READER_SKIP_NDEF_CHECK.

Chaim answered 20/12, 2013 at 22:20 Comment(3)
Thanks for your answer. I know that is what I used when I got it to work in the early stages. However I didn´t successfully implement the Ndef reading then. I will take another look at it.Ingamar
You may want to omit the flag FLAG_READER_SKIP_NDEF_CHECK then.Chaim
This did the trick! Everything is working exactly as I wanted and as a bonus I got less code :)Ingamar
L
1

You did nothing wrong. What you've tried to do will not work unfortunately.

If your phone has both card emulation and peer-to-peer (android beam) running, and the reader (your other phone) supports peer-to-peer as well, the peer-to-peer technology will take priority over the card emulation.

This makes perfect sense if you think about it: If you put a NFC enabled SIM card into your phone you will have a few SIM based off-host card emulations running. If peer-to-peer would not take priority over card emulation android beam would stop working and you would see connections to IsoDep tags instead.

In the case that your telephone is locked, peer-to-peer will be disabled and the card emulation gets priority. Therefore you can access the card emulation in this state.

If you want to access the card emulation even in the screen unlocked state your only option is to use a reader device that does not activate the peer-to-peer protocol (like a stand-alone payment terminal for example).

On Android there is no way to disable peer-to-peer. Disabling Android Beam in the settings will not help you either because only the high level Beam protocol will be disabled. The peer-to-peer protocol will still run to actively prevent you from seeing other peoples card emulation. This behavior is intended because Google does not want people accessing the payment card emulation by accident for security reasons.

Lymphoid answered 20/12, 2013 at 9:21 Comment(2)
Your answer makes perfect sense. But I wonder why it works when I don´t use the onNewIntent function but instead uses the onTagDiscovery directly. I can scan the emulated tag from the Nexus 5 on the other device without activating the beam-function while unlocked on the emulated device. My thought was that the whole "pendingIntent" part would make sure that my activity would override it. I was clearly wrong though.Ingamar
Together with API19 two new methods are introduced: enableReaderMode() and disableReaderMode() Maybe this can help to make the emulating phone stay in emulation mode.Tobolsk

© 2022 - 2024 — McMap. All rights reserved.