com.nxp.ltsm.ltsmclient.LTSMCommunicator.java Source code

Java tutorial

Introduction

Here is the source code for com.nxp.ltsm.ltsmclient.LTSMCommunicator.java

Source

/*
 * Copyright (C) 2014 NXP Semiconductors
 *
 * 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.nxp.ltsm.ltsmclient;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.codec.binary.Hex;

import android.R.bool;
import android.R.integer;
import android.R.string;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;

import com.nxp.eseclient.LtsmService;

import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.InflateException;

import com.google.gson.Gson;
import com.nxp.ltsm.ltsmclient.tools.Data;
import com.nxp.ltsm.ltsmclient.tools.Utils;
import com.nxp.ltsm.ltsmclient.tools.VCDescriptionFile;
import com.nxp.ltsm.ltsmclient.tools.TLV;

import java.util.Arrays;

public class LTSMCommunicator extends ILTSMClient.Stub {

    private static final String LOG_TAG = "LTSMCommunicator";
    private static Context context;
    private static String CurrentPackage;
    private static Binder mBinder = new Binder();
    private static Bundle bundle;
    static int[] channelId = new int[10];
    static int[] isopened = new int[10];
    static int channelCnt = 0;
    private static LtsmService mLtsmService = null;
    private static String currentProcess = "";
    static ltsmRegistry Registry;
    static int InitialVCEntry = 0;
    static int currentVcEntry = 0;
    static int DeleteVCEntry = 99;
    static short statusCode = 0;
    static byte[] vcUid = new byte[] {};
    private static boolean previousConcurrentActivationMode = true;

    public LTSMCommunicator(Context context) {
        LTSMCommunicator.context = context;

    }

    /*******************************************************
     *
     *  Responsible for creating the Virtual Card
     *  The second argument-Personalise Data is optional and it is not mandatory to pass.
     *  If VC Creation configuration is set to 1, then Personalise Data shall be also provided.
     *
     *@param    vcData
     *@param    Personalise Data
     *
     *@return   If success then the VC entry number and VCUID are returned
     *          to the SP Application along with status word. Otherwise an error code.
     *
     *******************************************************/

    @Override
    public synchronized byte[] createVirtualCard(byte[] vcData, byte[] personalizeData) throws RemoteException {
        String TAG = "LTSMCommunicator:CreateVirtualCard";
        Log.i(TAG, "Enter");
        statusCode = 0;
        byte[] rData = new byte[0];
        byte cData[] = new byte[0];
        currentProcess = "CreateVirtualCard";
        String currentWalletPkg = "";
        int status = Data.SW_REFERENCE_DATA_NOT_FOUND, rStatus;
        TLV tlvResult = null;
        TLV tlvResultF8 = null;
        TLV tlvResultE2 = null;
        boolean personalize = false;
        boolean saveRegistry = true;
        boolean addedintoRegistry = false;
        short vcEntry = 0;
        Log.i(TAG, "vcData : " + Utils.bytArrayToHex(vcData));

        try {
            createVcProcedure: {
                currentWalletPkg = Utils.getCallingAppPkg(context);
                boolean available = Utils.checkRegistryAvail(context);
                if (!available) {
                    available = Utils.checkRegBackupAvail();
                    if (available) {
                        Utils.importReg();
                    } else {
                        Log.i(TAG, "no backup available");
                        Registry = new ltsmRegistry();
                    }
                }

                // Initiate the LTSM. Open Logical Channel and Select LTSM
                rStatus = ltsmStart();
                if (rStatus != Data.SW_NO_ERROR) {
                    Log.i(TAG, "LTSMStart Failed");
                    status = rStatus;
                    break createVcProcedure;
                }

                if (available) {
                    //If Registry is already available in SharedPreference Check for Uninstalled Wallet Apps
                    rStatus = checkUninstalledApps();
                    if (rStatus == Data.SW_NO_ERROR) {
                        Log.i(TAG, "Uninstalled App Found");
                    }
                }

                /*get Virtual Card entry number*/
                cData = VCDescriptionFile.GetVC();
                byte[] rapdu = exchangeLtsmProcessCommand(cData);
                Log.i(TAG, "cData : " + Utils.bytArrayToHex(cData));

                List<TLV> listRapdu = TLV.parse(Utils.getRDATA(rapdu));
                tlvResult = TLV.find(listRapdu, 0x44); /*Tag contains VCEntry No. which is free */
                if (Utils.byteArrayToShort(tlvResult.getValue()) != Data.SW_NO_ERROR) {
                    Log.i(TAG, "VCEntry Error : " + Utils.bytArrayToHex(tlvResult.getValue()));
                    status = Utils.byteArrayToInt(tlvResult.getValue());
                    break createVcProcedure;
                }

                tlvResult = TLV.find(listRapdu, 0x40);

                //check duplicate Registry Entry
                for (int i = 0; i < Registry.walletName.size(); i++) {
                    if (Registry.walletName.get(i).toString().equals(currentWalletPkg)) {
                        //duplicate Registry Entry
                        saveRegistry = false;
                        break;
                    }
                }
                if (saveRegistry == true) {
                    Registry.walletName.add(currentWalletPkg);
                    Registry.walletName_hash.add(Utils.createSha(currentWalletPkg));
                    saveRegistry(Registry);
                    addedintoRegistry = true;
                }

                vcEntry = Utils.byteArrayToShort(tlvResult.getValue());
                if (personalizeData != null) {
                    rStatus = handlePersonalize(personalizeData, currentWalletPkg, vcEntry);

                    if (rStatus != Data.SW_NO_ERROR) {
                        Log.i(TAG, "Error Personalization : " + Utils.createStatusCode(statusCode));
                        status = rStatus;
                        break createVcProcedure;
                    }
                }
                byte[] spData = null;
                byte[] mfdfaid = null;
                byte[] cltecap = null;
                //check for that f8 tag
                List<TLV> tag = TLV.parse(vcData, new int[] { 0xA8, 0xE2, 0xF8 });
                tlvResultF8 = TLV.find(tag, Data.TAG_MF_DF_AID);
                tlvResultE2 = TLV.find(tag, Data.TAG_MF_AID);
                // Strip E2/F8 tag (when present)
                vcData = removeTags(vcData, 0xE2, 0xF8);
                if (tlvResultE2 != null || tlvResultF8 != null) {
                    if (tlvResultF8 != null) {
                        mfdfaid = tlvResultF8.getValue();
                    }
                    if (tlvResultE2 != null) {
                        cltecap = tlvResultE2.getValue();
                    }
                    Log.i(TAG, "E2/F8 Present : ");
                    cData = VCDescriptionFile.createF8Vc(Utils.createSha(currentWalletPkg), vcEntry, mfdfaid,
                            cltecap);
                    rData = exchangeLtsmProcessCommand(cData);
                    status = Utils.getSW(rData);
                    if (status != Data.SW_NO_ERROR) {
                        rData = Utils.createStatusCode((short) status);
                        return rData;
                    }
                }
                //currentProcess used during Process Response if CreateVC is Success
                currentProcess = "CreateVirtualCard";

                cData = VCDescriptionFile.createVc(vcData, //vcData
                        Utils.createSha(currentWalletPkg), //Sha of the SP Application
                        vcEntry); //VC Entry

                Log.i(TAG, "cData : " + Utils.bytArrayToHex(cData));
                rData = exchangeLtsmProcessCommand(cData);

                status = Utils.getSW(rData);
            }
            /*createVcProcedure Ends*/

            if (status == Data.SW_NO_ERROR) {
                processCreateVcResponse(rData);
                rData = VCDescriptionFile.CreateVCResp(vcEntry, vcUid);
            } else {
                rData = Utils.createStatusCode((short) status);
            }

            Log.i(TAG, "CreateVCResp : " + Utils.bytArrayToHex(rData));
            Log.i(TAG, "Exit");
            return rData;
        } catch (Exception e) {
            e.printStackTrace();
            return Utils.createStatusCode(Data.SW_EXCEPTION);
        } finally {
            //Delete VC with Create Status False in the Registry.
            if (status != Data.SW_NO_ERROR && addedintoRegistry == true) {
                Registry.walletName.remove(Registry.walletName.size() - 1);
                Registry.walletName_hash.remove(Registry.walletName_hash.size() - 1);
                saveRegistry(Registry);
            }
            Utils.exportReg();
            closeLogicalChannel();
        }

    }

    /*******************************************************
     *
     *  Responsible for deleting the Virtual Card
     *
     *@param    VC entry number to be deleted
     *
     *@return   Status word informing success or failure
     *
     ********************************************************/

    @Override
    public synchronized byte[] deleteVirtualCard(int vcEntry) throws RemoteException {
        String TAG = "LTSMCommunicator:deleteVirtualCard";
        Log.i(TAG, "Enter");
        int status, rStatus;
        String currentWalletPkg;
        String currentWalletPkg_hash;
        statusCode = 0;
        byte[] rData = new byte[0];
        boolean match_found = false;
        try {
            deleteVcProcedure: {
                /* On runtime if we try to get calling app pkg name,
                 * sometime it is possible that some other app may come to foreground which will give wrong data
                 * So get calling app pkg name as soon as it enters the method
                 * */
                currentWalletPkg = Utils.getCallingAppPkg(context);
                currentWalletPkg_hash = Utils.createSha(currentWalletPkg);

                boolean available = Utils.checkRegistryAvail(context);
                if (!available) {
                    available = Utils.checkRegBackupAvail();
                    if (available) {
                        Utils.importReg();
                    } else {
                        Log.i(TAG, "no backup available");
                        status = Data.SW_REGISTRY_IS_EMPTY;
                        break deleteVcProcedure;
                    }
                }
                Registry = loadRegistry();

                currentProcess = "deleteVirtualCard";

                rStatus = ltsmStart();
                if (rStatus != Data.SW_NO_ERROR) {
                    Log.i(TAG, "LTSMStart Failed");
                    status = rStatus;
                    break deleteVcProcedure;
                }

                byte[] cData = VCDescriptionFile.deleteVc(Utils.createSha(currentWalletPkg), (short) vcEntry);

                rData = exchangeLtsmProcessCommand(cData);
                status = Utils.getSW(rData);

                /*If status is success then remove the entry from registry*/
                if (status == Data.SW_NO_ERROR) {
                    byte[] getStatusRsp = getStatus(null);
                    List<TLV> getStatusRspTlvs = TLV.parse(Utils.getRDATA(getStatusRsp));
                    if (getStatusRspTlvs != null) {
                        for (TLV tlv : getStatusRspTlvs) {
                            TLV tlvVCx = TLV.find(tlv.getNodes(), Data.TAG_VC_ENTRY);
                            TLV tlvApkId = TLV.find(tlv.getNodes(), Data.TAG_SP_AID);
                            byte ApkId[] = tlvApkId.getValue();
                            byte regApkId[] = Hex.decodeHex(currentWalletPkg_hash.toCharArray());
                            Log.i(TAG, "SEApkId : " + Utils.bytArrayToHex(ApkId));
                            Log.i(TAG, "regApkId : " + Utils.bytArrayToHex(regApkId));
                            if (Arrays.equals(ApkId, regApkId)) {
                                match_found = true;
                                break;
                            }
                        }
                    }
                    if (!match_found) {
                        for (int i = 0; i < Registry.walletName.size(); i++) {
                            if (Registry.walletName.get(i).toString().equals(currentWalletPkg)) {
                                Registry.walletName.remove(i);
                                Registry.walletName_hash.remove(i);
                                saveRegistry(Registry);
                                break;
                            }
                        }
                    }
                }
                status = Utils.getSW(rData);
            }
            /*deleteVcProcedure Ends*/

            Log.i(TAG, "deleteVirtualCard : " + Utils.bytArrayToHex(Utils.createStatusCode((short) status)));
            Log.i(TAG, "Exit");

            return Utils.createStatusCode((short) status);
        } catch (Exception e) {
            e.printStackTrace();
            return Utils.createStatusCode(Data.SW_EXCEPTION);
        } finally {
            closeLogicalChannel();
            Utils.exportReg();
        }
    }

    /*******************************************************
     *
     *  Responsible to retrieve the activation state of a VC
     *
     *@param    VC entry number
     *
     *@return   Status word informing success or failure
     *
     ********************************************************/

    @Override
    public synchronized byte[] getVirtualCardStatus(int vcEntry) throws RemoteException {
        String TAG = "LTSMCommunicator:getVirtualCardStatus";
        Log.i(TAG, "Enter");
        byte[] rData = new byte[0];
        byte[] rcvData = new byte[] {};
        byte[] activationState = new byte[] {};
        int status, rStatus;
        String currentWalletPkg = "";
        statusCode = 0;
        try {
            getVcStatusProcedure: {
                currentWalletPkg = Utils.getCallingAppPkg(context);

                Log.i(TAG, "vcEntry : " + vcEntry);
                Log.i(TAG, "pkg : " + currentWalletPkg);

                /*Selecting LTSM Application
                 * */
                rStatus = ltsmStart();
                if (rStatus != Data.SW_NO_ERROR) {
                    Log.i(TAG, "LTSMStart Failed");
                    status = rStatus;
                    break getVcStatusProcedure;
                }

                /*If VC detail doesn't available status will not be success
                 * */
                byte[] getStatusRsp = getStatus(TLV.make(Data.TAG_VC_ENTRY, Utils.shortToByteArr((short) vcEntry)));
                if (Utils.getSW(getStatusRsp) != Data.SW_NO_ERROR) {
                    status = Data.SW_REFERENCE_DATA_NOT_FOUND;
                    break getVcStatusProcedure;
                }

                List<TLV> getStatusRspTlvs = TLV.parse(Utils.getRDATA(getStatusRsp)).get(0).getNodes();
                TLV tlvResult = TLV.find(getStatusRspTlvs, Data.TAG_SP_AID);

                if (tlvResult != null) {
                    byte[] spAid = tlvResult.getValue();
                    if (!Arrays.equals(Utils.getApkHash(currentWalletPkg), spAid)) {
                        status = Data.SW_CONDITION_OF_USE_NOT_SATISFIED;
                        break getVcStatusProcedure;
                    }
                } else {
                    status = Data.SW_NO_DATA_RECEIVED;
                    break getVcStatusProcedure;
                }

                //Step6:Select CRS.If it is not selected return the CRS Select status failed
                rStatus = crsSelect();
                if (rStatus != Data.SW_NO_ERROR) {
                    Log.i(TAG, "crsSelect Failed");
                    status = Data.SW_CRS_SELECT_FAILED;
                    break getVcStatusProcedure;
                }
                //Step7:LTSM Client sends a GET STATUS to retrieve the state of VC Manager
                byte[] vcmAid = Utils.append(Data.VC_MANAGER_AID, Utils.shortToByteArr((short) vcEntry));
                byte[] capdu = Utils.makeCAPDU(0x80, 0xF2, 0x40, 0x00,
                        Utils.append(TLV.make(0x4F, vcmAid), TLV.make(0x5C, Utils.parseHexString("9F70"))));

                rcvData = exchangeWithSe(capdu);

                if (Utils.getSW(rcvData) != Data.SW_NO_ERROR) {
                    status = Utils.getSW(rcvData);
                    break getVcStatusProcedure;
                }

                getStatusRspTlvs = TLV.parse(Utils.getRDATA(rcvData)).get(0).getNodes();
                tlvResult = TLV.find(getStatusRspTlvs, Data.TAG_VC_STATE);

                if (tlvResult != null) {
                    activationState = tlvResult.getValue();
                    status = Data.SW_NO_ERROR;
                } else {
                    status = Data.SW_REFERENCE_DATA_NOT_FOUND;
                }
            }
            /*
             * If success then return the activation status + status
             * else send the failed SW
             * */
            if (status == Data.SW_NO_ERROR) {
                rData = Utils.append(TLV.make(Data.TAG_VC_STATE, new byte[] { (byte) activationState[1] }),
                        Utils.createStatusCode(Data.SW_NO_ERROR));
                Log.i(TAG, "getVirtualCardStatus RDATA: " + Utils.bytArrayToHex(rData));

            } else {
                rData = Utils.createStatusCode((short) status);
            }

            return rData;
        } catch (Exception e) {
            e.printStackTrace();
            return Utils.createStatusCode(Data.SW_EXCEPTION);
        } finally {
            closeLogicalChannel();
        }
    }

    /*******************************************************
     *
     *  Activate/Deactivate Virtual Card.
     *
     *@param    vcEntry
     *@param    Activation mode - true/false for activate/deactivate the VC
     *
     *@return   Status word informing Success or Failure
     *
     ********************************************************/

    @Override
    public synchronized byte[] activateVirtualCard(int vcEntry, boolean mode, boolean concurrentActivationMode)
            throws RemoteException {
        String TAG = "LTSMCommunicator:ActivateVirtualCard";
        Log.i(TAG, "Enter");
        byte[] rData = new byte[0];
        short status = Data.SW_REFERENCE_DATA_NOT_FOUND;

        try {

            activationProcedure: {

                int stat = Data.SW_UNEXPECTED_BEHAVIOR;
                TLV tlvResult;
                byte[] retStat = new byte[4];
                byte[] deactivatedvcEntry = new byte[2], Ret_Data = new byte[4];
                byte[] rcvData = new byte[] {};
                byte[] getStatusRsp = new byte[] {};
                byte[] setStatusRsp = new byte[] {};

                byte[] vcInfo = new byte[] {};
                byte[] vcmAid = new byte[] {};
                byte[] smAid = new byte[] {};
                byte[] vcState = new byte[] {};
                List<TLV> getStatusRspTlvs;

                String pkg = Utils.getCallingAppPkg(context);
                Log.i(TAG, "vcEntry : " + vcEntry);
                Log.i(TAG, "pkg : " + pkg);

                /*Selecting LTSM Application
                 * */
                stat = ltsmStart();
                if (stat != Data.SW_NO_ERROR) {
                    Log.i(TAG, "LTSMStart Failed");
                    closeLogicalChannel();
                    status = (short) stat;
                    break activationProcedure;
                }

                /*If VC detail doesn't available status will not be success
                 * */
                getStatusRsp = getStatus(TLV.make(Data.TAG_VC_ENTRY, Utils.shortToByteArr((short) vcEntry)));
                if (Utils.getSW(getStatusRsp) != Data.SW_NO_ERROR) {
                    status = Data.CONDITION_OF_USE_NOT_SATISFIED;
                    break activationProcedure;
                }

                getStatusRspTlvs = TLV.parse(Utils.getRDATA(getStatusRsp)).get(0).getNodes();
                tlvResult = TLV.find(getStatusRspTlvs, Data.TAG_SP_AID);

                if (tlvResult != null) {
                    byte[] spAid = tlvResult.getValue();
                    Log.i(TAG, "spAid : " + Utils.bytArrayToHex(spAid));
                    Log.i(TAG, "appid : " + Utils.bytArrayToHex(Utils.getApkHash(pkg)));
                    if (!Arrays.equals(Utils.getApkHash(pkg), spAid)) {
                        status = Data.CONDITION_OF_USE_NOT_SATISFIED;
                        break activationProcedure;
                    }
                } else {
                    //TODO confirm with else
                }

                /*Check VC state*/
                tlvResult = TLV.find(getStatusRspTlvs, Data.TAG_VC_STATE);

                if (tlvResult != null) {
                    vcState = tlvResult.getValue();

                    /* possible values for vcState,
                     *  x x x x 0 0 1 0 - VC_CREATED
                     *  x x x x 0 0 0 1 - VC_IN_PROGRESS
                     *  x x x x 0 1 1 0 - VC_DEAD
                     * */

                    if ((vcState[0] & 0x02) != 0x02) {
                        status = Data.SW_INVALID_VC_ENTRY;
                        break activationProcedure;
                    } else {
                        tlvResult = TLV.find(getStatusRspTlvs, Data.TAG_SM_AID);
                        if (tlvResult != null) {
                            smAid = tlvResult.getValue();
                        } else {
                            smAid = Utils.append(Data.SERVICE_MANAGER_AID, Utils.shortToByteArr((short) vcEntry));
                        }

                        vcmAid = Utils.append(Data.VC_MANAGER_AID, Utils.shortToByteArr((short) vcEntry));
                    }
                } else {
                    //TODO confirm with else
                }

                //Select CRS Prior to Activate
                stat = crsSelect();
                if (stat != Data.SW_NO_ERROR) {
                    Log.i(TAG, "crsSelect Failed");
                    status = Data.SW_CRS_SELECT_FAILED;
                    break activationProcedure;
                }

                //If current request is Activation
                if (mode == true) {
                    //Checking VCM can be activate
                    // 4.a Issue GET STATUS (for the VCM)
                    byte[] capdu = Utils.makeCAPDU(0x80, 0xF2, 0x40, 0x00,
                            Utils.append(TLV.make(0x4F, vcmAid), TLV.make(0x5C, Utils.parseHexString("9F70"))));

                    getStatusRsp = exchangeWithSe(capdu);
                    if (Utils.getSW(getStatusRsp) != Data.SW_NO_ERROR) {
                        status = Data.SW_REFERENCE_DATA_NOT_FOUND;
                        break activationProcedure;
                    }

                    getStatusRspTlvs = TLV.parse(Utils.getRDATA(getStatusRsp)).get(0).getNodes();
                    tlvResult = TLV.find(getStatusRspTlvs, Data.TAG_VC_STATE);

                    if (tlvResult != null) {
                        vcState = tlvResult.getValue();
                        if (vcState.length == 0x02) {

                            if (((vcState[0] & 0x80) == 0x80) //check if VCM_LOCKED
                                    || ((vcState[1] & 0x80) == 0x80)) // Check if VCM_NON_ACTIVABLE
                            {
                                status = Data.SW_CONDITION_OF_USE_NOT_SATISFIED;
                                break activationProcedure;
                            }

                            if (((vcState[0] & 0x80) != 0x80) && (vcState[1] == 0x01)) //Check if Already Activated
                            {
                                Log.i(TAG, "Already Activated");
                                status = Data.SW_NO_ERROR;
                                break activationProcedure;
                            }
                        } else {
                            status = Data.CONDITION_OF_USE_NOT_SATISFIED;
                            break activationProcedure;
                        }
                    } else {
                        status = Data.SW_UNEXPECTED_BEHAVIOR;
                        break activationProcedure;
                    }

                    //Checking SM can be activate
                    capdu = Utils.makeCAPDU(0x80, 0xF2, 0x40, 0x00,
                            Utils.append(TLV.make(0x4F, smAid), TLV.make(0x5C, Utils.parseHexString("9F70"))));

                    getStatusRsp = exchangeWithSe(capdu);
                    if (Utils.getSW(getStatusRsp) != Data.SW_NO_ERROR) {
                        status = Data.SW_REFERENCE_DATA_NOT_FOUND;
                        break activationProcedure;
                    }

                    getStatusRspTlvs = TLV.parse(Utils.getRDATA(getStatusRsp)).get(0).getNodes();
                    tlvResult = TLV.find(getStatusRspTlvs, Data.TAG_VC_STATE);

                    if (tlvResult != null) {
                        vcState = tlvResult.getValue();
                        if (vcState.length == 0x02) {

                            if (((vcState[0] & 0x80) == 0x80) //check if VCM_LOCKED
                                    || ((vcState[1] & 0x80) == 0x80)) // Check if VCM_NON_ACTIVABLE
                            {
                                status = Data.SW_CONDITION_OF_USE_NOT_SATISFIED;
                                break activationProcedure;
                            }
                        }
                    } else {
                        status = Data.SW_UNEXPECTED_BEHAVIOR;
                        break activationProcedure;
                    }

                    if (concurrentActivationMode == true) {
                        status = (short) manageConcurrencyActivation(vcEntry);
                    } else {
                        status = (short) manageNonConcurrencyActivation(vcmAid);
                    }
                }
                /*If current request is DeActivation
                 * */
                else {
                    setStatusRsp = setStatus(TLV.make(0x4F, vcmAid), false);
                    status = Utils.getSW(setStatusRsp);
                }

            } //activationProcedure block ends

            Log.i(TAG, "activateVirtualCard : " + Utils.bytArrayToHex(rData));
            Log.i(TAG, "Exit");
            return Utils.createStatusCode(status);

        } // try block ends
        catch (Exception e) {
            e.printStackTrace();
            return Utils.createStatusCode(Data.SW_EXCEPTION);
        } finally {
            closeLogicalChannel();
        }
    }

    /*******************************************************
     *
     *  Add and Updates MDAC used to define the MIFARE Data that
     *  may be retrieved by the SP Application
     *
     *@param    vcEntry
     *@param    vcData
     *
     *@return   Status word informing Success or Failure
     *
     ********************************************************/

    @Override
    public synchronized byte[] addAndUpdateMdac(int vcEntry, byte[] vcData) throws RemoteException {
        String TAG = "LTSMCommunicator:addAndUpdateMdac";
        currentProcess = "addAndUpdateMdac";
        Log.i(TAG, "Enter");
        statusCode = 0;
        byte[] rData = new byte[0];
        int stat = Data.FAILED;

        String pkg = Utils.getCallingAppPkg(context);

        stat = ltsmStart();
        if (stat != Data.SW_NO_ERROR) {
            Log.i(TAG, "LTSMStart Failed");
            closeLogicalChannel();
            return Utils.createStatusCode((short) stat);
        }

        byte[] cData = VCDescriptionFile.AddandUpdateMDAC(vcData, (short) vcEntry, Utils.createSha(pkg));
        Log.i(TAG, "cData : " + cData.length);
        rData = exchangeLtsmProcessCommand(cData);

        if (Utils.getSW(rData) == Data.SW_NO_ERROR) {
            rData = Utils.createStatusCode(Data.SW_NO_ERROR);
        } else {
            rData = Utils.createStatusCode(Utils.getSW(rData));
        }
        closeLogicalChannel();
        Log.i(TAG, "retStat : " + Utils.arrayToHex(rData));
        Utils.exportReg();
        Log.i(TAG, "Exit");
        return rData;
    }

    /*******************************************************
     *
     *  Read Mifare Data used to retrieve MIFARE data from a VC
     *  under the condition that the MIFARE Data Access Control(s)
     *  have been provided with Add and update MDAC
     *
     *@param    vcData
     *@param    vcEntry
     *
     *@return   Read Mifare Data and Status Word
     *
     ********************************************************/

    @Override
    public synchronized byte[] readMifareData(int vcEntry, byte[] vcData) throws RemoteException {
        String TAG = "LTSMCommunicator:ReadMifareClassicData";
        Log.i(TAG, "Enter");
        statusCode = 0;
        byte[] rData = new byte[0];
        byte[] recvData = new byte[] {};
        byte[] smAid = new byte[] {};
        TLV tlvResult = null;

        String pkg = Utils.getCallingAppPkg(context);

        int i, stat;
        stat = ltsmStart();
        if (stat != Data.SW_NO_ERROR) {
            Log.i(TAG, "LTSMStart Failed");
            closeLogicalChannel();
            return Utils.createStatusCode((short) stat);
        }

        byte[] getStatusRsp = getStatus(TLV.make(Data.TAG_VC_ENTRY, Utils.shortToByteArr((short) vcEntry)));
        if (Utils.getSW(getStatusRsp) != Data.SW_NO_ERROR) {
            statusCode = Utils.getSW(getStatusRsp);
            closeLogicalChannel();
            return Utils.createStatusCode(statusCode);
        }

        List<TLV> getStatusRspTlvs = TLV.parse(Utils.getRDATA(getStatusRsp)).get(0).getNodes();
        tlvResult = TLV.find(getStatusRspTlvs, Data.TAG_SP_AID);

        if (tlvResult != null) {
            byte[] spAid = tlvResult.getValue();
            Log.i(TAG, "spAid : " + Utils.bytArrayToHex(spAid));
            Log.i(TAG, "appid : " + Utils.bytArrayToHex(Utils.getApkHash(pkg)));
            if (!Arrays.equals(Utils.getApkHash(pkg), spAid)) {
                closeLogicalChannel();
                return Utils.createStatusCode(Data.SW_CONDITION_OF_USE_NOT_SATISFIED);
            }
        } else {
            //TODO confirm with else
        }

        TLV tlvResult1 = TLV.find(getStatusRspTlvs, Data.TAG_SM_AID);
        if (tlvResult1 != null) {
            smAid = tlvResult1.getValue();
        } else {
            smAid = Utils.append(Data.SERVICE_MANAGER_AID, Utils.shortToByteArr((short) vcEntry));//Modified
        }
        byte[] cApdu = Utils.makeCAPDU(0x00, 0xA4, 0x04, 0x00, smAid);

        recvData = exchange(cApdu, channelId[channelCnt - 1]);
        if (recvData == null) {
            Log.i(TAG, "SE transceive failed ");
            closeLogicalChannel();
            return Utils.createStatusCode(Data.SW_NO_DATA_RECEIVED);
        }

        Log.i(TAG, "Received Data : " + Utils.bytArrayToHex(recvData));

        cApdu = Utils.makeCAPDU(0x80, 0xB0, 0x00, 0x00, vcData);
        recvData = exchange(cApdu, channelId[channelCnt - 1]);
        rData = processReadMifareDataResponse(recvData);

        Log.i(TAG, "Exit");
        closeLogicalChannel();
        Log.i(TAG, "recvData : " + Utils.bytArrayToHex(rData));
        return rData;
    }

    /*******************************************************
     *
     *  getVcList used to retrieve the list of couples VC entry
     *  in used and associated SP Application
     *
     *@param    none
     *
     *@return   byte array containing List of VC entry in use
     *and associated SP Applications and status word.
     *
     ********************************************************/

    @Override
    public byte[] getVcList() throws RemoteException {
        String TAG = "LTSMCommunicator:getVcList";
        Log.i(TAG, "Enter");
        byte[] tempData = new byte[] {};
        int stat = Data.FAILED;
        statusCode = 0;
        byte[] rData = new byte[0];
        byte[] VcList = new byte[0];

        boolean available = Utils.checkRegistryAvail(context);
        if (!available) {
            available = Utils.checkRegBackupAvail();
            if (available) {
                Utils.importReg();
            } else {
                Log.i(TAG, "no backup available");
                return Utils.createStatusCode(Data.SW_REGISTRY_IS_EMPTY);
            }
        }

        stat = ltsmStart();
        if (stat != Data.SW_NO_ERROR) {
            Log.i(TAG, "LTSMStart Failed");
            closeLogicalChannel();
            return Utils.createStatusCode((short) stat);
        }

        //If Registry is already available in SharedPreference Check for Uninstalled Wallet Apps
        stat = checkUninstalledApps();
        if (stat == Data.SW_NO_ERROR) {
            Log.i(TAG, "Uninstalled App Found");
        }
        rData = getStatus(null);
        if (Utils.getSW(rData) == Data.SW_NO_ERROR) {
            List<TLV> getStatusRspTlvs = TLV.parse(Utils.getRDATA(rData));
            if (getStatusRspTlvs != null) {
                VcList = VCDescriptionFile.getVcListResp(getStatusRspTlvs);
            }

            if (VcList.length != 0) {
                rData = Utils.append(VcList, Utils.createStatusCode(Utils.getSW(rData)));
            } else {
                rData = Utils.createStatusCode(Data.SW_REFERENCE_DATA_NOT_FOUND);
            }
        } else {
            rData = Utils.createStatusCode(Utils.getSW(rData));
        }

        Log.i(TAG, "retData : " + Utils.bytArrayToHex(rData));
        closeLogicalChannel();
        Utils.exportReg();
        return rData;
    }

    /*******************************************************
     *
     *Get the virtual card status from SE
     *
     ********************************************************/
    private byte[] getStatus(byte[] data) {
        String TAG = "LTSMCommunicator:getStatus";
        Log.i(TAG, "Enter");

        byte[] capdu = Utils.makeCAPDU(0x80, 0xF2, 0x40, 0x00, data);
        byte[] rapdu = exchange(capdu, channelId[channelCnt - 1]);
        byte[] status = new byte[0];
        int initLen = status.length;
        loop: for (;;) {
            Log.i(TAG, "rapdu : " + Utils.bytArrayToHex(rapdu));
            switch (Utils.getSW(rapdu)) {
            case (short) (0x6310):
                status = Utils.append(status, Utils.getRDATA(rapdu));
                break;

            case (short) (0x9000):
                status = Utils.append(status, rapdu);
                break loop;

            case (short) (0x6A88):
                status = Utils.append(status, rapdu);
                break loop;

            default: // Unexpected error.
                // FALL THROUGH
                break loop;
            }
            capdu = Utils.makeCAPDU(0x80, 0xF2, 0x40, 0x01, null); // Next occurrence.
            rapdu = exchange(capdu, channelId[channelCnt - 1]);
        }
        Log.i(TAG, "status : " + Utils.bytArrayToHex(status));
        return status;
    }

    /*******************************************************
     *
     *Set the virtual card status
     *
     ********************************************************/
    private byte[] setStatus(byte[] data, boolean mode) {
        String TAG = "LTSMCommunicator:setStatus";
        Log.i(TAG, "Enter");

        byte[] cData = new byte[] {};
        byte[] rData = new byte[] {};

        if (mode == true) {
            cData = Utils.makeCAPDU((byte) 0x80, (byte) 0xF0, 0x01, (byte) 0x01, data);
        } else {
            cData = Utils.makeCAPDU((byte) 0x80, (byte) 0xF0, 0x01, (byte) 0x00, data);
        }
        rData = exchangeWithSe(cData);
        Log.i(TAG, "Exit");
        return rData;
    }

    /*******************************************************
     *
     *Manage concurrency Activation Procedure
     * @param vcEntry
     * @return status
     *
     ********************************************************/
    private int manageConcurrencyActivation(int vcEntry) {
        String TAG = "LTSMCommunicator:manageCuncurrencyActivation";
        Log.i(TAG, "Enter");
        byte[] setStatusRsp = new byte[] {};
        byte[] getStatusRsp = new byte[] {};
        byte[] conflictingAidsTagA2 = new byte[] {};
        int skipFirstTag48 = 1;
        List<TLV> setStatusRspTlvs, parseTlvs;
        TLV tlvResult;
        int status;
        byte[] vcmAidToActivate = Utils.append(Data.VC_MANAGER_AID, Utils.shortToByteArr((short) vcEntry));

        if (!previousConcurrentActivationMode) {
            // Deactivate all
            deactivateAllContactlessApplications();
        }
        previousConcurrentActivationMode = true;

        /* Try to activate the intended VC
         * */
        tryActivate: for (;;) {
            setStatusRsp = setStatus(TLV.make(0x4F, vcmAidToActivate), true);

            switch (Utils.getSW(setStatusRsp)) {
            case Data.SW_NO_ERROR:

                Log.i(TAG, "Activated Successfully");
                status = Data.SW_NO_ERROR;
                break tryActivate;

            case Data.SW_6330_AVAILABLE:

            case Data.SW_6310_AVAILABLE:
                /*Activation not successful
                 * Conflicting AIDs present
                 * */
                tlvResult = getTlvA0A2(setStatusRsp);

                switch (tlvResult.getTag()) {
                case Data.TAG_A0_AVAILABLE:
                    byte[] conflictingAidsTagA0 = tlvResult.getValue();
                    setStatusRsp = setStatus(conflictingAidsTagA0, false);
                    if (Utils.getSW(setStatusRsp) == Data.SW_NO_ERROR) {
                        /*
                         * deActivation of conflictingAidsTagA0 successful
                         * try to activate initial intended aid
                         * */
                        break;
                    } else {
                        Log.i(TAG, "Dactivation of conflicting Aids of TagA0 failed. Status : "
                                + Utils.getSW(setStatusRsp));
                        status = Data.SW_UNEXPECTED_BEHAVIOR;
                        break tryActivate;
                    }

                case Data.TAG_A2_AVAILABLE:
                    parseTlvs = TLV.parse(tlvResult.getValue());
                    tlvResult = TLV.find(parseTlvs, Data.TAG_48_AVAILABLE);

                    switch (Utils.byteArrayToShort(tlvResult.getValue())) {
                    case Data.REASON_CODE_800B:
                    case Data.REASON_CODE_800C:

                        status = Data.SW_VC_IN_CONTACTLESS_SESSION;
                        break tryActivate;

                    case Data.REASON_CODE_8002:
                    case Data.REASON_CODE_8003:
                    case Data.REASON_CODE_8004:
                    case Data.REASON_CODE_8005:

                        /*Tag format
                         * A2 --
                         *       48 -- Mandatory
                         *       4F -- Mandatory
                         *       4F -- Optional
                         *       ..... .....
                         *       48 -- Optional
                         *       4F -- Optional
                         *       4F -- Optional
                         *       ..... .....
                         *       ..... .....
                         *       Get 4F Tag corresponding to only one Tag48
                         * */

                        conflictingAidsTagA2 = getSubsequentTlvs(skipFirstTag48, Data.TAG_4F_AVAILABLE, parseTlvs);

                        setStatusRsp = setStatus(conflictingAidsTagA2, false);

                        if (Utils.getSW(setStatusRsp) != Data.SW_NO_ERROR) {
                            Log.i(TAG, "Dactivation of conflicting Aids of TagA0 failed. Status : "
                                    + Utils.getSW(setStatusRsp));
                            status = Data.SW_UNEXPECTED_BEHAVIOR;
                            break tryActivate;
                        }
                        /*
                         * deActivation of conflictingAidsTagA2 successful
                         * try to activate initial intended aid
                         * */
                        break;

                    case Data.REASON_CODE_8009:
                        /*
                         * Deactivate the VC that contains a Mifare Desfire AID conflicting with
                         * Mifare Desfire AID of the VC to be activated
                         * */

                        TLV tlv4F, tlv;
                        tlv4F = TLV.find(parseTlvs, Data.TAG_4F_AVAILABLE);
                        byte[] aid = tlv4F.getValue();

                        /*appending 0x5C,0xA6 will make restrict the return Tag to only A6 (avoids all unnecessary tags)
                         * */
                        byte[] capdu = Utils.makeCAPDU(0x80, 0xF2, 0x40, 0x00,
                                Utils.append(TLV.make(0x4F, aid), TLV.make(0x5C, new byte[] { (byte) (0xA6) })));
                        getStatusRsp = exchangeWithSe(capdu);
                        Log.i(TAG, "REASON_CODE_8009 getStatusRsp"
                                + Utils.bytArrayToHex(Utils.getRDATA(getStatusRsp)));
                        parseTlvs = TLV.parse(Utils.getRDATA(getStatusRsp), new int[] { 0xF1 });
                        tlv = TLV.find(parseTlvs, 0x61);
                        parseTlvs = tlv.getNodes();
                        tlv = TLV.find(parseTlvs, Data.TAG_A6_AVAILABLE);
                        if (tlv == null) {
                            Log.i(TAG, "Tag A6 missing");
                            status = Data.SW_UNEXPECTED_BEHAVIOR;
                            break tryActivate;
                        }
                        parseTlvs = tlv.getNodes();
                        tlv = TLV.find(parseTlvs, Data.TAG_F1_AVAILABLE);
                        if (tlv == null) {
                            status = Data.SW_UNEXPECTED_BEHAVIOR;
                            break tryActivate;
                        }

                        setStatusRsp = setStatus(TLV.make(Data.TAG_4F_AVAILABLE, tlv.getValue()), false);
                        if (Utils.getSW(setStatusRsp) != Data.SW_NO_ERROR) {
                            status = Data.SW_UNEXPECTED_BEHAVIOR;
                            break tryActivate;
                        }
                        //TODO notify the application for deactivation of its VC
                        break;

                    default:
                        status = Data.SW_CONDITION_OF_USE_NOT_SATISFIED;
                        break tryActivate;

                    }
                    break;

                default:
                    status = Data.SW_UNEXPECTED_BEHAVIOR;
                    break tryActivate;
                }
                break;

            default:
                status = Data.SW_UNEXPECTED_BEHAVIOR;
                break tryActivate;
            }
        }

        Log.i(TAG, "Exit");
        return status;

    }

    /*******************************************************
     *
     *Manage Non-concurrency Activation Procedure
     * @param vcEntry
     * @return status
     *
     ********************************************************/
    private int manageNonConcurrencyActivation(byte[] vcmAid) {
        String TAG = "LTSMCommunicator:manageNonConcurrencyActivation";
        Log.i(TAG, "Enter");
        int status = 0;
        status = deactivateAllContactlessApplications();
        if (status != Data.SW_NO_ERROR) {
            return status;
        }
        previousConcurrentActivationMode = false;
        // Activate VCM (Do not check SW).
        byte[] capdu = Utils.makeCAPDU(0x80, 0xF0, 0x01, 0x01, TLV.make(0x4F, vcmAid));
        byte[] rapdu = exchangeWithSe(capdu);
        return Utils.getSW(rapdu);
    }

    private static boolean isVCManagerAID(byte[] aid) {
        if (aid.length < Data.NONCONCURRENT_AID_PARTIAL.length) {
            return false;
        }
        for (int i = 0; i < Data.NONCONCURRENT_AID_PARTIAL.length - 1; i++) {
            if (aid[i] != Data.NONCONCURRENT_AID_PARTIAL[i]) {
                return false;
            }
        }
        return (byte) (aid[Data.NONCONCURRENT_AID_PARTIAL.length - 1] & 0xF0) == 0x10;
    }

    private int deactivateAllContactlessApplications() {
        // First, deactivate all VCMs; Then deactivate all other applications.
        int status = 0;
        byte p2 = 0x00; // First
        for (boolean more = true; more; p2 = 0x01) {
            // 5. Get the list of activated VCMs.
            byte[] capdu = Utils.makeCAPDU(0x80, 0xF2, 0x40, p2,
                    Utils.append(TLV.make(0x4F, Utils.parseHexString("A000000396")), // RID
                            Utils.parseHexString("9F700207015C014F")));
            byte[] rapdu = exchangeWithSe(capdu);

            switch (Utils.getSW(rapdu)) {
            default:
                status = Data.SW_CONDITION_OF_USE_NOT_SATISFIED;
                return status;

            case Data.SW_6310_AVAILABLE:
                more = true;
                break;

            case Data.SW_NO_ERROR:
                more = false;
                status = Data.SW_NO_ERROR;
                break;

            case Data.SW_REFERENCE_DATA_NOT_FOUND:
                more = false;
                continue;
            }

            List<TLV> tlvs = TLV.parse(Utils.getRDATA(rapdu), new int[] { 0xF1 });
            for (TLV tlv61 : tlvs) {
                if (tlv61.getTag() != 0x61) {
                    continue;
                }

                List<TLV> tlv61s;
                if ((tlv61s = tlv61.getNodes()) == null) {
                    return 0;
                }

                for (TLV tlvAid : tlv61s) {
                    if (tlvAid.getTag() == 0x4F) {
                        byte[] aid = tlvAid.getValue();
                        // VCM ?
                        if (!isVCManagerAID(aid)) {
                            continue;
                        }

                        // Deactivate VCM (Do not check SW).
                        capdu = Utils.makeCAPDU(0x80, 0xF0, 0x01, 0x00, TLV.make(0x4F, aid));
                        rapdu = exchangeWithSe(capdu);
                        Intent intent = new Intent();
                        intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                        intent.setAction("com.nxp.ltsm.ltsmclient.VC_DEACTIVATED");
                        intent.putExtra("VC_AID", aid);
                        context.sendBroadcast(intent);

                    }
                }
            }
        }

        p2 = 0x00;
        for (boolean more = true; more; p2 = 0x01) {
            // 6. List all remaining activated contactless applications
            byte[] capdu = Utils.makeCAPDU(0x80, 0xF2, 0x40, p2, Utils.parseHexString("4F009F700207015C014F"));
            byte[] rapdu = exchangeWithSe(capdu);

            switch (Utils.getSW(rapdu)) {
            default:
                status = Data.SW_CONDITION_OF_USE_NOT_SATISFIED;
                return status;

            case Data.SW_6310_AVAILABLE:
                more = true;
                break;

            case Data.SW_NO_ERROR:
                more = false;
                status = Data.SW_NO_ERROR;
                break;

            case Data.SW_REFERENCE_DATA_NOT_FOUND:
                more = false;
                continue;
            }

            List<TLV> tlvs = TLV.parse(Utils.getRDATA(rapdu), new int[] { 0xF1 });
            for (TLV tlv61 : tlvs) {
                if (tlv61.getTag() != 0x61) {
                    continue;
                }

                List<TLV> tlv61s;
                if ((tlv61s = tlv61.getNodes()) == null) {
                    return 0;
                }

                for (TLV tlvAid : tlv61s) {
                    if (tlvAid.getTag() == 0x4F) {
                        byte[] aid = tlvAid.getValue();

                        // Deactivate VCM (Do not check SW).
                        capdu = Utils.makeCAPDU(0x80, 0xF0, 0x01, 0x00, TLV.make(0x4F, aid));
                        rapdu = exchangeWithSe(capdu);
                        Intent intent = new Intent();
                        intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                        intent.setAction("com.nxp.ltsm.ltsmclient.VC_DEACTIVATED");
                        intent.putExtra("VC_AID", aid);
                        context.sendBroadcast(intent);
                    }
                }
            }
        }
        return Data.SW_NO_ERROR;
    }

    /*******************************************************
     *
     *Returns subsequent Tags in parseTlvs
     *
     ********************************************************/
    private byte[] getSubsequentTlvs(int startNode, short tagToFind, List<TLV> parseTlvs) {
        byte[] foundTlvs = new byte[] {};

        for (int i = startNode; i < parseTlvs.size(); i++) {
            if (parseTlvs.get(i).getTag() == tagToFind) {
                foundTlvs = Utils.append(foundTlvs, parseTlvs.get(i).getTLV());
            } else {
                /*break from for loop*/
                break;
            }
        }
        return foundTlvs;
    }

    /*******************************************************
     *
     *Returns either TLV Tag A0 or Tag A1
     *
     ********************************************************/
    private TLV getTlvA0A2(byte[] setStatusRsp) {
        String TAG = "LTSMCommunicator:getTagsA0A2";
        Log.i(TAG, "Enter");
        List<TLV> setStatusRspTlvs;
        TLV tlvResult, retTlv = null;
        Log.i(TAG, "getTlvA0A2 setStatusRsp" + Utils.bytArrayToHex(Utils.getRDATA(setStatusRsp)));
        setStatusRspTlvs = TLV.parse(Utils.getRDATA(setStatusRsp)).get(0).getNodes();
        tlvResult = TLV.find(setStatusRspTlvs, Data.TAG_A0_AVAILABLE);
        /*Tag A0 Present*/
        if (tlvResult != null) {
            retTlv = tlvResult;
        } else {
            tlvResult = TLV.find(setStatusRspTlvs, Data.TAG_A2_AVAILABLE);
            if (tlvResult != null) {
                retTlv = tlvResult;
            }
        }
        Log.i(TAG, "Exit");

        return retTlv;
    }

    /*******************************************************
     *
     * Remove Registry Entry
     *
     ********************************************************/
    private void removeRegistryEntry(int pos, ltsmRegistry Registry) {
        // int tmpVc = Registry.walletName.get(pos);
        String TAG = "LTSMCommunicator:removeRegistryEntry";
        //Registry.vcEntry.remove(pos);
        // Log.i(TAG, " DeleteVCEntry " + tmpVc);
        saveRegistry(Registry);
    }

    /*******************************************************
     *
     * Initiate the LTSM. Opens the Logical Channel and Selects the LTSM
     *
     ********************************************************/

    public static int ltsmStart() {
        String TAG = "LTSMCommunicator:LTSMStart";
        Log.i(TAG, "Enter");
        int status;
        status = ltsmOpenChannel();
        if (status != Data.SW_NO_ERROR) {
            Log.i(TAG, "LTSM Open Channel Failed");
            return status;
        }
        status = ltsmSelect();
        if (status != Data.SW_NO_ERROR) {
            Log.i(TAG, "LTSM select Failed");
            closeLogicalChannel();
            return status;
        }
        status = Data.SW_NO_ERROR;
        Log.i(TAG, "Exit");
        return status;
    }

    /*******************************************************
     *
     * Opens Logical Channel
     *
     ********************************************************/

    private static int ltsmOpenChannel() {
        String TAG = "LTSMCommunicator:openLogicalChannel";
        int status;
        Log.i(TAG, "Enter");
        statusCode = 0;
        byte[] recvData = new byte[1024];

        try {
            openConnection();
        } catch (RemoteException e) {
            closeLogicalChannel();
            e.printStackTrace();
        }
        try {
            recvData = exchangeApdu(Data.openchannel);
        } catch (RemoteException e) {
            closeLogicalChannel();
            e.printStackTrace();
        }
        if (recvData == null) {
            Log.i(TAG, "SE transceive failed ");
            status = Data.SW_SE_TRANSCEIVE_FAILED;
        } else if (Utils.getSW(recvData) != Data.SW_NO_ERROR) {
            statusCode = Utils.getSW(recvData);
            Log.i(TAG, "Invalid Response");
            status = Utils.getSW(recvData);
        } else {
            Log.i(TAG, "openLogicalChannel SUCCESS");
            channelId[channelCnt] = recvData[recvData.length - 3];
            isopened[channelCnt] = 1;

            Utils.saveArray(channelId, "channelId", context);
            Utils.saveArray(isopened, "isopened", context);
            channelCnt = channelCnt + 1;
            Utils.saveInt(channelCnt, "channelCnt", context);
            status = Data.SW_NO_ERROR;
        }

        Log.i(TAG, "Exit");
        return status;
    }

    /*******************************************************
     *
     *Selects LTSM with currently opened Logical channel
     *
     ********************************************************/

    private static int ltsmSelect() {
        String TAG = "LTSMCommunicator:ltsmSelect";
        int status;
        boolean available = false;
        Log.i(TAG, "Enter");
        statusCode = 0;
        byte[] cApdu = Utils.makeCAPDU(0x00, 0xA4, 0x04, 0x00, Data.AID_M4M_LTSM);
        byte[] recvData = new byte[1024];

        recvData = exchange(cApdu, channelId[channelCnt - 1]);
        if (recvData == null) {
            Log.i(TAG, "SE transceive failed ");
            statusCode = Data.SW_SE_TRANSCEIVE_FAILED;
            status = Data.SW_SE_TRANSCEIVE_FAILED;
        } else {
            Log.i(TAG, "Received Data : " + Utils.bytArrayToHex(recvData));
            if (Utils.getSW(recvData) == Data.SW_NO_ERROR) {
                Log.i(TAG, "ltsmSelect SUCCESS ");
                status = Data.SW_NO_ERROR;
            } else {
                statusCode = Utils.getSW(recvData);
                status = Utils.getSW(recvData);
            }
        }
        Log.i(TAG, "Exit");
        return status;
    }

    /*******************************************************
     *
     *Handles Exchange of Data during LTSM Process Command
     *
     ********************************************************/

    private static byte[] exchangeLtsmProcessCommand(byte[] cdata) {
        String TAG = "LTSMCommunicator:exchangeLtsmProcessCommand";
        int stat = Data.SW_REFERENCE_DATA_NOT_FOUND;
        Log.i(TAG, "Enter");
        byte[] rData = new byte[0];
        Log.i(TAG, "(byte)cdata.length : " + (byte) cdata.length + "new byte[] {(byte)cdata.length} :  "
                + Utils.bytArrayToHex(new byte[] { (byte) cdata.length }));
        byte[] cApdu = Utils.makeCAPDU(Data.CLA_M4M_LTSM, Data.INS_M4M_LTSM_PROCESS_COMMAND, 0x00, 0x00, cdata);
        Log.i(TAG, "cApdu : " + Utils.bytArrayToHex(cApdu));
        byte[] rapdu = exchange(cApdu, channelId[channelCnt - 1]);

        try {

            exchangeLtsmCmdbreak: {
                switch (Utils.getSW(rapdu)) {
                default: {
                    rData = rapdu;
                    break;
                }

                case Data.SW_NO_ERROR:
                    cApdu = Utils.getRDATA(rapdu);
                    Log.i(TAG, "SW_NO_ERROR ");
                    rData = rapdu;
                    break;
                case Data.SW_6310_AVAILABLE:
                    process_se_response: for (;;) {
                        if (rapdu.length == 2) {
                            rData = rapdu;
                            break;
                        }
                        cApdu = Utils.getRDATA(rapdu);
                        Log.i(TAG, "SW_6310_AVAILABLE: Send Data back to SE ");
                        //             toLTSM = false;
                        rapdu = exchangeWithSe(cApdu);
                        if (rapdu.length < 256) {
                            cApdu = Utils.makeCAPDU(Data.CLA_M4M_LTSM, Data.INS_M4M_LTSM_PROCESS_SE_RESPONSE, 0x00,
                                    0x80, rapdu);
                            //                    }
                            rapdu = exchange(cApdu, channelId[channelCnt - 1]);

                            switch (Utils.getSW(rapdu)) {
                            default: {
                                rData = rapdu;
                                break process_se_response;
                            }

                            case Data.SW_NO_ERROR:
                                Log.i(TAG, "SW_NO_ERROR ");
                                rData = rapdu;
                                break process_se_response;

                            case Data.SW_6310_AVAILABLE:
                                Log.i(TAG, "SW_6310_AVAILABLE: Send Data back to SE Again");
                            }
                        } else {
                            byte[] rapdu1 = Utils.extract(rapdu, 0, 255);
                            byte[] rapdu2 = Utils.extract(rapdu, 255, rapdu.length - 255);

                            // First part
                            cApdu = Utils.makeCAPDU(Data.CLA_M4M_LTSM, Data.INS_M4M_LTSM_PROCESS_SE_RESPONSE, 0x00,
                                    0x00, rapdu1);
                            rapdu = exchange(cApdu, channelId[channelCnt - 1]);

                            if (Utils.getSW(rapdu) != Data.SW_NO_ERROR) {
                                System.err.println("FAILED: APDU exchange (PROCESS SE RESPONSE) (1 of 2) failed!");
                                System.err.println(String.format("LTSM-SW: %04X", Utils.getSW(rapdu)));
                                System.exit(1);
                            }

                            // Second part
                            cApdu = Utils.makeCAPDU(Data.CLA_M4M_LTSM, Data.INS_M4M_LTSM_PROCESS_SE_RESPONSE, 0x00,
                                    0x80, rapdu2);
                            rapdu = exchange(cApdu, channelId[channelCnt - 1]);

                            switch (Utils.getSW(rapdu)) {
                            default: {
                                rData = rapdu;
                                break process_se_response;
                            }

                            case Data.SW_NO_ERROR:
                                rData = rapdu;
                                break process_se_response;

                            case Data.SW_6310_AVAILABLE:
                                // FALL THROUGH
                            }
                        }
                    }
                }
            } //break block exit
        } //try exit
        catch (Exception e) {
            e.printStackTrace();
        }
        Log.i(TAG, "Exit");
        return rData;
    }

    /*******************************************************
     *
     *Exchange APDU With Secure Element
     *
     ********************************************************/

    private static byte[] exchangeWithSe(byte[] cdata) {
        String TAG = "LTSMCommunicator:exchangeWithSe";
        Log.i(TAG, "Enter");
        byte[] recvData = new byte[1024];
        boolean channel_open_cmd = false;
        if (cdata[1] == 0x70) // MANAGE CHANNEL ?
        {
            if (cdata[2] == 0x00) // [open]
            {
                cdata[4] = (byte) 0x01;
            }
        }
        recvData = exchange(cdata, (cdata[0] & 0x03));
        return recvData;
    }

    /*******************************************************
     *
     *Common Exchange method for Exchange with SE and Process LTSM Command
     *
     ********************************************************/

    private static byte[] exchange(byte[] cApdu, int chnl_id) {
        String TAG = "LTSMCommunicator:exchange";
        Log.i(TAG, "Enter");
        byte[] rData = new byte[0];

        //     byte[] xchangeData = new byte[cApdu.length];
        try {

            cApdu[0] = Utils.adjustCLA(cApdu[0], (byte) chnl_id);

            Log.i(TAG, "Sending Data : " + Utils.bytArrayToHex(cApdu));
            rData = exchangeApdu(cApdu);
            if (rData.length == 0) {
                Log.i(TAG, "SE transceive failed ");
                rData = Utils.createStatusCode(Data.SW_NO_DATA_RECEIVED);
            }
            Log.i(TAG, " rData : " + Utils.bytArrayToHex(rData));
        } catch (RemoteException e) {
            closeLogicalChannel();
            e.printStackTrace();
        }
        return rData;
    }

    /*******************************************************
     *
     *Selects CRS during Activate VC
     *
     ********************************************************/

    private static int crsSelect() {
        String TAG = "LTSMCommunicator:crsSelect";
        byte[] rData = new byte[0];
        Log.i(TAG, "Enter");
        rData = exchangeWithSe(Data.selectCRS);
        if (rData == null) {
            Log.i(TAG, "SE transceive failed ");
            return Data.SW_NO_DATA_RECEIVED;
        }
        Log.i(TAG, "rData : " + Utils.bytArrayToHex(rData));
        Log.i(TAG, "Exit");
        return Utils.getSW(rData);
    }

    /*******************************************************
     *
     *Closes currently opened Logical Channels
     *
     ********************************************************/

    public static void closeLogicalChannel() {
        String TAG = "LTSMCommunicator:closeLogicalChannel";
        Log.i(TAG, "Enter");
        byte[] rData = new byte[0];
        byte[] closeChannelCmd = new byte[] {};
        channelCnt = Utils.loadInt("channelCnt", context);
        channelId = Utils.loadArray("channelId", context);
        isopened = Utils.loadArray("isopened", context);

        Log.i(TAG, "channelCnt : " + channelCnt);

        for (int cnt = 0; (cnt < channelCnt); cnt++) {
            if (isopened[cnt] == 1) {

                closeChannelCmd = new byte[] { (byte) channelId[cnt], (byte) 0x70, (byte) 0x80,
                        (byte) channelId[cnt], (byte) 0x00 };
                try {
                    rData = exchangeApdu(closeChannelCmd);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

                if (rData == null) {
                    Log.i(TAG, "exchangeApdu FAILED");
                    rData = Utils.createStatusCode(Data.SW_NO_DATA_RECEIVED);
                } else if (Utils.getSW(rData) == Data.SW_NO_ERROR) {
                    Log.i(TAG, "Close Channe id : " + channelId[cnt] + "is SUCCESS");
                    channelCnt = channelCnt - 1;
                } else {
                    Log.i(TAG, "Close Channe id : " + channelId[cnt] + "is Failed");
                }
            }
        }

        Utils.saveInt(channelCnt, "channelCnt", context);

        try {
            closeConnection();
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        Log.i(TAG, "Exit");
    }

    /*******************************************************
     *
     *Deletes VC's which are not Created Properly
     *
     ********************************************************/

    private int deleteTempVc(int vcEntry, String pkg) {
        String TAG = "LTSMCommunicator:deleteTempVc";
        Log.i(TAG, "Enter");
        int stat = Data.FAILED;
        DeleteVCEntry = vcEntry;
        Log.i(TAG, "DeleteVCEntry = vcEntry : " + DeleteVCEntry);
        currentProcess = "deleteVirtualCard";
        byte[] cData = VCDescriptionFile.deleteVc(pkg, (short) vcEntry);
        byte[] rData = exchangeLtsmProcessCommand(cData);
        Log.i(TAG, "Exit");
        return Utils.getSW(rData);
    }

    /*******************************************************
     *
     *Handles Personalisation During CreateVC
     *
     *******************************************************/

    private int handlePersonalize(byte[] vcData, String pkg, short intValue) {
        String TAG = "LTSMCommunicator:handlePersonalize";
        int stat = Data.SW_REFERENCE_DATA_NOT_FOUND;
        Log.i(TAG, "Enter");
        currentProcess = "handlePersonalize";
        byte[] cData = VCDescriptionFile.CreatePersonalizeData(vcData, pkg, Utils.createSha(pkg), intValue,
                context);
        Log.i(TAG, "cData : " + Utils.bytArrayToHex(cData));
        byte[] rData = exchangeLtsmProcessCommand(cData);
        return Utils.getSW(rData);
    }

    /*******************************************************
     *
     *Opens Connection for Communication with SE
     *
     ********************************************************/

    public static boolean openConnection() throws RemoteException {
        String TAG = "LTSMCommunicator:openConnection";
        Log.i(TAG, "Enter");
        String errorCause = "";

        try {

            bundle = new Bundle();
            try {
                mLtsmService = LtsmService.createLtsmServiceInterface();
                errorCause = "Open Secure Element: Failed";
                if (mLtsmService != null) {
                    bundle = mLtsmService.open("com.nxp.ltsm.ltsmclient", mBinder);
                }
                Log.i(LOG_TAG, "openConnection() to ESe: Success Bundle : " + bundle);
                if (bundle == null) {
                    Log.i(LOG_TAG, "openConnection() not successful");
                }

            } catch (Exception e) {
                Log.i(LOG_TAG, "openConnection() not successful");
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /*******************************************************
     *
     *Opens Connection for Communication with SE
     *
     ********************************************************/

    public static boolean closeConnection() throws RemoteException {
        // TODO Auto-generated method stub
        String TAG = "LTSMCommunicator:closeConnection";
        Log.i(TAG, "Enter");
        String errorCause = "";

        try {

            bundle = new Bundle();

            //mBinder = new Binder();
            try {
                if (mLtsmService != null) {
                    bundle = mLtsmService.close("com.nxp.ltsm.ltsmclient", mBinder);
                }
                Log.i(LOG_TAG, "closeConnection() to ESe: Success Bundle : " + bundle);
                if (bundle == null) {
                    Log.i(LOG_TAG, "closeConnection() not successful");
                }

            } catch (Exception e) {
                Log.i(LOG_TAG, "closeConnection() not successful");

            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /*******************************************************
     *
     *Transceives the APDU
     *
     ********************************************************/

    public static byte[] exchangeApdu(byte[] apdu) throws RemoteException {

        Log.i(LOG_TAG, "exchange APDU ENTER");
        try {
            if (mLtsmService != null) {
                bundle = mLtsmService.transceive("com.nxp.ltsm.ltsmclient", apdu);
            }
            if (bundle == null) {
                Log.i(LOG_TAG, "exchange APDU failed");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return bundle.getByteArray("out");
    }

    /*******************************************************
     *
     *Processes Create VC Response
     *
     ********************************************************/

    private static void processCreateVcResponse(byte[] rapdu) {
        String TAG = "processCreateVcResponse";
        Log.i(TAG, "Enter");
        //   String pkg = Utils.getCallingAppPkg(context);
        Log.i(TAG, "CurrentPackage : " + CurrentPackage);
        int i;

        vcUid = Utils.getValue((byte) 0x41, rapdu);
    }

    /*******************************************************
     *
     *Processes Read Mifare Classic Data Response
     *
     ********************************************************/

    private byte[] processReadMifareDataResponse(byte[] rapdu) {
        statusCode = 0;
        statusCode = Utils.getSW(rapdu);
        byte[] retdata = new byte[1024];

        if (Utils.getSW(rapdu) == Data.SW_NO_ERROR) {
            byte[] cApdu = Utils.getRDATA(rapdu);
            retdata = Utils.append(cApdu, Utils.createStatusCode(statusCode));
        } else {
            retdata = Utils.createStatusCode(statusCode);
        }
        return retdata;
    }

    /*******************************************************
     *
     *Loads LTSM Registry from SharedPreference
     *
     ********************************************************/

    public static ltsmRegistry loadRegistry() {
        String TAG = "LTSMCommunicator:loadRegistry";
        Log.i(TAG, "Enter");
        SharedPreferences prefs = context.getSharedPreferences("Reg", Context.MODE_PRIVATE);
        Gson gson = new Gson();
        String json = prefs.getString("ltsmRegistry", "");
        ltsmRegistry RegObj = gson.fromJson(json, ltsmRegistry.class);
        Log.i(TAG, "Exit");
        return RegObj;
    }

    /*******************************************************
     *
     *Saves LTSM Registry to SharedPreference
     *
     ********************************************************/

    public static void saveRegistry(ltsmRegistry Registry) {
        SharedPreferences.Editor prefsEditor = context.getSharedPreferences("Reg", Context.MODE_PRIVATE).edit();
        Gson gson = new Gson();
        String json = gson.toJson(Registry);
        prefsEditor.putString("ltsmRegistry", json);
        //        if(Registry.vcEntry.size()==10){
        //            prefsEditor.clear();
        //        }
        prefsEditor.commit();
    }

    /*******************************************************
     *
     *Check and Process Uninstalled Wallet Apps
     *
     ********************************************************/

    private int checkUninstalledApps() {
        String TAG = "LTSMCommunicator:checkUninstalledApps";
        Log.i(TAG, "Enter");
        List<PackageInfo> InstalledApps = Utils.getINstalledApps(context);
        List<String> UninstalledApps = new ArrayList<String>();
        Registry = loadRegistry();
        int i, j;
        byte regApkId[];
        /*
         * Checking Registry entry against Currently installed app entries
         * */
        for (i = 0; i < Registry.walletName.size(); i++) {
            for (j = 0; j < InstalledApps.size(); j++) {
                if (InstalledApps.get(j).packageName.equals(Registry.walletName.get(i).toString())) {
                    Log.i(TAG, "CONTAINS : " + Registry.walletName.get(i).toString());
                    break;
                }
            }
            /*
             * If Current Registry entry is not found in Currently installed app entries
             * */
            if (j == InstalledApps.size()) {
                UninstalledApps.add(Registry.walletName.get(i).toString());
            }
        }
        /*
         * Delete VCs which are in Uninstalled List
         * */
        if (!UninstalledApps.isEmpty()) {
            byte[] getStatusRsp = getStatus(null);
            List<TLV> getStatusRspTlvs = TLV.parse(Utils.getRDATA(getStatusRsp));
            for (i = 0; i < UninstalledApps.size(); i++) {
                for (j = 0; j < Registry.walletName.size(); j++) {
                    if (UninstalledApps.get(i).equals(Registry.walletName.get(j).toString())) {
                        String walletName_hash = Registry.walletName_hash.get(j).toString();
                        try {
                            regApkId = Hex.decodeHex(Registry.walletName_hash.get(j).toCharArray());
                        } catch (Exception e) {
                            e.printStackTrace();
                            return Data.SW_EXCEPTION;
                        }
                        if (getStatusRspTlvs != null) {
                            for (TLV tlv : getStatusRspTlvs) {
                                TLV tlvVCx = TLV.find(tlv.getNodes(), Data.TAG_VC_ENTRY);
                                TLV tlvApkId = TLV.find(tlv.getNodes(), Data.TAG_SP_AID);
                                byte ApkId[] = tlvApkId.getValue();
                                Log.i(TAG, "SEApkId : " + Utils.bytArrayToHex(ApkId));
                                Log.i(TAG, "regApkId : " + Utils.bytArrayToHex(regApkId));
                                if (Arrays.equals(ApkId, regApkId)) {
                                    deleteTempVc(Utils.byteArrayToShort(tlvVCx.getValue()), walletName_hash);
                                }
                            }
                            Registry.walletName.remove(j);
                            Registry.walletName_hash.remove(j);
                            saveRegistry(Registry);
                        } else {
                            Registry.walletName.remove(j);
                            Registry.walletName_hash.remove(j);
                            saveRegistry(Registry);
                        }
                    }
                }
            }

            Log.i(TAG, "Exit");
            return Data.SW_NO_ERROR;
        } else {
            Log.i(TAG, "Exit");
            return Data.SW_REFERENCE_DATA_NOT_FOUND;
        }
    }

    private static boolean inList(int value, int... values) {
        for (int v : values) {
            if (v == value) {
                return true;
            }
        }
        return false;
    }

    private byte[] removeTags(byte[] vcData, int... tags) {
        List<TLV> tlvs = new ArrayList<TLV>();

        for (TLV tlv : TLV.parse(vcData, new int[] { 0xA8, 0xE2, 0xF8 })) {
            if (!inList(tlv.getTag(), tags)) {
                tlvs.add(tlv);
            }
        }
        return TLV.make(tlvs);
    }
}