/**
   * Reads data from the current selected file (INS 0xB0)
   *
   * @param offset Offset at which to start reading
   * @param nbBytes Number of bytes to read
   * @return Data retreived from the file
   */
  public byte[] readBinary(int offset, int nbBytes) throws SecureElementException {
    if (mFileSize == 0) return null;
    if (nbBytes == -1) nbBytes = mFileSize;
    if (mFileType != EF) throw new SecureElementException("Incorrect file type");
    if (mFileStructure != TRANSPARENT) throw new SecureElementException("Incorrect file structure");

    int length, pos = 0;
    byte[] result = new byte[nbBytes];
    byte[] cmd = {0x00, (byte) 0xB0, 0x00, 0x00, 0x00};

    while (nbBytes != 0) {
      if (nbBytes < BUFFER_LEN) length = nbBytes;
      else length = BUFFER_LEN; // Set to max buffer size

      Log.d(TAG, "ReadBinary [" + offset + ".." + length + "b]");

      cmd[2] = (byte) (offset >> 8);
      cmd[3] = (byte) offset;
      cmd[4] = (byte) length;
      System.arraycopy(mSEHandle.exchangeAPDU(this, cmd), 0, result, pos, length);
      nbBytes -= length;
      offset += length;
      pos += length;
    }
    return result;
  }
  /**
   * Reads a record from the current selected file (INS 0xB2)
   *
   * @param record Record ID [0..n]
   * @return Data from requested record
   */
  public byte[] readRecord(short record) throws SecureElementException {
    // Check the type of current selected file
    if (mFileType != EF) throw new SecureElementException("Incorrect file type");
    if (mFileStructure != LINEAR_FIXED)
      throw new SecureElementException("Incorrect file structure");

    // Check if requested record is valid
    if ((record < 0) || (record > mFileNbRecords))
      throw new SecureElementException("Incorrect record number");

    Log.d(TAG, "ReadRecord [" + record + "/" + mFileRecordSize + "b]");
    byte[] cmd = {0x00, (byte) 0xB2, (byte) record, 0x04, (byte) mFileRecordSize};

    return Arrays.copyOf(mSEHandle.exchangeAPDU(this, cmd), mFileRecordSize);
  }
  /**
   * Selects a file (INS 0xA4)
   *
   * @param path Path of the file
   * @return Command status code [sw1 sw2]
   */
  public int selectFile(byte[] path) throws SecureElementException {
    if ((path == null) || (path.length == 0) || ((path.length % 2) != 0))
      throw new SecureElementException("Incorrect path");

    int index;
    int length = path.length;
    if ((mSEHandle.getSeInterface() == SecureElement.SIM_IO) && (length > 2)) {
      index = length - 2; // Only FileID is usefull
      mFilePath = Util.bytesToString(path, 0, index, "");
    } else {
      index = 0;
      mFilePath = "";
    }

    byte[] data = null;
    byte[] cmd = new byte[] {0x00, (byte) 0xA4, 0x00, 0x04, 0x02, 0x00, 0x00};

    mFileType = UNKNOWN;
    mFileStructure = UNKNOWN;
    mFileSize = 0;
    mFileRecordSize = 0;
    mFileNbRecords = 0;

    // iterate through path
    for (int sw1; index < length; index += 2) {
      mFileID = ((path[index] & 0xFF) << 8) | (path[index + 1] & 0xFF);
      cmd[5] = (byte) (mFileID >> 8);
      cmd[6] = (byte) mFileID;

      data = mSEHandle.exchangeAPDU(this, cmd);

      // Check ADPU status
      sw1 = data[data.length - 2] & 0xFF;
      if ((sw1 != 0x62) && (sw1 != 0x63) && (sw1 != 0x90) && (sw1 != 0x91)) {
        return (sw1 << 8) | (data[data.length - 1] & 0xFF);
      }
    }

    // Analyse file properties
    decodeFileProperties(data);

    if (mFileNbRecords == 0) Log.d(TAG, "SelectFile [" + mFileSize + "b]");
    else Log.d(TAG, "SelectFile [" + mFileNbRecords + "*" + mFileRecordSize + "b]");
    return APDU_SUCCESS;
  }