Java tutorial
/* * 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()); } }