HCE Support

Suppose I wanted to implement Host Card Emulation (HCE) in my Fuse app, which requires using Android’s HostApduService class. Assuming that there isn’t any support for this in Fuse (or is there?!) is it possible to write some sort of native plugin to enable an interface to this? And do you have any tutorials / documentation on how to do this?
Thanks again.

Ok, this is Native Interop, right?

Did a quick search on the forums, and found this post.

There are still no known NFC bindings as of now, so you should be looking into writing some Foreign code.

For a start, see what the docs have to offer. Then, I would suggest you to find some suitable Gradle (Java) and Cocoapods (Objective-C) libraries that do what you need, and check out how similar libs have been wrapped in Uno before.

Ok, thanks. I’ll have a good look through the foreign code stuff. Looks like it should do what I need. I’m sure I’ll have some more questions later! Cheers.

Can I just ask about Uno…? I don’t find any references to Uno on the web (apart from here). Is this something that you (Fuse) have invented?

Yes, Uno was made by the core team of Fuse. As explained in the linked docs, it closely resembles C#, so most C#-things apply to Uno too.

Great, thanks.

Hi (sorry, please ignore the last message)

In order to receive an NFC message in Android I believe I have to implement onNewIntent() in the main Activity. I can access the main Activity from Uno but how can I add an implementation of onNewIntent() to this Activity?

Thanks

Hi Iain,
In Uno, the way to dispatch an activity for a result is:

ActivityUtils.StartActivity(intent, OnResult);

When intent is a Java.Object and where the callback method looks like this:

extern(android) void OnResult(int resultCode, Java.Object intent, object info)
{
    debug_log "blam!: " + resultCode + " - "+ intent;
}

However if you can show an example (maybe from google’s docs?) of what you are trying to do I can guide you a little more precisely.

Hope this helps!

Hi Chris,

Thanks for your reply. In my Android main Activity I want to do this:

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    log("Received!");
}

This is to capture an NFC Intent. I believe that you must capture NFC Intents in this way (as opposed to using a dynamically created Intent/BroadcastReceiver).

So somehow I need this method to be overridden as above in my main Activity in order to capture an NFC Intent.

Any ideas how I can do this?

Many thanks.

Sorry for the confusion. I meant to ask for any code sample/docs that show that you need to use onNewIntent in this way. We have ways to hook into this but I’d prefer to recommend a cleaner one :slight_smile:

Hey Chris,

Below is some pure Android code that receives an NFC Intent and extracts the Tag ID or NDEF data. It also uses the Foreground Dispatch System as per Google’s documentation (using onPause, onResume etc).

Essentially I want to have this functionality in Fuse such that I can read simple NDEF data packets from another NFC device and then present them to the JS layer in Fuse.

My main concern is how to override onNewIntent, onPause and onResume using foreign code etc.

Hope that explains things better.

Many thanks.

public class MainActivity extends Activity {

    private LinearLayout _logs;

    private PendingIntent _pendingIntent;
    private IntentFilter[] _filters;
    private String[][] _techLists;

    private void log(String s) {
        TextView textView = new TextView(this);
        textView.setPadding(20, 10, 20, 10);
        textView.setText(s);
        _logs.addView(textView);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        _logs = new LinearLayout(this);
        _logs.setOrientation(LinearLayout.VERTICAL);
        ViewGroup viewGroup =
            (ViewGroup)((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
        viewGroup.addView(_logs);
        doNfc();
    }

    private void doNfc() {
        log("Started");
        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if(nfcAdapter == null) log("No NFC found");
        else log("NFC found");
        _pendingIntent = PendingIntent.getActivity(
            this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        IntentFilter ndefDiscovered = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        try {
            ndefDiscovered.addDataType("*/*");
        }
        catch (IntentFilter.MalformedMimeTypeException e) {
            throw new RuntimeException(e);
        }
        IntentFilter tagDiscovered = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
        _filters = new IntentFilter[] { ndefDiscovered, tagDiscovered };
        _techLists = new String[][] { new String[] {
                NfcV.class.getName(),
                NfcF.class.getName(),
                NfcA.class.getName(),
                NfcB.class.getName(),
                MifareClassic.class.getName()
        }};
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            log("NDEF received");
            Parcelable[] rawMessages =
                intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawMessages != null) {
                NdefMessage[] messages = new NdefMessage[rawMessages.length];
                for (int i = 0; i < rawMessages.length; i++) {
                    messages[i] = (NdefMessage) rawMessages[i];
                }
            }
        }
        else if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
            log("Tag received");
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            byte[] bytes = tag.getId();
            String id = String.format(
                "%010d",
                new BigInteger(1, new byte[]{bytes[3], bytes[2], bytes[1], bytes[0]}));
            log("Data: " + id);
        }
        else log("Unknown intent");
    }

    @Override
    protected void onPause() {
        super.onPause();
        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (nfcAdapter != null) nfcAdapter.disableForegroundDispatch(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (nfcAdapter != null)
            nfcAdapter.enableForegroundDispatch(this, _pendingIntent, _filters, _techLists);
    }

}

Docs describing this are here:
https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc.html

See: Using the Foreground Dispatch System

Thanks!

In addition to this, it is possible to dynamically set up an IntentFilter to capture Intents (without overriding onNewIntent) but this does not work with NFC Intents due to the nature of those Intents. I have not found any documentation describing this apart from comments on Stack Overflow and from my own experience that capturing NFC Intents in this way definitely does not work.

It’s not documented but this will be much cleaner than trying to add things directly to the activity.

Call this from java (a foreign method would be perfect)

com.fuse.Activity.IntentListener listener = new com.fuse.Activity.IntentListener() {
    public void onIntent (Intent newIntent) {
	    Log.d("Foo", "yay!");
	}
};
com.fuse.Activity.subscribeToIntents(listener, NfcAdapter.ACTION_NDEF_DISCOVERED);
com.fuse.Activity.subscribeToIntents(listener, NfcAdapter.ACTION_TAG_DISCOVERED);

I hope this helps

Thanks for this Chris. I will try it out soon and get back to you.

I tried Chris’s solution and wrote this external code.

package com.visa.aira;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;

import android.nfc.NfcAdapter;
import android.util.Log;

import android.nfc.tech.MifareClassic;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcB;
import android.nfc.tech.NfcF;
import android.nfc.tech.NfcV;

public class NfcReader {

    private final static String TAG = "FuseForeignCode:NfcReader";

    private static IntentFilter[] _filters;
    private static String[][] _techLists;
    private static Activity activity;

    public static void read() {
        Log.d(TAG, "Starting NfcReader()");
        activity = com.fuse.Activity.getRootActivity();
        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
        if (nfcAdapter == null) Log.d(TAG, "No NFC found");
        else Log.d(TAG, "NFC found");

        com.fuse.Activity.IntentListener listener = new com.fuse.Activity.IntentListener() {
            public void onIntent(Intent newIntent) {
                Log.d(TAG, "NEW INTENT!");
            }

            public void onPause() {
                Log.d(TAG, "ON PAUSE!");
                NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
                if (nfcAdapter != null) nfcAdapter.disableForegroundDispatch(activity);
            }

            public void onResume() {
                Log.d(TAG, "ON RESUME!");
                NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
                PendingIntent _pendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
                IntentFilter ndefDiscovered = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
                try {
                    ndefDiscovered.addDataType("*/*");
                } catch (IntentFilter.MalformedMimeTypeException e) {
                    throw new RuntimeException(e);
                }
                IntentFilter tagDiscovered = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
                _filters = new IntentFilter[]{ndefDiscovered, tagDiscovered};
                _techLists = new String[][]{new String[]{
                        NfcV.class.getName(),
                        NfcF.class.getName(),
                        NfcA.class.getName(),
                        NfcB.class.getName(),
                        MifareClassic.class.getName()
                }};


                if (nfcAdapter != null)
                    nfcAdapter.enableForegroundDispatch(activity, _pendingIntent, _filters, _techLists);
            }
        };

        com.fuse.Activity.subscribeToIntents(listener, NfcAdapter.ACTION_NDEF_DISCOVERED);
        com.fuse.Activity.subscribeToIntents(listener, NfcAdapter.ACTION_TAG_DISCOVERED);
    }

}

I can see NFC Found log but unfortunately, I can’t see any logs from onIntent or onPause or onResume when I touch my card.

I thought I could see NEW INTENT log at least but it doesn’t appear.

Thanks for the info Baris. Strange stuff. Sadly I don’t have any NFC gear to test this with yet ( I guess Fuse better get shopping :slight_smile: )

Also the interface is:

public interface IntentListener
{
    void onIntent (Intent newIntent);
}

There are no `onPause etc methods in there

Hi everyone.

I’d really appreciate it if someone could share a working example, at least for reading NFC tags when you come right.

I’ll make one of those now then :slight_smile: