Android NFC HCE host-apdu-service >> Problem with aid-filter declaration

468 Views Asked by At

I am trying to build a HCE NFC Android Application for payment using AID Visa.

When launching, aid seem not to be recognized at manifest and its ressource file declaration.

Indeed, OS ApduServiceInfo service throws a "Not adding <aid-group> with empty or invalid AIDs" message

My Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

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

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:theme="@style/Theme.MyHCE"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:fullBackupContent="@xml/backup_rules"
        android:supportsRtl="true"
        tools:targetApi="31"
        android:dataExtractionRules="@xml/data_extraction_rules">

        <activity
            android:name=".views.MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:launchMode="singleInstance"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".services.MyHostApduService"
            android:exported="true"
            android:permission="android.permission.BIND_NFC_SERVICE">
            <intent-filter>
                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <meta-data
                android:name="android.nfc.cardemulation.host_apdu_service"
                android:resource="@xml/apduservice" />
        </service>

My host-apdu xlm ressource file:

<?xml version="1.0" encoding="utf-8"?>

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/hce_host_apdu_service_description"
    android:requireDeviceUnlock="false"
    android:apduServiceBanner="@raw/my_hce_banner">

    <aid-group android:description="@string/aid_group_description_payment"
        android:category="payment"\>
        <aid-filter android:name="325041592E5359532E4444463031"
            android:description="@string/aid_description_ppse"/>
        <aid-filter android:name="315041592E5359532E44444630"
            android:description="@string/aid_description_pse"/>
        <aid-filter android:name="A0000000031010"
            android:description="@string/aid_description_visa"/>
    </aid-group\>

</host-apdu-service>

My Host Adpu Service Java code:

public class MyHostApduService extends HostApduService {

    @Override public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
        vibrate(); // personal function that makes the device vibrate
        logMessage("APDU command detected"); // function that logs a tagged message
        Toast.makeText(getApplicationContext(), "APDU detected", Toast.LENGTH_SHORT);
        // to be developped...
        return new byte[0];
    }

    @Override public void onDeactivated(int reason) {
        logMessage("desactivation");
        Toast.makeText(getApplicationContext(), "desactivation", Toast.LENGTH_SHORT);
    }
}

Here are depencencies declared to gradle/app

dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'}

OS recognizes the app as I can selected it as my prefered payment app from NFC settings menu But when launching, AID seem not to be taken as if I haven't declare them at the manifest and its ressource file.

Of course, APDU commands are not routed to my app when I use it on a POS device... :-(

I have deeply read Android NFC HCE documentation https://developer.android.com/guide/topics/connectivity/nfc/hce but cannot fix the problem

I runs the app on API 29 to 33 (Android 10 to 13) on Samsung S9 and A54. Maybye the android documentation is not up to date and some security features have been introduced since and require some more declaration ?

Someone could help ?

1

There are 1 best solutions below

3
Michael Fehr On

It seems that your "host-apdu xls ressource file" is missing a data field. Usually a Credit Card reader is not asking for an AID but tries to read the "directory" by selecting "PPSE 2PAY.SYS.DDF01" and this is the starting point for the communication. This string is the general filter you need to append to your xls file (the data behind name is just the hex encoded data of the string):

Below you find my xls file a project on my own and it is much than you need it as I tested my app for Visa-, Master- and American Express card:

<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<!-- This file defines which AIDs this application should emulate cards for.

     Vendor-specific AIDs should always start with an "F", according to the ISO 7816 spec. We
     recommended vendor-specific AIDs be at least 6 characters long, to provide sufficient
     uniqueness. Note, however, that longer AIDs may impose a burden on non-Android NFC terminals.
     AIDs may not exceed 32 characters (16 bytes).

     Additionally, AIDs must always contain an even number of characters, in hexadecimal format.

     In order to avoid prompting the user to select which service they want to use when the device
     is scanned, this app must be selected as the default handler for an AID group by the user, or
     the terminal must select *all* AIDs defined in the category simultaneously ("exact match").
-->
<!-- BEGIN_INCLUDE(CardEmulationXML) -->
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/service_name"
    android:apduServiceBanner="@drawable/hce_260_96"
    android:requireDeviceUnlock="false">
    <!--
    If category="payment" is used for any aid-groups, you must also add an android:apduServiceBanner
    attribute above, like so:

    android:apduServiceBanner="@drawable/settings_banner"

     apduServiceBanner should be 260x96 dp. In pixels, that works out to...
       - drawable-xxhdpi: 780x288 px
       - drawable-xhdpi:  520x192 px
       - drawable-hdpi:   390x144 px
       - drawable-mdpi:   260x96  px

    The apduServiceBanner is displayed in the "Tap & Pay" menu in the system Settings app, and
    is only displayed for apps which implement the "payment" AID category.

    Since this sample is implementing a non-standard card type (a loyalty card, specifically), we
    do not need to define a banner.

    Important: category="payment" should only be used for industry-standard payment cards. If you are
        implementing a closed-loop payment system (e.g. stored value cards for a specific merchant
        or transit system), use category="other". This is because only one "payment" card may be
        active at a time, whereas all "other" cards are active simultaneously (subject to AID
        dispatch).
    -->

    <aid-group android:description="@string/card_title" android:category="payment">
        <!-- select PPSE 2PAY.SYS.DDF01 -->        
        <aid-filter android:name="325041592E5359532E4444463031"/>
        <!-- VisaCard -->
        <aid-filter android:name="A0000000031010"/>
        <aid-filter android:name="A0000000032010"/>
        <aid-filter android:name="A0000000032020"/>
        <aid-filter android:name="A0000000038010"/>
        <!-- MasterCard -->
        <aid-filter android:name="A0000000041010"/>
        <aid-filter android:name="A0000000049999"/>
        <aid-filter android:name="A0000000043060"/>
        <aid-filter android:name="A0000000046000"/>
        <aid-filter android:name="A0000000048002"/>
        <aid-filter android:name="A0000000050001"/>
        <!-- American Express -->
        <aid-filter android:name="A00000002501"/>
        <aid-filter android:name="A000000025010801"/>
        <aid-filter android:name="A00000079001"/>
        <!-- German GiroCard -->
        <aid-filter android:name="a00000005945430100"/>
        <aid-filter android:name="a0000003591010028001"/>
        <aid-filter android:name="d27600002547410100"/>
        <aid-filter android:name="a0000000043060"/>
    </aid-group>
<!-- END_INCLUDE(CardEmulationXML) -->
</host-apdu-service>