Package org.nfctools.llcp.pdu

Source Code of org.nfctools.llcp.pdu.PduDecoder

/**
* Copyright 2011-2012 Adrian Stabiszewski, as@nfctools.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nfctools.llcp.pdu;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import org.nfctools.llcp.parameter.LinkTimeOut;
import org.nfctools.llcp.parameter.Miux;
import org.nfctools.llcp.parameter.Option;
import org.nfctools.llcp.parameter.ReceiveWindowSize;
import org.nfctools.llcp.parameter.ServiceName;
import org.nfctools.llcp.parameter.Version;
import org.nfctools.llcp.parameter.WellKnownServiceList;
import org.nfctools.utils.NfcUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PduDecoder {

  private Logger log = LoggerFactory.getLogger(getClass());

  public AbstractProtocolDataUnit decode(byte[] pduData) {

    int destination = (pduData[0] >> 2) & 0x3f;
    int source = pduData[1] & 0x3f;
    int pduType = ((pduData[0] & 0x03) << 2) | ((pduData[1] & 0xc0) >> 6);

    switch (pduType) {
      case PduConstants.PDU_CONNECT:
        return new Connect(destination, source, decodeParameter(pduData, 2));
      case PduConstants.PDU_CONNECT_COMPLETE:
        return new ConnectComplete(destination, source, decodeParameter(pduData, 2));
      case PduConstants.PDU_DISCONNECT:
        return new Disconnect(destination, source);
      case PduConstants.PDU_DISCONNECTED_MODE:
        int reason = pduData[2];
        return new DisconnectedMode(destination, source, reason);
      case PduConstants.PDU_INFORMATION:
        int received = pduData[2] & 0x0f;
        int send = (pduData[2] >>> 4) & 0x0f;
        byte[] informationData = new byte[pduData.length - 3];
        System.arraycopy(pduData, 3, informationData, 0, informationData.length);
        return new Information(destination, source, received, send, informationData);
      case PduConstants.PDU_PARAMETER_EXCHANGE:
        return new ParameterExchange(destination, source, decodeParameter(pduData, 2));
      case PduConstants.PDU_RECEIVE_READY:
        int receivedReady = pduData[2] & 0x0f;
        return new ReceiveReady(destination, source, receivedReady);
      case PduConstants.PDU_SYMMETRY:
        return new Symmetry();
      case PduConstants.PDU_UNNUMBERED_INFORMATION:
        byte[] unnumberedData = new byte[pduData.length - 2];
        System.arraycopy(pduData, 2, unnumberedData, 0, unnumberedData.length);
        return new UnnumberedInformation(destination, source, unnumberedData);
      case PduConstants.PDU_AGGREGATED_FRAME:
        int currIndex = 2;
        ArrayList<AbstractProtocolDataUnit> frames = new ArrayList<AbstractProtocolDataUnit>();       
        for(int i = currIndex; i+2 <pduData.length;)
        {
          short framelenght =  ByteBuffer.wrap(new byte[]{pduData[i],pduData[i+1]}).getShort();
          byte[] currFrame = new byte[framelenght];
          System.arraycopy(pduData, i+2, currFrame, 0, framelenght);         
          frames.add(this.decode(currFrame));
          i=i+2+framelenght;
        }       
        AbstractProtocolDataUnit[] resultantFrames = new AbstractProtocolDataUnit[frames.size()];
        for(int i = 0; i < frames.size();i++)
          resultantFrames[i] = frames.get(i);       
        return new AggregatedFrame(resultantFrames);
      case PduConstants.PDU_RECEIVE_NOT_READY:
      case PduConstants.PDU_FRAME_REJECT:
        throw new UnsupportedOperationException("PDU TYPE: " + pduType);
      default:
        throw new RuntimeException("unknown pdu type: " + pduType);
    }
  }

  public byte[] encode(AbstractProtocolDataUnit protocolDataUnit) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    if (protocolDataUnit instanceof ConnectComplete) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_CONNECT_COMPLETE);
      appendParameter(baos, (AbstractParameterProtocolDataUnit)protocolDataUnit);
    }
    else if (protocolDataUnit instanceof Connect) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_CONNECT);
      appendParameter(baos, (AbstractParameterProtocolDataUnit)protocolDataUnit);
    }
    else if (protocolDataUnit instanceof Disconnect) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_DISCONNECT);
    }
    else if (protocolDataUnit instanceof DisconnectedMode) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_DISCONNECTED_MODE);
      baos.write(((DisconnectedMode)protocolDataUnit).getReason());
    }
    else if (protocolDataUnit instanceof Information) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_INFORMATION);
      appendSequence(baos, (AbstractSequenceProtocolDataUnit)protocolDataUnit);
      appendData(baos, ((Information)protocolDataUnit).getServiceDataUnit());
    }
    else if (protocolDataUnit instanceof ParameterExchange) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_PARAMETER_EXCHANGE);
      appendParameter(baos, (AbstractParameterProtocolDataUnit)protocolDataUnit);
    }
    else if (protocolDataUnit instanceof ReceiveReady) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_RECEIVE_READY);
      appendSequence(baos, (AbstractSequenceProtocolDataUnit)protocolDataUnit);
    }
    else if (protocolDataUnit instanceof Symmetry) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_SYMMETRY);
    }
    else if (protocolDataUnit instanceof UnnumberedInformation) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_UNNUMBERED_INFORMATION);
      appendData(baos, ((UnnumberedInformation)protocolDataUnit).getServiceDataUnit());
    }
    else if (protocolDataUnit instanceof AggregatedFrame) {
      appendHeader(baos, protocolDataUnit, PduConstants.PDU_AGGREGATED_FRAME);
      for(int i = 0 ; i < ((AggregatedFrame)protocolDataUnit).getInnerFrames().length;i++)
      {
        byte[] currFrame = this.encode(((AggregatedFrame)protocolDataUnit).getInnerFrames()[i])
       
        byte[] currFrameLenght = ByteBuffer.allocate(2).putShort((short)currFrame.length).array();
       
        appendData(baos, currFrameLenght);
        appendData(baos, currFrame);
      }     
    }
    return baos.toByteArray();
  }

  private void appendData(ByteArrayOutputStream baos, byte[] data) {
    try {
      baos.write(data);
    }
    catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  private void appendSequence(ByteArrayOutputStream baos, AbstractSequenceProtocolDataUnit protocolDataUnit) {
    int sequence = (protocolDataUnit.getSend() & 0x0f) << 4 | (protocolDataUnit.getReceived() & 0x0f);
    baos.write(sequence);
  }

  private void appendParameter(ByteArrayOutputStream baos, AbstractParameterProtocolDataUnit protocolDataUnit) {
    appendData(baos, encodeParameter(protocolDataUnit.getParameter()));
  }

  protected void appendHeader(ByteArrayOutputStream baos, AbstractProtocolDataUnit protocolDataUnit, int pduType) {
    int b1 = ((protocolDataUnit.getDestinationServiceAccessPoint() & 0x3f) << 2) | (pduType >> 2);
    int b2 = ((pduType & 0x03) << 6) | (protocolDataUnit.getSourceServiceAccessPoint() & 0x3f);
    baos.write(b1);
    baos.write(b2);
  }

  public Object[] decodeParameter(byte[] pduData) {
    return decodeParameter(pduData, 0);
  }

  public Object[] decodeParameter(byte[] pduData, int offset) {
    List<Object> params = new ArrayList<Object>();
    while (offset < pduData.length) {
      switch (pduData[offset]) {
        case PduConstants.PARAM_VERSION:
          byte major = (byte)((pduData[offset + 2] >> 4) & 0x0F);
          byte minor = (byte)(pduData[offset + 2] & 0x0F);
          params.add(new Version(major, minor));
          break;
        case PduConstants.PARAM_SN:
          String serviceName = new String(pduData, offset + 2, pduData[offset + 1]);
          params.add(new ServiceName(serviceName));
          break;
        case PduConstants.PARAM_MIUX:
          int miux = (pduData[offset + 2] & 0x03) << 8 | (pduData[offset + 3] & 0xFF);
          params.add(new Miux(miux));
          break;
        case PduConstants.PARAM_WKS:
          int wks = (pduData[offset + 2] & 0xFF) << 8 | (pduData[offset + 3] & 0xFF);
          params.add(new WellKnownServiceList(wks));
          break;
        case PduConstants.PARAM_LTO:
          params.add(new LinkTimeOut(pduData[offset + 2] & 0xFF));
          break;
        case PduConstants.PARAM_RW:
          params.add(new ReceiveWindowSize(pduData[offset + 2] & 0x0F));
          break;
        case PduConstants.PARAM_OPT:
          params.add(new Option(pduData[offset + 2] & 0x01));
          break;

        default:
          throw new IllegalArgumentException("unknown code " + pduData[offset] + " at position " + offset
              + ". [" + NfcUtils.convertBinToASCII(pduData) + "]");
      }
      offset += 2 + pduData[offset + 1];
    }
    return params.toArray();
  }

  public byte[] encodeParameter(Object[] parameter) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    for (Object object : parameter) {
      if (object instanceof Version) {
        baos.write(PduConstants.PARAM_VERSION);
        baos.write(0x01);
        byte versionByte = createVersionByte((Version)object);
        baos.write(versionByte);
      }
      else if (object instanceof ServiceName) {
        byte[] serviceName = ((ServiceName)object).getName().getBytes();
        baos.write(PduConstants.PARAM_SN);
        baos.write(serviceName.length);
        appendData(baos, serviceName);
      }
      else if (object instanceof Miux) {
        Miux miux = (Miux)object;
        baos.write(PduConstants.PARAM_MIUX);
        baos.write(2);
        baos.write((miux.getValue() >> 8) & 0x03);
        baos.write(miux.getValue() & 0xff);
      }
    }

    return baos.toByteArray();
  }

  private byte createVersionByte(Version version) {
    if (version.getMajor() > 15 | version.getMajor() < 1 | version.getMinor() > 15 | version.getMinor() < 0)
      throw new IllegalArgumentException("Version out of range");
    byte versionByte = (byte)((version.getMajor() << 4) | version.getMinor());
    return versionByte;
  }
}
TOP

Related Classes of org.nfctools.llcp.pdu.PduDecoder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.