How to communicate with HostApduService from an Activity

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-04 09:52:13

Whether you can use onBind or not I do not know, but I recently worked with a BroadcastReceiver from which I had to start a Service. You cannot bind a Service from a BroadcastReceiver according to docs, you can only start it. I needed to send some data to the Service from my BroadcastReceiver at some later point, and since the binder techniques was not available to me, I had to find a different way to communicate with the Service, much like your case where you don't have a reference to it.

I did some research but could not find any solution, but then I remembered that you can pass intent data with the startService(intent) call. I start my Service work in onCreate instead, as onCreate is only called once when the Service is created.

In your Activity

public void sendDataToService(){
    Intent intent = new Intent(context, MyService.class);
    intent.putExtra("message", SOME_DATA);
    context.startService(intent);
}

In your Service

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    // Check if intent has extras
    if(intent.getExtras() != null){

        // Get message
        int message = intent.getExtras().getInt("message");
    }

    return START_NOT_STICKY;
}

This may be some sort what of a hack since "startService" does not sound like it should be used to send messages, and am not sure if this is exactly what you need, but it worked for me, so I hope it works for you. Cheers

Edit: BTW. I use it to tell a LocationService that a particular activity no longer want location updates.

I ended up taking a different approach to solving this same problem. When I bind to my HostApduService subclass, I grab a handle to the Messenger interface returned by the HostApduService onBind implementation.

Here's some sample code. This would all go in your activity implementation (calling it MyActivity here, communicating with MyHostApduServiceSubclass). Here's what MyActivity would need to include:

private Messenger mAPDUMessenger;
...
@Override
protected void onStart() {
    super.onStart();
    Context context = getApplicationContext();
    Intent apduIntent = new Intent(montext, ContactlessApduService.class);
    context.bindService(apduIntent, mAPDUConnection, Context.BIND_AUTO_CREATE);
}
...
private ServiceConnection mAPDUConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName className, IBinder service) {
        // The HostApduService has a final override on the onBind() service method that returns
        // an IMessageHandler interface that we can grab and use to send messages back to the
        // terminal - would be better to get a handle to the running instance of the service so
        // that we could make use of the HostApduService#sendResponseApdu public method
        mAPDUMessenger = new Messenger(service);
        registerAPDUMessengerIntentFilters();
        // ^ This method sets up my handlers for local broadcast messages my BroadcastReceiver processes.
    }
...
}
...
private void registerAPDUMessengerIntentFilters() {
    LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(MyActivity.this);

    IntentFilter intentFilter = new IntentFilter(MyHostApduServiceSubclass.ACTION_PPSE_APDU_SELECT);
    lbm.registerReceiver(apduMessageBroadcastReceiver, intentFilter);
}
...
BroadcastReceiver apduMessageBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(MyHostApduServiceSubclass.ACTION_PPSE_APDU_SELECT)) {
            sendResponseApdu(MyActivity.PPSE_APDU_SELECT_RESPONSE_BYTES);
        }
    }
};
...
public final void sendResponseApdu(byte[] responseApdu) {
    Message responseMsg = Message.obtain(null, MyHostApduServiceSubclass.MSG_RESPONSE_APDU);
    // ^ Note here that because MSG_RESPONSE_APDU is the message type
    //   defined in the abstract HostApduService class, I had to override
    //   the definition in my subclass to expose it for use from MyActivity.
    //   Same with the KEY_DATA constant value below.
    Bundle dataBundle = new Bundle();
    dataBundle.putByteArray(MyHostApduServiceSubclass.KEY_DATA, responseApdu);
    responseMsg.setData(dataBundle);
    try {
        mAPDUMessenger.send(responseMsg);
    } catch (RemoteException e) {
        // Do something with the failed message
    }
}

And then your HostApduService subclass would just need to send a broadcast to your activity indicating what APDU command was received. Here is what would need to be included in MyHostApduServiceSubclass:

public static final String ACTION_PPSE_APDU_SELECT = "ACTION_PPSE_APDU_SELECT";

// Abstract super class constant overrides
public static final String KEY_DATA = "data";
public static final int MSG_RESPONSE_APDU = 1;

@Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
    Context context = getApplicationContext();
    LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
    if (Arrays.equals(MyHostApduServiceSubclass.PPSE_APDU_SELECT_BYTES, commandApdu)) {
        lbm.sendBroadcast(new Intent(ACTION_PPSE_APDU_SELECT));
    }
    return null;
    // ^ Note the need to return null so that the other end waits for the
    //   activity to send the response via the Messenger handle
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!