如何从Activity与HostApduService进行通信

时间:2014-07-09 10:58:08

标签: android android-activity android-service

  

我问过这个问题here但是它被标记为重复 -   但是我没有在评论中找到任何有用的解决方案。   在这里,我再次询问更多细节......

我正在HCE上进行示例应用(PoC),并根据Android用户指南使用HostApduService。我创建了两个应用程序 1)ReaderApp - 充当读卡器 2)HCEApp - 模拟卡

在HCEApp中,我创建了一个扩展HostApduService

的类'MyService'
public class MyService extends HostApduService {

private int messageCounter;
private final String TAG = "MyService";

Intent mIntent;

@Override
public void onCreate() {
    super.onCreate();
    Log.i(TAG, "onCreate");

    mIntent = new Intent(this, MyActivity.class);
    mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(mIntent);
}

/**
 * returned bytes will be sent as response. This method runs in Main thread
 * so return ASAP.
 */

@Override
public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
    if (selectAidApdu(apdu)) {
        Log.i(TAG, "Application selected");
        return getWelcomeMessage();
    } else {
        Log.i(TAG, "Received: " + new String(apdu));
        return getNextMessage();
    }
}

private byte[] getWelcomeMessage() {
    return "Hello Desktop!".getBytes();
}

private byte[] getNextMessage() {
    return ("Message from android: " + messageCounter++).getBytes();
}

private boolean selectAidApdu(byte[] apdu) {

    if (apdu != null) {
        for (byte b : apdu) {
            System.out.printf("0x%02X", b);
        }
    }

    return apdu.length >= 2 && apdu[0] == (byte) 0
            && apdu[1] == (byte) 0xa4;
}

@Override
public void onDeactivated(int reason) {
    Log.i(TAG, "Deactivated: " + reason);
}

@Override
public boolean onUnbind(Intent intent) {
    return super.onUnbind(intent);
}

}

正如您在 onCreate()中看到的那样,我正在启动 MyActivity ,用户可以输入一些信息,需要将其发送回 MyService

我认为我不能使用绑定,因为' onBind()'在HostApduService中声明为final,如下所示

@Override
public final IBinder onBind(Intent intent) {
    return mMessenger.getBinder();
}

如果我正确地理解它,请告诉我。感谢任何帮助。

感谢
iuq

2 个答案:

答案 0 :(得分:1)

你是否可以使用onBind我不知道,但我最近使用的是BroadcastReceiver,我必须从中启动一个服务。根据文档,您无法从BroadcastReceiver bind提供服务,您只能start它。稍后我需要从我的BroadcastReceiver向服务发送一些数据,由于我无法使用绑定器技术,我必须找到一种与服务通信的不同方式,就像你不喜欢的情况一样。 ; t引用它。

我做了一些研究,但找不到任何解决方案,但后来我记得你可以通过startService(intent)调用传递intent数据。我在onCreate中开始我的服务工作,因为onCreate仅在创建服务时调用一次。

在您的活动中

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

在您的服务中

@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;
}

这可能是某种黑客行为,因为" startService"听起来不应该用它来发送消息,我不确定这是否正是你所需要的,但它对我有用,所以我希望它适合你。干杯

编辑:BTW。我用它来告诉LocationService特定活动不再需要位置更新。

答案 1 :(得分:1)

我最终采取了不同的方法来解决同样的问题。当我绑定到HostApduService子类时,我抓住了Messenger HostApduService实现返回的onBind接口的句柄。

这是一些示例代码。这将全部放在您的活动实施中(在此处将其称为MyActivity,与MyHostApduServiceSubclass进行通信)。这是MyActivity需要包含的内容:

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
    }
}

然后你的HostApduService子类只需要向你的活动发送广播,指示收到了什么APDU命令。以下是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
}