1

I'm working on NFC and currently try to write code for the Open Mobile API built into Android 9.0, but don't understand the way to pass the Executor parameter in SEService.

public class MainActivity extends Activity implements SEService.OnConnectedListener {

    final String LOG_TAG = "NfcTest";

    private SEService seService;

    private Button button;


    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        LinearLayout layout = new LinearLayout(this);
        layout.setLayoutParams(new LayoutParams(
                LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT));

        button = new Button(this);
        button.setLayoutParams(new LayoutParams(
                LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT));

        button.setText("Click Me");
        button.setEnabled(false);
        button.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                try {

                    Reader[] readers = seService.getReaders();
                    if (readers.length < 1)
                        return;


                    Session session = readers[0].openSession();



                    Channel channel = session.openLogicalChannel(new byte[]{
                            (byte) 0xF0, 0x51, (byte) 0xBC, 0x53, 0x54, 0x69, 0x64,
                            (byte) 0x4D, 0x6F, 0x62, 0x69, (byte) 0x6C,
                            (byte) 0x65, 0x2D, (byte) 0x49, 0x44});


                    byte[] respApdu = channel.transmit(new byte[]{
                            (byte) 0x90, 0x10, 0x00, 0x00, 0x00});

                    channel.close();

                    // Parse response APDU and show text but remove SW1 SW2 first
                    byte[] helloStr = new byte[respApdu.length - 2];
                    System.arraycopy(respApdu, 0, helloStr, 0, respApdu.length - 2);
                    Toast.makeText(MainActivity.this, new String(helloStr), Toast.LENGTH_LONG).show();
                } catch (Exception e) {
                    Log.i(LOG_TAG, "Message: " + e.getClass().getCanonicalName());
                    if(e.getClass().isInstance(new NoSuchElementException()))
                        Toast.makeText(MainActivity.this, "NFC sim", Toast.LENGTH_LONG).show();
                    else if (e.getClass().isInstance(new IOException())){
                        Toast.makeText(MainActivity.this, "No sim found", Toast.LENGTH_LONG).show();
                    }
                    else{
                        Toast.makeText(MainActivity.this, "Normal sim", Toast.LENGTH_LONG).show();
                    }
                }
            }
        });

        layout.addView(button);
        setContentView(layout);


        try {

            seService = new SEService(this, this,this);
        } catch (SecurityException e) {
            Log.e(LOG_TAG, "Binding not allowed, uses-permission org.simalliance.openmobileapi.SMARTCARD?");
        } catch (Exception e) {
            Log.e(LOG_TAG, "Exception: " + e.getMessage());
        }
    }

    @Override
    protected void onDestroy() {
        if (seService != null && seService.isConnected()) {
            seService.shutdown();
        }
        super.onDestroy();
    }

    public void serviceConnected(SEService service) {
        Log.i(LOG_TAG, "seviceConnected()");
        button.setEnabled(true);
    }

    @Override
    public void onConnected() {

    }
}

Problem occurs on:

seService = new SEService(this, this,this);

First and last parameter accepted the middle one executer appears error!

java.util.concurrent.Executor this

How to pass this?

Michael Roland
  • 39,663
  • 10
  • 99
  • 206
  • @MichaelRoland, it's working but still service not running, what should I pass in **onConnected()** error occurred: **android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.** – muhammad Ahmad Dec 13 '18 at 11:54
  • See my updated answer, you have to access UI elements from the UI thread... – Michael Roland Dec 13 '18 at 14:26
  • Well, Thanks, @MichaelRoland Have you any work over OpenMobileAPI? this piece of code working fine on a different phone (Using external OpenMobileApi), it uses internal (SDK 28) didn't get the readers array! *Testing Device [Google Pixel] – muhammad Ahmad Dec 18 '18 at 11:43
  • I could be wrong, but I don't expect that the Pixel (and probably also the Pixel 2) devices will ever get an actual OMAPI terminal implementation for access to their UICC or eSE. – Michael Roland Dec 19 '18 at 10:42

1 Answers1

5

You get this error because the second aregument of the constructor SEService must be a java.util.concurrent.Executor. You MainActivity (this object) does not implement an executor.

THis executor is used by the SEService to invoke callback methods. So you have to create such an executor yourself in order to pass it to SEService. YOu could, for instance, simply create an ExecutorService with a single worker thread to handle the callbacks:

ExecutorService pool = Executors.newSingleThreadExecutor();
seService = new SEService(this, pool, this);

Be aware though, that these callbacks are then executed on a worker thread (not the main thread of your app). Consequently, if you want to perform any UI operations from within the callbacks, you will need to explicitly run them on the UI thread, e.g.

runOnUiThread(new Runnable() {
    void run() {
        button.setEnabled(true); // ... or some other UI modifications
    }
});
Michael Roland
  • 39,663
  • 10
  • 99
  • 206
  • This was helpful. – Kiran k g Jan 08 '19 at 02:08
  • @Michael Roland Does the runOnUiThread code to modify the UI run only after the ExecutorService code is completely finished? If not, is there a way to add some code in the runOnUiThread section so that it only executes after the background processing in ExecutorService has completed? – AJW Nov 10 '21 at 15:49