Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

android

nfc

hce

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.

like image 808
Markus Avatar asked Dec 20 '13 07:12

Markus


2 Answers

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.

like image 109
NFC guy Avatar answered Oct 12 '22 23:10

NFC guy


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.

like image 30
Nils Pipenbrinck Avatar answered Oct 13 '22 01:10

Nils Pipenbrinck