Search code examples
androidnfcpaymentapduhce

UnsupportedOperationException for Android API method CardEmulation.getInstance()


I need to check if card emulation is enable on a device. I found one method here:

boolean isDefault = CardEmulation
                .getInstance(NfcAdapter.getDefaultAdapter(this))
                .isDefaultServiceForCategory(
                        new ComponentName(this, MyPaymentService.class),
                        CardEmulation.CATEGORY_PAYMENT);

It looks like this works on some devices, but not for all devices.

For example, on the Samsung GT-I9300I (with Android 4.4), there is an NFC module, but it doesn't show the Tap-and-pay options in the settings.

When my app runs on that device I get the following error:

E/CardEmulation: This device does not support card emulation
09-26 16:41:13.592 2625-2625/? E/AndroidRuntime: FATAL EXCEPTION: main
                                                 Process: com.android.settings, PID: 2625
                                                 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.settings/com.android.settings.nfc.PaymentDefaultDialog}: java.lang.UnsupportedOperationException
                                                     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2441)
                                                     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2500)
                                                     at android.app.ActivityThread.access$900(ActivityThread.java:171)
                                                     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1309)
                                                     at android.os.Handler.dispatchMessage(Handler.java:102)
                                                     at android.os.Looper.loop(Looper.java:146)
                                                     at android.app.ActivityThread.main(ActivityThread.java:5679)
                                                     at java.lang.reflect.Method.invokeNative(Native Method)
                                                     at java.lang.reflect.Method.invoke(Method.java:515)
                                                     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
                                                     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
                                                     at dalvik.system.NativeStart.main(Native Method)
                                                  Caused by: java.lang.UnsupportedOperationException
                                                     at android.nfc.cardemulation.CardEmulation.getInstance(CardEmulation.java:159)
                                                     at com.android.settings.nfc.PaymentBackend.(PaymentBackend.java:53)
                                                     at com.android.settings.nfc.PaymentDefaultDialog.onCreate(PaymentDefaultDialog.java:57)
                                                     at android.app.Activity.performCreate(Activity.java:5582)
                                                     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1093)
                                                     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2405)
                                                     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2500) 
                                                     at android.app.ActivityThread.access$900(ActivityThread.java:171) 
                                                     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1309) 
                                                     at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                     at android.os.Looper.loop(Looper.java:146) 
                                                     at android.app.ActivityThread.main(ActivityThread.java:5679) 
                                                     at java.lang.reflect.Method.invokeNative(Native Method) 
                                                     at java.lang.reflect.Method.invoke(Method.java:515) 
                                                     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291) 
                                                     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107) 
                                                     at dalvik.system.NativeStart.main(Native Method)

However, according to the documentation, CardEmulation is available since API level 19 (Android 4.4).

So can anyone explain why I'm getting this exception? How can I overcome this problem?


Solution

  • The error is quite clear in the log output:

    E/CardEmulation: This device does not support card emulation
    

    This class can only be used on devices that support card emulation and AID-based routing configuration. This is also documented in the documentation of the class CardEmulation:

    Use of this class requires the FEATURE_NFC_HOST_CARD_EMULATION to be present on the device.

    Consequently, you must only use the method getInstance() on devices that actually support the HCE feature. Note that host card emulation is a bit misleading here since that feature is also required to manage routing configuration using an OffHostApduService declaration and to use the CardEmulation class for any operations related to OffHostApduService. So this feature applies to both, HCE and management of routing configuration for secure element based card emulation. However, some device manufacturers developed other mechanisms for testing for the availability (and potentailly also for managing the routing) of secure element based card emulation.

    You can use the following code to check for the HCE feature before calling the CardEmulation.getInstance() method:

    boolean isDefault = false;
    if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
        isDefault  = CardEmulation.getInstance(NfcAdapter.getDefaultAdapter(this))
                                  .isDefaultServiceForCategory(
                                          new ComponentName(this, MyPaymentService.class),
                                          CardEmulation.CATEGORY_PAYMENT);
    }