com.tapcentive.sdk.touchpoint.nfc.SEManager.java Source code

Java tutorial

Introduction

Here is the source code for com.tapcentive.sdk.touchpoint.nfc.SEManager.java

Source

/*
 * Copyright 2015 Tapcentive, Inc.
 *
 * 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 com.tapcentive.sdk.touchpoint.nfc;

import java.io.IOException;

import org.apache.http.util.ByteArrayBuffer;

import android.content.Context;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.SystemClock;
import android.util.Base64;
import android.util.Log;

/**
 * The Class SEManager.
 */
public class SEManager {

    /** The _instance. */
    private static SEManager _instance;

    /** The _tag. */
    private Tag _tag;

    /** The  nfc iso dep. */
    private IsoDep _NFC_ISO_DEP = null;

    /** The Constant TAG. */
    private static final String TAG = "SEManager";

    /** The _context. */
    private static Context _context;

    // SELECT command for the tapcentive application
    /** The Constant selectTapCentive. */
    static final byte[] selectTapCentive = new byte[] { (byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00,
            (byte) 0x0b, (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x05, (byte) 0x70, (byte) 0x01, (byte) 0x00,
            (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00 };

    // Header bytes for the INFORMATION command. Full command is constructed based on the profile and potentially breadcrumbs
    /** The information header. */
    static byte[] informationHeader = new byte[] { (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x00,
            (byte) 0x00 };

    // READ command for Status of a CTP
    /** The Constant readStatus. */
    static final byte[] readStatus = new byte[] { (byte) 0x80, (byte) 0xB0, (byte) 0x00, (byte) 0x00, (byte) 0x01,
            (byte) 0x62 };

    // READ command for all the descriptions
    /** The Constant readAllDescriptions. */
    static final byte[] readAllDescriptions = new byte[] { (byte) 0x80, (byte) 0xB0, (byte) 0x00, (byte) 0x00,
            (byte) 0x01, (byte) 0x6C };

    // GET RESPONSE command used to retrieve APDU response data if over 256 in length
    /** The Constant getResponse. */
    public static final byte[] getResponse = new byte[] { (byte) 0x80, (byte) 0xC0, (byte) 0x00, (byte) 0x00,
            (byte) 0x00 };

    /**
     * Gets the single instance of SEManager.
     *
     * @param context the context
     * @param tag the tag
     * @return single instance of SEManager
     * @throws IOException 
     */
    public static SEManager getInstance(Context context, Tag tag) throws IOException {
        _context = context;
        if (_instance == null) {
            _instance = new SEManager();
        }

        _instance.setTag(tag);

        return _instance;
    }

    /**
     * Sets the tag.
     *
     * @param tag the new tag
     */
    private void setTag(Tag tag) throws IOException {
        _tag = tag;
        _NFC_ISO_DEP = IsoDep.get(_tag);

        byte[] atr = _NFC_ISO_DEP.getHistoricalBytes();
        try {
            _NFC_ISO_DEP.connect();
        } catch (Exception e) {
            Log.d(TAG, "Connection with the tag failed" + e);
            throw new IOException("Connection to tag failed");
        }

        // Set the time that the phone will wait for the Touchpoint to respond to a single command to 1/2 second.
        _NFC_ISO_DEP.setTimeout(500);
    }

    /**
     * Force close.
     */
    public void forceClose() {
        try {
            _NFC_ISO_DEP.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * Select tapcentive.
     *
     * @return the select response
     */
    public SelectResponse selectTapcentive() {
        long time1 = SystemClock.uptimeMillis();
        byte[] responseBuffer = sendAndReceive(selectTapCentive);

        long time2 = SystemClock.uptimeMillis();
        Log.d(TAG, "TIME: selectTapcentive:sendAndReceive: " + (time2 - time1));
        SelectResponse selectResponse = new SelectResponse(responseBuffer);
        long time3 = SystemClock.uptimeMillis();
        Log.d(TAG, "TIME: selectTapcentive:new SelectResponse: " + (time3 - time2));
        return selectResponse;
    }

    /**
     * Do tapcentive interaction.
     *
     * @param storage the storage
     * @return the interaction response
     * @throws TapcentiveException the tapcentive exception
     */
    public InteractionResponse doTapcentiveInteraction(ProfileStorage storage) throws TapcentiveException {
        long time1 = SystemClock.uptimeMillis();

        @SuppressWarnings("unused")
        String prof = storage.getProfile();

        byte[] profile = Base64.decode(storage.getProfile(), Base64.DEFAULT);
        byte[] informationCommand = new byte[5 + profile.length];

        System.arraycopy(informationHeader, 0, informationCommand, 0, 5);
        System.arraycopy(profile, 0, informationCommand, 5, profile.length);

        informationCommand[4] = (byte) (profile.length);

        byte[] responseBuffer = sendAndReceive(informationCommand);

        long time2 = SystemClock.uptimeMillis();
        Log.d(TAG, "TIME: doTapcentiveIngeraction:sendAndReceive: " + (time2 - time1));

        InteractionResponse iResponse = new InteractionResponse(responseBuffer);
        long time3 = SystemClock.uptimeMillis();
        Log.d(TAG, "TIME: do TapcentiveInteraction:new InteractionResponse: " + (time3 - time2));

        return iResponse;
    }

    /**
     * Get the Status Block.
     *
     * @return the status response
     * @throws TapcentiveException the tapcentive exception
     */
    public StatusResponse doReadStatus() throws TapcentiveException {

        byte[] responseBuffer = sendAndReceive(readStatus);
        StatusResponse readResponse = new StatusResponse(responseBuffer);
        return readResponse;
    }

    /**
     * Fetch all descriptions.
     *
     * @return the byte[]
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws TapcentiveException the tap centive exception
     */
    public byte[] fetchAllDescriptions() throws IOException, TapcentiveException {
        //DescriptionResponse description;
        long time1 = SystemClock.uptimeMillis();
        byte[] response = sendAndReceive(readAllDescriptions);
        long time2 = SystemClock.uptimeMillis();
        Log.d(TAG, "TIME: fetchAllDescriptions:sendAndReceive=" + (time2 - time1));
        return response;
    }

    /**
     * Partial send and receive.
     *
     * @param cmd the cmd
     * @return the byte[]
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private byte[] partialSendAndReceive(byte[] cmd) throws IOException {

        byte[] ret = null;

        if (_NFC_ISO_DEP.isConnected()) {
            ret = _NFC_ISO_DEP.transceive(cmd);
        } else {
            Log.d(TAG, "Connection to touchpoint failed");
            throw new IOException("Connection to touchpoint failed");
        }

        return ret;
    }

    /**
     * Send and receive.
     *
     * @param cmd the cmd
     * @return the data that was read or null if an error occurred
     */
    public byte[] sendAndReceive(byte[] cmd) {
        byte[] result;
        byte[] fullBytes = null;
        ByteArrayBuffer fullresult = new ByteArrayBuffer(255);
        boolean hasMore = true;
        byte[] command = cmd;
        boolean gotSuccessSW = false;
        long time1 = SystemClock.uptimeMillis();
        while (hasMore) {
            try {
                result = partialSendAndReceive(command);

                fullresult.append(result, 0, result.length - 2);

                if (result.length < 2) {
                    Log.d(TAG, "READ command issue");
                    return null;
                }
                // Check if more data is available from touchpoint
                if (result[result.length - 2] == (byte) 0x61) {
                    hasMore = true;
                } else {
                    if (result[result.length - 2] != (byte) 0x90) {
                        Log.d(TAG,
                                "Bad SW on READ (command/response):"
                                        + ApduUtil.getHexString(command, 0, command.length, "") + " / "
                                        + ApduUtil.getHexString(result, 0, result.length, ""));
                        return null;
                        /*throw new TapcentiveException("An error occurred. Please try again and if this problem persists contact customer service");*/
                    } else { // SW 9000
                        hasMore = false;
                        gotSuccessSW = true;
                    }
                }
                command = getResponse; // After the first iteration change the command header to a GET RESPONSE
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
        long time2 = SystemClock.uptimeMillis();
        Log.d(TAG, "TIME: send&Receive: " + (time2 - time1));
        /*if (!gotSuccessSW){
           throw new TapcentiveException("An error occurred. Please try again and if this problem persists contact customer service");
        }*/

        if (gotSuccessSW) {
            fullBytes = fullresult.toByteArray();
            Log.d(TAG, "TIME: send&receive apdu size: " + fullBytes.length);
        }

        return fullBytes;
    }

    /**
     * Log bytes.
     *
     * @param prefix the prefix
     * @param message the message
     */
    public static void logBytes(String prefix, byte[] message) {
        if (null == message) {
            Log.d("Warning", prefix + " is null");
            return;
        }

        StringBuilder sb = new StringBuilder();
        for (byte b : message) {
            sb.append(String.format("%02x", b) + " ");
        }
        Log.d("SEManager", prefix + ": " + sb.toString());
    }
}