使用 HCE 将位图从一个设备传输到另一个设备 (Android 4.4+)

transfering bitmap from one device to another (Android 4.4+) using HCE

我正在尝试使用 HCE 将图像从一台设备发送到另一台设备(一台设备处于卡模拟模式,另一台设备处于 reader 模式)。我可以发送字符串但不能发送图像。在 reader 方面,我总是将 TAG 作为 null.

非常感谢您的帮助,在此先致谢。

HCE 端(标签side/sender):

   private final static byte[] SELECT_APP = new byte[] { (byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xd2, (byte)0x76, (byte)0x00, (byte)0x00, (byte)0x85, (byte)0x01, (byte)0x01, (byte)0x00, };
private final static byte[] SELECT_CC_FILE = new byte[] { (byte)0x00, (byte)0xa4, (byte)0x00, (byte)0x0c, (byte)0x02, (byte)0xe1, (byte)0x03, };
private final static byte[] SELECT_NDEF_FILE = new byte[] { (byte)0x00, (byte)0xa4, (byte)0x00, (byte)0x0c, (byte)0x02, (byte)0xe1, (byte)0x04, };

private final static byte[] SUCCESS_SW = new byte[] { (byte)0x90, (byte)0x00, };
private final static byte[] FAILURE_SW = new byte[] { (byte)0x6a, (byte)0x82, };

private final static byte[] CC_FILE = new byte[] {
        0x00, 0x0f, // CCLEN
        0x20, // Mapping Version
        0x00, 0x3b, // Maximum R-APDU data size
        0x00, 0x34, // Maximum C-APDU data size
        0x04, 0x06, // Tag & Length
        (byte)0xe1, 0x04, // NDEF File Identifier
        0x00, 0x32, // Maximum NDEF size
        0x00, // NDEF file read access granted
        (byte)0xff, // NDEF File write access denied
};

public void onCreate() {
    super.onCreate();

    mAppSelected = false;
    mCcSelected = false;
    mNdefSelected = false;

    Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.transferimage1);
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    mBitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream);
    byte[] byteArray = stream.toByteArray();
    NdefRecord picRecord = NdefRecord.createMime("image/jpeg", byteArray);
    NdefMessage ndefMessage  = new NdefMessage(new NdefRecord[] { picRecord });

    int nlen = ndefMessage.getByteArrayLength();  // <- this is 164906

    mNdefRecordFile = new byte[nlen + 2];

    mNdefRecordFile[0] = (byte)((nlen & 0xff00) / 256);
    mNdefRecordFile[1] = (byte)(nlen & 0xff);
    System.arraycopy(ndefMessage.toByteArray(), 0, mNdefRecordFile, 2, ndefMessage.getByteArrayLength());
}

@Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
    if (Arrays.equals(SELECT_APP, commandApdu)) {
        mAppSelected = true;
        mCcSelected = false;
        mNdefSelected = false;
        return SUCCESS_SW; 
    } else if (mAppSelected && Arrays.equals(SELECT_CC_FILE, commandApdu)) {
        mCcSelected = true;
        mNdefSelected = false;
        return SUCCESS_SW; 
    } else if (mAppSelected && Arrays.equals(SELECT_NDEF_FILE, commandApdu)) {
        mCcSelected = false;
        mNdefSelected = true;
        return SUCCESS_SW; 
    } else if (commandApdu[0] == (byte)0x00 && commandApdu[1] == (byte)0xb0) {
        int offset = (0x00ff & commandApdu[2]) * 256 + (0x00ff & commandApdu[3]);
        int le = 0x00ff & commandApdu[4];
        byte[] responseApdu = new byte[le + SUCCESS_SW.length];

        if (mCcSelected && offset == 0 && le == CC_FILE.length) {
            System.arraycopy(CC_FILE, offset, responseApdu, 0, le);
            System.arraycopy(SUCCESS_SW, 0, responseApdu, le, SUCCESS_SW.length);

            return responseApdu;
        } else if (mNdefSelected) {
            if (offset + le <= mNdefRecordFile.length) {
                System.arraycopy(mNdefRecordFile, offset, responseApdu, 0, le);
                System.arraycopy(SUCCESS_SW, 0, responseApdu, le, SUCCESS_SW.length);

                return responseApdu;
            }
        }
    }

    return FAILURE_SW;
}

@Override
public void onDeactivated(int reason) {
    mAppSelected = false;
    mCcSelected = false;
    mNdefSelected = false;
}

Reader 模式应用 (reader side/receiver):

@Override
public void onTagDiscovered(Tag tag) {
    Ndef ndef = Ndef.get(tag);  // <- this returns null
    NdefMessage message;

    if (ndef != null) {
        message = ndef.getCachedNdefMessage();  // <- cannot get NDEF message as ndef is null
    } else {
        return;
    }
   }

您在实施模拟 Type 4 标签时遇到了一些问题:

  1. 能力容器将 NDEF 文件的最大大小定义为 50 字节 (0x00, 0x32)。但是,您在 NDEF 文件中使用的实际长度值(前两个字节)要大得多:

    int nlen = ndefMessage.getByteArrayLength();         // -> 164906
    mNdefRecordFile[0] = (byte)((nlen & 0xff00) / 256);  // -> 0x84
    mNdefRecordFile[1] = (byte)(nlen & 0xff);            // -> 0x2A
    

    注意实际长度被截断为0x842A(见下)!

  2. 您使用的 NDEF 消息大于映射版本 2.0 的 Type 4 标签的最大可能 NDEF 大小。由于 READ BINARY 命令的格式,可在类型 4 标签(映射版本 2.0)上寻址的 NDEF 消息的最大大小为 33022 字节(0x80FE)。因此,此标记类型不支持您的 NDEF 消息。

  3. 您应该期望 reader 仅读取功能容器文件的部分内容(与您对 NDEF 文件所做的方式类似)。目前,您只允许偏移量为 0 且长度为 0x0F 的读取操作。

  4. 如果 READ BINARY 命令失败,
  5. Return 正确的状态字(当前您 return "file not found")。

  6. 即使没有 Le 字段,您也可能想要接受 SELECT(通过 AID)命令。