【问题标题】:How to communicate with HostApduService from an Activity如何从 Activity 与 HostApduService 通信
【发布时间】:2014-07-09 10:58:08
【问题描述】:

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

我正在 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

【问题讨论】:

  • 我添加了指向 HostApduService 的链接。你能解释一下(链接到)什么是 PoC、pos 和 HCE?
  • @k3b 我添加了指向 HCE 的链接,并将 PoC 替换为示例应用程序,并将 POS 替换为读卡器。希望在上下文中更容易理解!
  • 那么...您的问题是什么?你可以使用onBind,即使它被声明为final。
  • @Manu 感谢您的回复。您能否发布一个示例/示例?我的目标是通过“MyService”启动的“MyActivity”与“MyService”进行通信。

标签: android android-activity android-service


【解决方案1】:

你是否可以使用 onBind 我不知道,但我最近使用了一个广播接收器,我必须从它启动一个服务。根据文档,您不能 bind 来自 BroadcastReceiver 的服务,您只能 start 它。我需要稍后从我的 BroadcastReceiver 向服务发送一些数据,并且由于我无法使用绑定技术,我必须找到一种不同的方式与服务通信,就像你没有的情况一样可以参考一下。

我做了一些研究,但找不到任何解决方案,但后来我记得您可以使用 startService(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”听起来不应该用于发送消息,并且不确定这是否正是您需要的,但它对我有用,所以我希望它为你工作。干杯

编辑:顺便说一句。我用它来告诉 LocationService 某个特定活动不再需要位置更新。

【讨论】:

  • 在我的情况下,当手机(有我的应用程序)靠近“NFC 阅读器”时,Android 操作系统会启动服务。 NFC 阅读器是在另一个 Android Kitkat 设备上运行的另一个应用程序。服务反过来启动一个活动,让用户输入一些需要发送回服务的数据。这就是问题所在 - 我无法弄清楚这个活动到服务的通信。 ...谢谢。
  • 正如我刚刚描述的那样,活动到服务的通信可以完成。从您的活动中调用 startService,意图保存您的数据。
  • 我接受您的帖子作为答案,因为我找不到任何其他方法来解决问题。虽然我认为必须有一些更好的方法!谢谢。
  • 是的,我知道你的意思。我对这个解决方案也不是很满意,但它确实有效。
  • @iuq 你终于可以和 Apdu 服务通信了吗?我和你有同样的问题。
【解决方案2】:

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

这里有一些示例代码。这都将在您的活动实施中进行(此处称为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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-11
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 1970-01-01
    相关资源
    最近更新 更多