HelloWorld
HelloWorld

Reputation: 2355

Why did my HCE app reset a physical POS device?

I am poking around with Android Host-Based Card Emulation (HCE) on my Huawei phone (Android 10). My first goal is to make my phone aware that a point-of-sale (POS) is talking to it. I don't need to pay with it so far. As I don't know how to test my app reliably at my place I bother the local bakery (visiting the bakery once a day is not unusual in France).

Unfortunately this morning when I tested my app on the actual bakery POS, the POS reset to 0.00 although my app processCommandApdu function did not trigger. I tried approaching my phone a second time (after the baker re-entered the amount) and the POS reset again without any message displayed on my app side (Google Pay did not show either as it was not set as default app for NFC). I did not try a third time (because of other customers present) and paid with my NFC enabled Credit Card.

Please note that my phone NFC settings read as follows : NFC is enabled, default app is my App, and Use the currently running app.

I followed this guide as well as Android documentation on HCE. So my apduservice.xml registers all Visa Card AIDs shown on wikipedia and looks like :

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/servicedesc"
android:requireDeviceUnlock="false">
<aid-group
    android:category="payment"
    android:description="@string/aiddescription">
    <aid-filter android:name="A0000000031010"/>
    <aid-filter android:name="A0000000032010"/>
    <aid-filter android:name="A0000000032020"/>
    <aid-filter android:name="A0000000038010"/>
</aid-group>
</host-apdu-service>

The HCE service is like

class HostCardEmulatorService : HostApduService() {
companion object {
    val TAG = "Host Card Emulator"
    val STATUS_SUCCESS = "9000"
    val STATUS_FAILED = "6F00"
    
}

/**
* The function always returns success as this is just a test
*/
override fun processCommandApdu(commandApdu: ByteArray?, extras: Bundle?): ByteArray {

    Toast.makeText(this, "ProcessCommandApdu called with \n $commandApdu", Toast.LENGTH_SHORT).show()

    println("ProcessCommandApdu called!")

    return Utils.hexStringToByteArray(STATUS_SUCCESS)


}

/**
 * The `onDeactiveted` method will be called when the a different AID has been selected or the NFC connection has been lost.
 */
override fun onDeactivated(reason: Int) {
    Toast.makeText(this, "Connection lost because of $reason!", Toast.LENGTH_SHORT).show()
}
}

The manifest file requires the NFC permissions and asks for the NFC service :

  <uses-permission android:name="android.permission.NFC" />
<uses-feature
    android:name="android.hardware.nfc"
    android:required="true" />
<uses-permission android:name="android.permission.NFC_TRANSACTION_EVENT" />
... (standard ANdroid project)
<service
        android:name=".HostCardEmulatorService"
        android:exported="true"
        android:permission="android.permission.BIND_NFC_SERVICE">
        <intent-filter>
            <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
        </intent-filter>

        <meta-data
            android:name="android.nfc.cardemulation.host_apdu_service"
            android:resource="@xml/apduservice" />
    </service>

And the MainActivity starts/stops the HCE service :

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
    //...
    val hceService: HostCardEmulatorService = HostCardEmulatorService()
    val hceIntent = Intent(applicationContext, HostCardEmulatorService::class.java)

    binding.mainButton.setOnClickListener {

        when (serviceEnabled) {
            false -> {
                applicationContext.startService(hceIntent)
                if (isServiceRunning(HostCardEmulatorService::class.java)) {
                    binding.output.setText("Service enabled!")
                    // Changing state
                    serviceEnabled = true;
                }

            }
            true -> {
                applicationContext.stopService(hceIntent)
                binding.output.setText("Service disabled!")
                // Changing state
                serviceEnabled = false;
            }

        }
 }
}

Now I wonder why my app processCommandApdu function did not trigger while obviously the POS reached something on my phone ?

Furthermore can I reliably test my first goal (detect a POS is talking to my app) with another NFC enabled phone through an NFC reader app so that I don't bother the baker every day with my geeky tests? I mean is an NFC reader app running on another phone sufficient to trigger my app processCommandApduthe same way as an actual POS would do ?

Finally (if this can be answered) did the actual POS reset to 0.00 and did not keep the amount and asked for another trial because I returned the "Success command" in processCommandApdu function (in that case it would mean the function ran but the toast message did not appear or I missed it) ?

Any help appreciated :-)

Upvotes: 0

Views: 377

Answers (1)

HelloWorld
HelloWorld

Reputation: 2355

So thanks to this older SO question my first question regarding the non triggering of processCommandApdufunction can be answered. Indeed it did not trigger because it was not expecting the Application ID the EMV POS was sending. This AID corresponds to the PPSE (Proximity Payment System Environment) which requests the list of all the AIDs supported by the smart card.

Using this PPSE AID in the apduservice.xml made the HCE service work as expected

<aid-filter android:name="325041592E5359532E4444463031"/>

Regarding the testing of my app, using an NFC card reader does not work as they don't send the PPSE AID as the EMV POS does. Writing my own app that sends this special AID should work.

As far as the resetting of the amount shown by the EMV POS is concerned, it may have been caused by the Sim Card NFC chip or the mobile NFC antenna reacting in some way that was not expected by the POS.

Upvotes: 0

Related Questions