本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处!

最近NFC支付挺火的,趁国庆宅在家,学习下Android 卡模拟(Host-based Card Emulation)。HCE的特点是模拟智能IC卡(ISO 7816-4),可用于金融和行业应用,相应地,CardReader例子中使用IsoDep。

智能IC卡本身是一个微型计算机,常见为Java Card平台,特别是多功能集于一身的卡(如联名卡),Java Card比J2ME更加硬件受限。Java Card可以运行一到多个Java Applet,这些Applet也就是卡应用,例如一张能刷公交的银行卡可能就包含了2个Applet。每个Applet都有一个AID,受理终端(刷卡设备)通过AID来找到对应的卡应用(受理终端向卡发送SELECT命令),受理终端找到对应的卡应用后就可以进行数据交互,交互的数据一般是密文,不联机解密的话,用对称算法,联机解密的话,用非对称和对称算法都行。

HCE是软件模拟的智能IC卡,所以也会有AID。本文CardEmulation只注册一个AID。本文的代码改自Android 4.4 Sample,没改动CardEmulation,简化CardReader并支持Android 2.3系统,适应低版本的NFC手机做虚拟卡的卡读取了。使用努比亚Z7 MAX做CardEmulation,小米 2A作CardReader。本文的代码可以到http://pan.baidu.com/s/1o6JnXr0下载。个人觉得阅读CardReader核心代码更能了解2者交互的过程:

public final class CardReader {    private static final String TAG = "LoyaltyCardReader";    // AID for our loyalty card service.    private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222";    // ISO-DEP command HEADER for selecting an AID.    // Format: [Class | Instruction | Parameter 1 | Parameter 2]    private static final String SELECT_APDU_HEADER = "00A40400";    // "OK" status word sent in response to SELECT AID command (0x9000)    private static final byte[] SELECT_OK_SW = {(byte) 0x90, (byte) 0x00};public static String[][] TECHLISTS;public static IntentFilter[] FILTERS;static {try {//the tech lists used to perform matching for dispatching of the ACTION_TECH_DISCOVERED intentTECHLISTS = new String[][] { { IsoDep.class.getName() },{ NfcV.class.getName() }, { NfcF.class.getName() }, };FILTERS = new IntentFilter[] { new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED, "*/*") };} catch (Exception e) {}}    static public String tagDiscovered(Tag tag) {Log.i(TAG, "New tag discovered");// Android's Host-based Card Emulation (HCE) feature implements the// ISO-DEP (ISO 14443-4)// protocol.//// In order to communicate with a device using HCE, the discovered tag// should be processed// using the IsoDep class.IsoDep isoDep = IsoDep.get(tag);if (isoDep != null) {try {// Connect to the remote NFC deviceisoDep.connect();// Build SELECT AID command for our loyalty card service.// This command tells the remote device which service we wish to// communicate with.Log.i(TAG, "Requesting remote AID: " + SAMPLE_LOYALTY_CARD_AID);byte[] command = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID);// Send command to remote deviceLog.i(TAG, "Sending: " + ByteArrayToHexString(command));byte[] result = isoDep.transceive(command);// If AID is successfully selected, 0x9000 is returned as the// status word (last 2// bytes of the result) by convention. Everything before the// status word is// optional payload, which is used here to hold the account// number.int resultLength = result.length;byte[] statusWord = { result[resultLength - 2],result[resultLength - 1] };byte[] payload = Arrays.copyOf(result, resultLength - 2);if (Arrays.equals(SELECT_OK_SW, statusWord)) {// The remote NFC device will immediately respond with its// stored account numberString accountNumber = new String(payload, "UTF-8");Log.i(TAG, "Received: " + accountNumber);// Inform CardReaderFragment of received account numberreturn accountNumber;}} catch (IOException e) {Log.e(TAG, "Error communicating with card: " + e.toString());}}return null;}public static String load(Parcelable parcelable) {// 从Parcelable筛选出各类NFC标准数据final Tag tag = (Tag) parcelable;return tagDiscovered(tag);}    /**     * Build APDU for SELECT AID command. This command indicates which service a reader is     * interested in communicating with. See ISO 7816-4.     *     * @param aid Application ID (AID) to select     * @return APDU for SELECT AID command     */    public static byte[] BuildSelectApdu(String aid) {        // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]        return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", aid.length() / 2) + aid);    }    /**     * Utility class to convert a byte array to a hexadecimal string.     *     * @param bytes Bytes to convert     * @return String, containing hexadecimal representation.     */    public static String ByteArrayToHexString(byte[] bytes) {        final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};        char[] hexChars = new char[bytes.length * 2];        int v;        for ( int j = 0; j < bytes.length; j++ ) {            v = bytes[j] & 0xFF;            hexChars[j * 2] = hexArray[v >>> 4];            hexChars[j * 2 + 1] = hexArray[v & 0x0F];        }        return new String(hexChars);    }    /**     * Utility class to convert a hexadecimal string to a byte string.     *     * <p>Behavior with input strings containing non-hexadecimal characters is undefined.     *     * @param s String containing hexadecimal characters to convert     * @return Byte array generated from input     */    public static byte[] HexStringToByteArray(String s) {        int len = s.length();        byte[] data = new byte[len / 2];        for (int i = 0; i < len; i += 2) {            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)                    + Character.digit(s.charAt(i+1), 16));        }        return data;    }


单地说:

1。CardReader使用SAMPLE_LOYALTY_CARD_AID +SELECT_APDU_HEADER 生成 SELECT APDU;

2。CardReader发送SELECT APDU到CardEmulation后,CardEmulation返回SELECT_OK_SW + accountNumber。

其中SAMPLE_LOYALTY_CARD_AID ,SELECT_APDU_HEADER ,SELECT_OK_SW 都必须是CardReader与CardEmulation双方定义好的。


另外,CardReader在4.4和4.4以下系统的用法也略有不同,4.4以下系统用onNewIntent(),4.4系统就要通过enableReaderMode()方法,如果4.4系统用onNewIntent()的话,会导致一直P2P,而不是卡与读卡器的关系。

public final class NFCard extends Activity {private NfcAdapter nfcAdapter;private PendingIntent pendingIntent;private EditText board;private int sysVersion;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.cardreader);board = (EditText) this.findViewById(R.id.editText1);nfcAdapter = NfcAdapter.getDefaultAdapter(this);pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);//区分系统版本sysVersion = Integer.parseInt(VERSION.SDK);if(sysVersion<19)onNewIntent(getIntent());}@Overrideprotected void onPause() {super.onPause();if (nfcAdapter != null){nfcAdapter.disableForegroundDispatch(this);disableReaderMode();}}@Overrideprotected void onResume() {super.onResume();if (nfcAdapter != null){nfcAdapter.enableForegroundDispatch(this, pendingIntent,CardReader.FILTERS, CardReader.TECHLISTS);enableReaderMode();}Log.e("NFC----", IsoDep.class.getName());refreshStatus();}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);final Parcelable p = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);Log.d("NFCTAG", intent.getAction());board.setText((p != null) ? CardReader.load(p) : null);}    @TargetApi(19)     private void enableReaderMode() {    if(sysVersion<19)    return;            int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;        if (nfcAdapter != null) {        nfcAdapter.enableReaderMode(this, new MyReaderCallback(), READER_FLAGS, null);        }    }    @TargetApi(19)     private void disableReaderMode() {    if(sysVersion<19)    return;            if (nfcAdapter != null) {        nfcAdapter.disableReaderMode(this);        }    }    @TargetApi(19) public class MyReaderCallback implements NfcAdapter.ReaderCallback {@Overridepublic void onTagDiscovered(final Tag arg0) {NFCard.this.runOnUiThread(new Runnable() {            @Override            public void run() {                        board.setText(CardReader.tagDiscovered(arg0));            }        });}}    protected void onActivityResult(int requestCode, int resultCode, Intent data) {refreshStatus();}private void refreshStatus() {final String tip;if (nfcAdapter == null)tip = this.getResources().getString(R.string.tip_nfc_notfound);else if (nfcAdapter.isEnabled())tip = this.getResources().getString(R.string.tip_nfc_enabled);elsetip = this.getResources().getString(R.string.tip_nfc_disabled);final StringBuilder s = new StringBuilder(this.getResources().getString(R.string.app_name));s.append("  --  ").append(tip);setTitle(s);}


更多相关文章

  1. Android系统文件夹结构解析
  2. android系统自带的Service原理与使用
  3. 走进Android
  4. Android系统上实现应用程序的静默安装
  5. 搭建Android开发环境01——Java
  6. Android文件系统保护——dmverity
  7. Service与Android系统设计(3)
  8. 创建和使用Android(安卓)library工程
  9. Android(安卓)添加系统服务的方法

随机推荐

  1. android中Animation的使用!
  2. 画出 Android 的大体架构图
  3. Android APK 签名比对
  4. Android无障碍服务( Accessibility Servi
  5. OOM----内存溢出或内存泄漏
  6. Android设计缺陷--无法通话录音
  7. Android ListView工作原理完全解析,带你从
  8. 写在20110721:横竖屏切换
  9. Android屏幕刷新——源码分析
  10. Android DialogFragment宽度占满高度自适