Merge c1a54fcde9aafe092ae341870fb3a1779072829f on remote branch

Change-Id: Ic41d94fcbe2fb61453ab7b7b40185f7d80ec924e
diff --git a/README.md b/README.md
index ca5588c..bd86d4f 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,34 @@
 # nfcandroid_frameworks
 
 
-#### Supported Versions on "br_android_ncihalx_q branch
-
+#### Supported Version on "br_android_ncihalx_comm_13" branch
 | Android Version        | NXP Release          | NXP Tag  |
 | :-------------: |:---------------------:| :-----:|
-| aosp-master                |  10.00.04 (SN100x) |  NFC_AR_00_2000_10.00.04_OpnSrc  |
-| aosp-master                |  10.00.06 (SN100x) |  NFC_AR_00_6000_10.00.06_OpnSrc  |
-| aosp-master              |  10.00.08 (SN100x) |  NFC_AR_00_6000_10.00.08_OpnSrc |
-| aosp-master              |  10.00.0A (SN110) |  NFC_AR_00_6000_10.00.0A_OpnSrc |
-| aosp-master              |  10.00.0B (SN100) |  NFC_AR_00_6000_10.00.0B_OpnSrc |
-| aosp-master              |  10.00.0C (SN110) |  NFC_AR_00_6000_10.00.0C_OpnSrc |
-| android-10.0.0_r2              |  10.00.0D (SN110) |  NFC_AR_00_6000_10.00.0D_OpnSrc |
-| android-10.0.0_r2              |  10.00.0F (SN110) |  NFC_AR_00_6000_10.00.0F_OpnSrc |
-
-#### Supported Version on "br_android_ncihalx_11" branch
-| Android Version        | NXP Release          | NXP Tag  |
-| :-------------: |:---------------------:| :-----:|
-| aosp-master           |  11.01.00 (SN110) |  NFC_AR_00_6000_11.01.00_OpnSrc |
-| aosp-master              |  11.02.00 (SN110) |  NFC_AR_00_6000_11.02.00_OpnSrc |
+| aosp-master            |  13.02.01 (SN100/SN110/SN220) |  NFC_AR_00_E800_13.02.01_OpnSrc |
+| aosp-master            |  13.03.00 (SN100/SN110/SN220) |  NFC_AR_00_E800_13.03.00_OpnSrc || AOSP-MASTER              |  13.03.00 (SN100/SN110/SN220) |  NFC_AR_00_E800_13.03.00_OpnSrc |
 
 
-#### Supported Version on "br_android_ncihalx_comm_12" branch
-| Android Version        | NXP Release          | NXP Tag  |
-| :-------------: |:---------------------:| :-----:|
-| aosp-master           |  12.51.00 (SN110) |  NFC_AR_00_02.00_6000_12.51.00_OpnSrc |
-| aosp-master              |  12.02.01 (SN100/SN110/SN220) |  NFC_AR_00_E000_12.02.01_OpnSrc |
-| aosp-master              |  12.04.01 (SN100/SN110/SN220)|  NFC_AR_00_E000_12.04.01_OpnSrc |
 
-#### Supported Version on "br_android_ncihalx_row_12" branch
-| Android Version        | NXP Release          | NXP Tag  |
-| :-------------: |:---------------------:| :-----:|
-| aosp-master            |  12.01.00 (PN80T/PN81T/PN553/PN557) |  NFC_AR_00_18C0_12.01.00_OpnSrc |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/com/nxp/nfc/INxpNfcAdapter.aidl b/com/nxp/nfc/INxpNfcAdapter.aidl
index c5d4719..f53d942 100644
--- a/com/nxp/nfc/INxpNfcAdapter.aidl
+++ b/com/nxp/nfc/INxpNfcAdapter.aidl
@@ -33,8 +33,6 @@
     void changeDiscoveryTech(IBinder binder, int pollTech, int listenTech);
     void startPoll(String pkg);
     byte[]  getFWVersion();
-    byte[] readerPassThruMode(byte status, byte modulationTyp);
-    byte[] transceiveAppData(in byte[] data);
     int setConfig(String configs , String pkg);
     int selectUicc(int uiccSlot);
     int getMaxAidRoutingTableSize();
@@ -48,4 +46,9 @@
     byte[] doReadT4tData(in byte[] fileId);
     int nfcSelfTest(int type);
     int configureSecureReader(boolean on, String readerType);
+    int startExtendedFieldDetectMode(int detectionTimeout);
+    int stopExtendedFieldDetectMode();
+    int startRssiMode(int rssiNtfTimeIntervalInMillisec);
+    int stopRssiMode();
+    boolean isRssiEnabled();
 }
diff --git a/com/nxp/nfc/NxpNfcAdapter.java b/com/nxp/nfc/NxpNfcAdapter.java
index 93d58c2..e6cc80a 100644
--- a/com/nxp/nfc/NxpNfcAdapter.java
+++ b/com/nxp/nfc/NxpNfcAdapter.java
@@ -110,6 +110,7 @@
      */
     private static void attemptDeadServiceRecovery(Exception e) {
         Log.e(TAG, "Service dead - attempting to recover",e);
+        sIsInitialized = false;
         INfcAdapter service = getServiceInterface();
         if (service == null) {
             Log.e(TAG, "could not retrieve NFC service during service recovery");
@@ -121,6 +122,9 @@
         // and on a well-behaved system should never happen
         sService = service;
         sNxpService = getNxpNfcAdapterInterface();
+        if (sNxpService != null) {
+            sIsInitialized = true;
+        }
         return;
     }
     /**
@@ -558,24 +562,6 @@
       }
     }
 
-    public byte[] readerPassThruMode(byte status, byte modulationTyp)
-        throws IOException {
-      try {
-        return sNxpService.readerPassThruMode(status, modulationTyp);
-      } catch (RemoteException e) {
-        Log.e(TAG, "Remote exception in readerPassThruMode(): ", e);
-        throw new IOException("Remote exception in readerPassThruMode()");
-      }
-    }
-
-    public byte[] transceiveAppData(byte[] data) throws IOException {
-      try {
-        return sNxpService.transceiveAppData(data);
-      } catch (RemoteException e) {
-        Log.e(TAG, "RemoteException in transceiveAppData(): ", e);
-        throw new IOException("RemoteException in transceiveAppData()");
-      }
-    }
     /**
      * This api is called by applications to update the NFC configurations which
      * are already part of libnfc-nxp.conf and libnfc-brcm.conf <p>Requires
@@ -698,6 +684,84 @@
     }
 
     /**
+     * This api is called by applications to start RSSI mode.
+     * Once RSSI is enabled, RSSI data notifications are broadcasted to registered
+     * application when the device is in the reader field. Application can then
+     * analyze this data and find best position for transaction.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.<ul>
+     * <li>This api shall be called only after Nfcservice is enabled.
+
+     * </ul>
+     * @param  rssiNtfTimeIntervalInMillisec to set time interval between RSSI
+     * notification in milliseconds. It is recommended that this value is
+     * greater than 10 millisecs and multiple of 10.
+     * @return whether  the update of configuration is
+     *          success or not with reason.
+     *          0x01  - NFC_IS_OFF,
+     *          0x02  - NFC_BUSY_IN_MPOS
+     *          0x03  - ERROR_UNKNOWN
+     *          0x00  - SUCCESS
+     * @throws  IOException if any exception occurs during setting the NFC configuration.
+     */
+    public int startRssiMode(int rssiNtfTimeIntervalInMillisec) {
+      try {
+        return sNxpService.startRssiMode(rssiNtfTimeIntervalInMillisec);
+      } catch (RemoteException e) {
+        e.printStackTrace();
+        attemptDeadServiceRecovery(e);
+        return 0x03; /*ERROR_UNKNOWN*/
+      }
+    }
+
+    /**
+     * This api is called by applications to stop RSSI mode
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.<ul>
+     * <li>This api shall be called only after Nfcservice is enabled.
+
+     * </ul>
+     * @param  None
+     * @return whether  the update of configuration is
+     *          success or not with reason.
+     *          0x01  - NFC_IS_OFF,
+     *          0x02  - NFC_BUSY_IN_MPOS
+     *          0x03  - ERROR_UNKNOWN
+     *          0x00  - SUCCESS
+     * @throws  IOException if any exception occurs during setting the NFC configuration.
+     */
+    public int stopRssiMode() {
+      try {
+        return sNxpService.stopRssiMode();
+      } catch (RemoteException e) {
+        e.printStackTrace();
+        attemptDeadServiceRecovery(e);
+        return 0x03; /*ERROR_UNKNOWN*/
+      }
+    }
+
+    /**
+     * This api is called by applications to check whether RSSI is enabled or not
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.<ul>
+     * <li>This api shall be called only after Nfcservice is enabled.
+
+     * </ul>
+     * @param  None
+     * @return whether  the feature is enabled(true) disabled (false)
+     *          success or not.
+     *          Enabled  - true
+     *          Disabled - false
+     * @throws  IOException if any exception occurs during setting the NFC configuration.
+     */
+    public boolean isRssiEnabled() {
+      try {
+        return sNxpService.isRssiEnabled();
+      } catch (RemoteException e) {
+        e.printStackTrace();
+        attemptDeadServiceRecovery(e);
+        return false;
+      }
+    }
+
+    /**
      * This api is called by applications to Activate Secure Element Interface.
      * <p>Requires {@link android.Manifest.permission#NFC} permission.<ul>
      * <li>This api shall be called only Nfcservice is enabled.
@@ -787,4 +851,44 @@
         return null;
       }
     }
+    /**
+     * This API starts extended field detect mode.
+     * @param detectionTimeout : The time after 1st RF ON to
+     *                            exit extended filed detect mode(msec).
+     * @return status     :-0x00 :EFDSTATUS_SUCCESS
+     *                      0x01 :EFDSTATUS_FAILED
+     *                      0x02 :EFDSTATUS_ERROR_ALREADY_STARTED
+     *                      0x03 :EFDSTATUS_ERROR_FEATURE_NOT_SUPPORTED
+     *                      0x04 :EFDSTATUS_ERROR_FEATURE_DISABLED_IN_CONFIG
+     *                      0x05 :EFDSTATUS_ERROR_NFC_IS_OFF
+     *                      0x06 :EFDSTATUS_ERROR_UNKNOWN
+     * <p>Requires {@link   android.Manifest.permission#NFC} permission.
+     */
+    public int startExtendedFieldDetectMode(int detectionTimeout) {
+      try {
+        return sNxpService.startExtendedFieldDetectMode(detectionTimeout);
+      } catch (RemoteException e) {
+        e.printStackTrace();
+        attemptDeadServiceRecovery(e);
+        return 0x06; /*EFDSTATUS_ERROR_UNKNOWN*/
+      }
+    }
+    /**
+     * This API stops extended field detect mode.
+     * @return status     :-0x00 :EFDSTATUS_SUCCESS
+     *                      0x01 :EFDSTATUS_FAILED
+     *                      0x05 :EFDSTATUS_ERROR_NFC_IS_OFF
+     *                      0x06 :EFDSTATUS_ERROR_UNKNOWN
+     *                      0x07 :EFDSTATUS_ERROR_NOT_STARTED
+     * <p>Requires {@link   android.Manifest.permission#NFC} permission.
+     */
+    public int stopExtendedFieldDetectMode() {
+      try {
+        return sNxpService.stopExtendedFieldDetectMode();
+      } catch (RemoteException e) {
+        e.printStackTrace();
+        attemptDeadServiceRecovery(e);
+        return 0x06; /*EFDSTATUS_ERROR_UNKNOWN*/
+      }
+    }
 }
diff --git a/sems/com/nxp/sems/SemsAgent.java b/sems/com/nxp/sems/SemsAgent.java
index 350bc08..4a15f35 100644
--- a/sems/com/nxp/sems/SemsAgent.java
+++ b/sems/com/nxp/sems/SemsAgent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2021 NXP
+ * Copyright 2019-2022 NXP
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,8 +39,11 @@
   public static final byte SEMS_STATUS_BUSY = 0x02;
   public static final byte SEMS_STATUS_DENIED = 0x03;
   public static final byte SEMS_STATUS_UNKNOWN = 0x0F;
+  public static final byte SEMS_STATUS_HASH_INVALID = 0x04;
+  public String SEMS_HASH_TYPE_SHA1   = "SHA1";
+  public String SEMS_HASH_TYPE_SHA256 = "SHA256";
   public static final short major = 1;
-  public static final short minor = 1;
+  public static final short minor = 3;
 
   private static final byte DEFAULT_TERMINAL_ID = 1;
   private static SemsAgent sInstance;
@@ -50,7 +53,6 @@
   private SemsExecutor mExecutor = null;
   public static Object semsObj = new Object();
   public static boolean flagSemsObj = false;
-
   /**
    * Returns SemsAgent singleton object
    * <br/>
@@ -63,14 +65,16 @@
     if (context == null) {
       throw new SemsException("Context information invalid/null");
     }
-    if (sInstance == null) {
-      synchronized (SemsAgent.class) { sInstance = new SemsAgent(); }
+    synchronized (SemsAgent.class) {
+      if (sInstance == null) {
+        sInstance = new SemsAgent();
+      }
+      if (context != sContext) {
+        sContext = context;
+      }
+      Log.d(TAG, "Sems Agent version " + major + "." + minor);
+      return sInstance;
     }
-    if (context != sContext) {
-      sContext = context;
-    }
-    Log.d(TAG, "Sems Agent version " + major + "." + minor);
-    return sInstance;
   }
 
   private SemsAgent() {}
@@ -199,4 +203,50 @@
       throw new SemsException("Unable to get last sems execute status");
     }
   }
+
+  /**
+   * Get Hash type of the algorithm execution.
+   * <br/>
+   * Returns response SHAType to Application
+   * From SEMS
+   *
+   * @return Hash algorithm type used by SEMS
+   * script.
+   */
+  public String GetHashAlgorithm() throws SemsException {
+    Log.d(TAG, "GetHashAlgorithm");
+    try {
+      mSemsApduChannel = SemsApduChannelFactory.getInstance(
+          SemsApduChannelFactory.OMAPI_CHANNEL, sContext, sTerminalID);
+      mExecutor = SemsExecutor.getInstance(mSemsApduChannel, sContext);
+      return mExecutor.getHashAlgorithm();
+    } catch (Exception e) {
+      throw new SemsException("Unable to get Hash type");
+    }
+  }
+
+  /**
+   * Set Hash type of the algorithm execution.
+   * <br/>
+   * Set response SHAType to Application
+   * From SEMS
+   * @param Hash algorithm type used by SEMS
+   * to set
+   * @return {@code status} 0 in SUCCESS, otherwise SEMS_STATUS_HASH_INVALID in failure
+   */
+  public int SetHashAlgorithm(String semsHashAlgoType) throws SemsException {
+    Log.d(TAG, "SetHashAlgorithm");
+    try {
+      if ((semsHashAlgoType != SEMS_HASH_TYPE_SHA1)
+          && (semsHashAlgoType != SEMS_HASH_TYPE_SHA256)) {
+        return SEMS_STATUS_HASH_INVALID;
+      }
+      mSemsApduChannel = SemsApduChannelFactory.getInstance(
+          SemsApduChannelFactory.OMAPI_CHANNEL, sContext, sTerminalID);
+      mExecutor = SemsExecutor.getInstance(mSemsApduChannel, sContext);
+      return mExecutor.setHashAlgorithm(semsHashAlgoType);
+    } catch (Exception e) {
+      throw new SemsException("Unable to set Hash type");
+    }
+  }
 }
diff --git a/sems/com/nxp/sems/channel/SemsApduChannelFactory.java b/sems/com/nxp/sems/channel/SemsApduChannelFactory.java
index db8170a..d64fc8e 100644
--- a/sems/com/nxp/sems/channel/SemsApduChannelFactory.java
+++ b/sems/com/nxp/sems/channel/SemsApduChannelFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 NXP
+ * Copyright 2019,2022 NXP
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,16 +29,14 @@
 
   public static ISemsApduChannel getInstance(byte type, Context context,
           byte terminalID) throws SemsException{
-    if(mChannelFactory == null) {
       synchronized (SemsApduChannelFactory.class) {
         if(mChannelFactory == null) {
           Log.d(TAG, "SemsApduChannelFactory Initialization");
           mChannelFactory = (ISemsApduChannel) SemsApduChannelFactory.
                                  createApduChannel(type, context, terminalID);
         }
+        return mChannelFactory;
       }
-    }
-    return mChannelFactory;
   }
 
   private SemsApduChannelFactory() {}
diff --git a/sems/com/nxp/sems/semscore/SemsAppletIdentifier.java b/sems/com/nxp/sems/semscore/SemsAppletIdentifier.java
new file mode 100644
index 0000000..57ff3b6
--- /dev/null
+++ b/sems/com/nxp/sems/semscore/SemsAppletIdentifier.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2021-2022 NXP
+ *
+ * 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.sems;
+
+import android.util.Log;
+import com.nxp.sems.SemsTLV;
+import com.nxp.sems.SemsUtil;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class SemsAppletIdentifier {
+  public static final String TAG = "SemsAppletIdentifier";
+  private static SemsTLV tlv5D, tlv5E;
+
+  /*Track line number in script Apdu.*/
+  private static int lineCounter;
+  private static boolean isTAG73Supported;
+  private static int tag73Len;
+  private static int commandNumber;
+  private static int delayInMillsec;
+
+  /*Strong Box Applet Identifier ASCII value*/
+  private static final byte[] SEMS_SB_APP_ID = SemsUtil.parseHexString("5374726F6E67426F78");
+
+  /**
+   * Validate TAG and subtag support.
+   * And, set flag as true if pre-condition meet.
+   * <br/>
+   * Sems Agent shall introduce delay on Nth Command.
+   * @param TLV certifacte in ScriptTLV.
+   *
+   * @return void.
+   */
+  protected static void validateTag73Support(SemsTLV tlvCertInScript) throws Exception {
+    Log.d(TAG, "***Initalize the variable to default values..**");
+    lineCounter = 0;
+    isTAG73Supported = false;
+    tlv5D = null;
+    tlv5E = null;
+    tag73Len = 0;
+    SemsTLV tlv5C, tlv73;
+    List<SemsTLV> tlvs = SemsTLV.parse(tlvCertInScript.getValue());
+    if (tlvs == null) {
+      Log.d(TAG, "tlvCertInScript null");
+      return;
+    }
+    tlv73 = SemsTLV.find(tlvs, 0x73);
+    if (tlv73 == null) {
+      Log.d(TAG, "tag73 is null");
+      return;
+    }
+    // Parse outer T-L-V TAG73 for inner tags 5C, 5D and 5E
+    tlvs = SemsTLV.parse(tlv73.getValue());
+    tlv5C = SemsTLV.find(tlvs, 0x5C);
+    if (tlv5C == null) {
+      Log.d(TAG, "tlv5C is null");
+      return;
+    }
+    if (!Arrays.equals(tlv5C.getValue(), SEMS_SB_APP_ID)) {
+      Log.d(TAG, "Is not SB Applet");
+      return;
+    }
+    tlv5D = SemsTLV.find(tlvs, 0x5D);
+    if (tlv5D == null) {
+      Log.d(TAG, "tlv5D is null");
+      return;
+    }
+    commandNumber = arrayToValue(tlv5D.getValue());
+    tlv5E = SemsTLV.find(tlvs, 0x5E);
+    if (tlv5E == null) {
+      Log.d(TAG, "tlv5E is null");
+      return;
+    }
+    delayInMillsec = arrayToValue(tlv5E.getValue());
+    if (delayInMillsec != 0x00) {
+      Log.d(TAG,
+          "***TAG 73 and sub-tag 5C,5D " + commandNumber + " 5E " + delayInMillsec
+              + " are supported.**");
+      isTAG73Supported = true;
+      return;
+    } else {
+      Log.d(TAG, "Invalid delayInMillsec");
+    }
+  }
+
+  /**
+   * No of command in the script before sending it to SEMS applet,
+   * introduce delay (Delay in msec).
+   * <br/>
+   *
+   * @return void.
+   */
+  protected static void delayNthCommand() {
+    try {
+      if (isTAG73Supported) {
+        lineCounter++;
+        if (lineCounter == commandNumber) {
+          Log.d(TAG, " Add Delay " + delayInMillsec);
+          TimeUnit.MILLISECONDS.sleep(delayInMillsec);
+        }
+      }
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Get TAG 73 Length(Inclusive of TAG and Length field).
+   * <br/>
+   *
+   * @return int: TAG73 Length.
+   */
+  protected static int getTag73Len() {
+    /*Include TAG & LENGTH field 2 bytes*/
+    return (isTAG73Supported ? (tag73Len + 2) : 0);
+  }
+
+  /**
+   * Calculate integer from byte array.
+   * <br/>
+   *
+   * @return int: summed up array value
+   */
+  protected static int arrayToValue(byte[] arr) {
+    int temp = 0;
+    int len = arr.length;
+    /*Length cannot be more than 4 bytes*/
+    if (len > 4)
+      return 0;
+
+    if (len == 1)
+      temp = (arr[0] & 0xFF);
+    else if (len == 2)
+      temp = (arr[0] << 8) & 0xFF00 | (arr[1] & 0xFF);
+    else if (len == 3)
+      temp = (arr[0] << 16) & 0xFF0000 | (arr[1] << 8) & 0xFF00 | (arr[2] & 0xFF);
+    else if (len == 4)
+      temp = (arr[0] << 24) | (arr[1] << 16) & 0xFF0000 | (arr[2] << 8) & 0xFF00 | (arr[3] & 0xFF);
+
+    return temp;
+  }
+}
diff --git a/sems/com/nxp/sems/semscore/SemsExecutor.java b/sems/com/nxp/sems/semscore/SemsExecutor.java
index 5cc7ec9..fe1ab11 100644
--- a/sems/com/nxp/sems/semscore/SemsExecutor.java
+++ b/sems/com/nxp/sems/semscore/SemsExecutor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2021 NXP
+ * Copyright 2019-2022 NXP
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,26 +16,20 @@
 
 package com.nxp.sems;
 
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Vector;
-
+import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.util.Log;
+import com.nxp.sems.ISemsCallback;
+import com.nxp.sems.SemsFileOperation;
+import com.nxp.sems.SemsGetLastExecStatus;
+import com.nxp.sems.SemsStatus;
 import com.nxp.sems.SemsTLV;
 import com.nxp.sems.SemsUtil;
 import com.nxp.sems.channel.ISemsApduChannel;
-import com.nxp.sems.ISemsCallback;
-import com.nxp.sems.SemsStatus;
-import com.nxp.sems.SemsGetLastExecStatus;
-import android.content.Context;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 
 public class SemsExecutor {
 
@@ -51,18 +45,19 @@
 
   public static final byte SEMS_CERTIFICATE_SIGNATURE_5F37_LEN = 64;
   public static final byte SEMS_CERTIFICATE_SIGNATURE_7F49_86_LEN = 67;
+  public static final short MAX_FRAME_SIZE = 255;
+  public static final int TAG73 = 0x73;
+  public static final int TAG5F37 = 0x5F37;
 
   private static ISemsApduChannel sChannel;
   private final byte basicChannel = 0x00;
-  private String callerPackageName;
-  private static String sRespOutlog;
+  private String shatype;
   private byte[] AID_MEM;
-  private String encryptedScriptDirectory = "";
-  private String outDirectory = "";
+  private ISemsCallback mSemsCallback;
+  private static Context sContext;
+  private SemsFileOperation mSemsFileOp;
   private String inputScript;
   private String outputScript;
-  private ISemsCallback mSemsCallback;
-  private static Context mContext;
 
   private int certIndex = -1;
   private byte[] rapduSelect;
@@ -73,6 +68,8 @@
   private boolean lsCommandSeen = false;
   private byte mState;
   private static SemsExecutor sSemsExecutor;
+  public String SEMS_HASH_TYPE_SHA1   = "SHA1";
+  public String SEMS_HASH_TYPE_SHA256 = "SHA256";
   /**
    * AID of the SEMS Application Instance.
    */
@@ -106,8 +103,7 @@
   public static SemsExecutor getInstance(ISemsApduChannel semsChannel,
                                          Context context) {
     sChannel = semsChannel;
-    mContext = context;
-    sRespOutlog = "";
+    sContext = context;
     if (sSemsExecutor == null) {
       sSemsExecutor = new SemsExecutor();
     }
@@ -115,150 +111,47 @@
   }
   private SemsExecutor() {
     this.AID_MEM = SEMS_APP_AID;
+    this.shatype = SEMS_HASH_TYPE_SHA1;
+    mSemsFileOp = new SemsFileOperation();
   }
 
   /**
-   * Logging the response APDU received during SEMS execution
+   * Set Hash type of the algorithm execution.
    * <br/>
-   * The Input type of response,
-   * The Input status bytes
-   * Agent to provide the SEMS Application with an identifier
-   * @param void
-   *
-   * @return {@code true} if the SW returned is 9000, {@code false} otherwise.
+   * @param Hash algorithm type used by SEMS
+   * to set
+   * @return {@code status} 0 in SUCCESS, otherwise -1 in failure
    */
-  private void putIntoLog(byte[] what, byte type) {
-    byte[] data;
-
-    /*Skip responses ending with SW '6310'*/
-    if ((what[what.length - 2] == 0x63) && (what[what.length - 1] == 0x10)) {
-      return;
-    }
-
-    switch (type) {
-    case ErrorResponse:
-      return;
-    default:
-      return;
-
-    case SemsCertResponse:
-      data = new byte[] {0x7F, 0x21};
-      break;
-    case SemsAuthResponse:
-      data = new byte[] {0x60};
-      break;
-    case SemsResponse:
-      data = new byte[] {0x40};
-      break;
-    }
-
-    data = SemsTLV.make(0x61, SemsUtil.append(SemsTLV.make(0x43, data),
-                                              SemsTLV.make(0x44, what)));
-    sRespOutlog = sRespOutlog + SemsUtil.toHexString(data) + "\r\n";
+  public synchronized int setHashAlgorithm(String shaType) {
+    this.shatype = shaType;
+    return 0;
   }
 
   /**
-   * Set the current application directory & caller information
+   * Get Hash type of the algorithm execution.
    * <br/>
-   * The Input Logical channel,
-   * The Input the AID to be selected
-   * Agent to provide the SEMS Application with an identifier
-   * @param void
+   * Returns response SHAType to Application
+   * From SEMS
    *
-   * @return {@code true} if the SW returned is 9000, {@code false} otherwise.
+   * @return SHAType use for Hash
+   * script.
    */
-  private SemsStatus setDirectories() {
-    SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
-    PackageInfo pInfo;
-    PackageManager pm = mContext.getPackageManager();
-    String str = mContext.getPackageName();
-
-    try {
-      pInfo = pm.getPackageInfo(str, 0);
-      str = pInfo.applicationInfo.dataDir;
-      this.callerPackageName = pInfo.packageName;
-      this.encryptedScriptDirectory = str;
-      this.outDirectory = str;
-      status = SemsStatus.SEMS_STATUS_SUCCESS;
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-    return status;
-  }
-
-  private static Path getPath(String dir, String file) {
-    return (dir != null) ? FileSystems.getDefault().getPath(dir, file)
-                         : FileSystems.getDefault().getPath(file);
-  }
-  /**
-   * Write the content of String buffer to out file
-   * <br/>
-   * The Input Logical channel,
-   * The Input the AID to be selected
-   * Agent to provide the SEMS Application with an identifier
-   * @param void
-   *
-   * @return {@code true} if the SW returned is 9000, {@code false} otherwise.
-   */
-  private byte[] writeScriptOutFile(String scriptOut) {
-    Path p = getPath(outDirectory, scriptOut);
-    try {
-      Files.write(p, sRespOutlog.getBytes());
-    } catch (IOException e) {
-      Log.e(TAG, "IOException during writeScriptOutfile: ");
-    }
-    return sRespOutlog.getBytes();
+  public synchronized String getHashAlgorithm() {
+    return shatype;
   }
 
   /**
-   * Write the content of String buffer to backup file
+   * Check Hash type of the algorithm execution.
    * <br/>
-   * The Input Logical channel,
-   * The Input the AID to be selected
-   * Agent to provide the SEMS Application with an identifier
-   * @param void
+   * Returns response True if shatype is SHA256
    *
-   * @return {@code true} if the SW returned is 9000, {@code false} otherwise.
-   */
-  private byte[] writeScriptInputFile(String filename, String scriptBuffer) {
-    Path p = getPath(outDirectory, filename);
-    try {
-      Files.write(p, scriptBuffer.getBytes());
-    } catch (IOException e) {
-      Log.e(TAG, "IOException during writeScriptInputfile: ");
-    }
-    return scriptBuffer.getBytes();
-  }
-
-  /**
-   * Read the file content to String format
-   * <br/>
-   * The Input path of the SEMS encrypted script stored,
-   * Agent to provide the SEMS Application with an identifier
-   * @param void
-   *
-   * @return {@code true} if the SW returned is 9000, {@code false} otherwise.
+   * @return boolean TRUE for SHA256 ShaHash type
    */
 
-  private String readScriptFile(String scriptIn) throws Exception {
-    Path p = getPath(encryptedScriptDirectory, scriptIn);
-    String script = "";
-    Iterator<String> i;
-    try {
-      List<String> lines = Files.readAllLines(p, Charset.defaultCharset());
-      i = lines.iterator();
-      while (i.hasNext()) {
-        String s = i.next();
-        if (!s.startsWith("%%%")) {
-          script += s;
-        }
-      }
-    } catch (IOException e) {
-      Log.e(TAG, "IOException during reading script: ");
-      throw new Exception();
-    }
-    return script;
+  private synchronized boolean isSemsHashAlgoSHA256() {
+    return (shatype == SEMS_HASH_TYPE_SHA256);
   }
+
   /**
    * Close the session with Application selected
    * <br/>
@@ -319,15 +212,37 @@
    */
   private byte[] sendSHA1OfCallerPackage(byte channel, byte[] callerPackage) throws Exception{
     byte[] SHA1ofCallerPackage = SemsUtil.SHA1(callerPackage);
+
     final byte[] header = {(byte)0x80, (byte)0xE2, (byte)0x00, 0x00,
                            (byte)0x16, (byte)0x4F, (byte)0x14};
-    Log.d(TAG, "Register Caller: ");
+    Log.d(TAG, "Register Caller for SHA1: "+ SHA1ofCallerPackage.length);
     return sChannel.transmit(SemsUtil.append(header, SHA1ofCallerPackage));
   }
 
   /**
    * Performs store data operation of caller information.
    * <br/>
+   * The Input SHA-256 digest of caller package name,
+   * The STORE DATA APDU command is used by the SEMS Device
+   * Agent to provide the SEMS Application with an identifier
+   * of the caller (SP Device Application), e.g.
+   * the digest value of the SP Device Application package name
+   * @param void
+   *
+   * @return {@code true} if the SW returned is 9000, {@code false} otherwise.
+   */
+  private byte[] sendSHA256OfCallerPackage(byte channel, byte[] callerPackage) throws Exception{
+    byte[] SHA256ofCallerPackage = SemsUtil.SHA256(callerPackage);
+    final byte[] header = {(byte)0x80, (byte)0xE2, (byte)0x00, 0x00,
+                           (byte)0x22, (byte)0x4F, (byte)0x20};
+    Log.d(TAG, "Register Caller for SHA256: "+ SHA256ofCallerPackage.length);
+    return sChannel.transmit(SemsUtil.append(header, SHA256ofCallerPackage));
+  }
+
+
+  /**
+   * Performs store data operation of caller information.
+   * <br/>
    * The Input SHA-1 digest of caller package name,
    * The STORE DATA APDU command is used by the SEMS Device
    * Agent to provide the SEMS Application with an identifier
@@ -339,7 +254,8 @@
    */
   private byte[] sendAPCertificate(byte channel, byte[] APCert) {
     try {
-      if (APCert.length < 255) {
+      SemsAppletIdentifier.delayNthCommand();
+      if (APCert.length < MAX_FRAME_SIZE) {
         // One command
         byte[] header = {(byte)0x80, (byte)0xA0, (byte)0x01, (byte)0x00};
         byte[] len = new byte[1];
@@ -348,45 +264,83 @@
         byte[] command = SemsUtil.append(SemsUtil.append(header, len), APCert);
 
         Log.d(TAG, "******* Processing LS Certificate 1/1 command");
+
         byte[] rapdu = sChannel.transmit(command);
-        putIntoLog(rapdu, SemsCertResponse);
+        mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
         return rapdu;
-      } else {
-        // Two commands
+      } else { // Two/Three commands based on tag length
         byte[] rapdu;
-        /* static length because of Brainpool curve*/
-        int signAndPubKeyLen = SEMS_CERTIFICATE_SIGNATURE_5F37_LEN +
-                SEMS_CERTIFICATE_SIGNATURE_7F49_86_LEN + 6;
+        byte[] commandHeader = {(byte) 0x80, (byte) 0xA0, (byte) 0x01, (byte) 0x00};
+        byte[] cmdLen = new byte[1];
+        int tag73Offset = 0, tag5F37Offset = 0;
 
-        byte[] firstCommandData =
-            Arrays.copyOfRange(APCert, 0, APCert.length - signAndPubKeyLen);
-        byte[] secondCommandData = Arrays.copyOfRange(
-            APCert, APCert.length - signAndPubKeyLen, APCert.length);
+        int signAndPubKeyLen;
 
-        byte[] firstHeader = {(byte)0x80, (byte)0xA0, (byte)0x01, (byte)0x00};
-        byte[] secondHeader = {(byte)0x80, (byte)0xA0, (byte)0x00, (byte)0x00};
+        /*If TAG73 does not exists*/
+        if (SemsAppletIdentifier.getTag73Len() == 0) {
+          /* static length because of Brainpool curve*/
+          signAndPubKeyLen =
+              (SEMS_CERTIFICATE_SIGNATURE_5F37_LEN + SEMS_CERTIFICATE_SIGNATURE_7F49_86_LEN + 6);
+        } else {
+          tag73Offset = SemsTLV.getTagOffset(APCert, TAG73);
+          tag5F37Offset = SemsTLV.getTagOffset(APCert, TAG5F37);
+          /* Length of remaining frame from TAG73 onwards*/
+          signAndPubKeyLen = (APCert.length - tag73Offset);
+        }
 
-        byte[] firstLen = new byte[1];
-        byte[] secondLen = new byte[1];
+        /*First frame till either start of (TAG73 or TAG5F37)*/
+        byte[] commandData = Arrays.copyOfRange(APCert, 0, APCert.length - signAndPubKeyLen);
+        cmdLen[0] = (byte) commandData.length;
 
-        firstLen[0] = (byte)firstCommandData.length;
-        secondLen[0] = (byte)secondCommandData.length;
+        byte[] firstCommand = SemsUtil.append(SemsUtil.append(commandHeader, cmdLen), commandData);
 
-        byte[] firstCommand = SemsUtil.append(
-            SemsUtil.append(firstHeader, firstLen), firstCommandData);
-        byte[] secondCommand = SemsUtil.append(
-            SemsUtil.append(secondHeader, secondLen), secondCommandData);
-
-        Log.d(TAG, "******* Processing LS Certificate 1/2 command");
+        Log.d(TAG,
+            "******* Processing LS Certificate 1st Frame APCert " + APCert.length
+                + " signAndPubKeyLen " + signAndPubKeyLen + " tag73Offset " + tag73Offset
+                + " tag5F37Offset " + tag5F37Offset);
         rapdu = sChannel.transmit(firstCommand);
-        if (SemsUtil.getSW(rapdu) != (short)0x9000) {
-          putIntoLog(rapdu, SemsCertResponse);
+        mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
+        if (SemsUtil.getSW(rapdu) != (short) 0x9000) {
           return Arrays.copyOfRange(rapdu, rapdu.length - 2, rapdu.length);
         }
 
-        Log.d(TAG, "******* Processing LS Certificate 2/2 command");
-        rapdu = sChannel.transmit(secondCommand);
-        putIntoLog(rapdu, SemsCertResponse);
+        /*If the second frame is greater than 255*/
+        if (signAndPubKeyLen > MAX_FRAME_SIZE) {
+          /*2nd Frame shall be only TAG73*/
+          commandData = Arrays.copyOfRange(APCert, (tag73Offset), (tag5F37Offset));
+          cmdLen[0] = (byte) (commandData.length);
+          commandHeader[2] = 0x00;
+          byte[] secondCommand =
+              SemsUtil.append(SemsUtil.append(commandHeader, cmdLen), commandData);
+          Log.d(TAG, "******* Processing LS Certificate 2nd/3 Frame cmdLen " + commandData.length);
+          rapdu = sChannel.transmit(secondCommand);
+          mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
+          if (SemsUtil.getSW(rapdu) != (short) 0x9000) {
+            return Arrays.copyOfRange(rapdu, rapdu.length - 2, rapdu.length);
+          }
+
+          /*3rd Frame shall be remaining from TAG5F37 onwards till end*/
+          commandData = Arrays.copyOfRange(APCert, tag5F37Offset, APCert.length);
+          cmdLen[0] = (byte) (commandData.length);
+
+          byte[] thirdCommand =
+              SemsUtil.append(SemsUtil.append(commandHeader, cmdLen), commandData);
+
+          Log.d(TAG, "******* Processing LS Certificate 3rd/3 Frame");
+          rapdu = sChannel.transmit(thirdCommand);
+          mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
+        } else {
+          /*2nd Frame shall be remaining (TAG73  or TAG5F37) onwards*/
+          commandData = Arrays.copyOfRange(APCert, APCert.length - signAndPubKeyLen, APCert.length);
+          cmdLen[0] = (byte) commandData.length;
+          commandHeader[2] = 0x00;
+          byte[] secondCommand =
+              SemsUtil.append(SemsUtil.append(commandHeader, cmdLen), commandData);
+
+          Log.d(TAG, "******* Processing LS Certificate 2nd Frame");
+          rapdu = sChannel.transmit(secondCommand);
+          mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
+        }
 
         return Arrays.copyOfRange(rapdu, rapdu.length - 2, rapdu.length);
       }
@@ -463,7 +417,7 @@
     } catch (IOException e) {
       e.printStackTrace();
     }
-    putIntoLog(rapdu, SEResponse);
+    mSemsFileOp.putIntoLog(rapdu, SEResponse);
     return rapdu;
   }
 
@@ -509,8 +463,8 @@
   public String getSemsOutputResponse(String fileName) throws Exception {
     SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
     Log.d(TAG, "******* Read response output APDU data");
-    status = setDirectories();
-    return readScriptFile(fileName);
+    status = mSemsFileOp.setDirectories(SemsExecutor.sContext);
+    return mSemsFileOp.readScriptFile(fileName);
   }
 
   /**
@@ -526,12 +480,12 @@
   public SemsStatus executeScript(String scriptIn, String scriptOut,
                                     ISemsCallback callback) {
     SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
-    this.inputScript = scriptIn;
-    this.outputScript = scriptOut;
+    inputScript = scriptIn;
+    outputScript = scriptOut;
     this.mSemsCallback = callback;
-    status = setDirectories();
+    status = mSemsFileOp.setDirectories(SemsExecutor.sContext);
     if (status == SemsStatus.SEMS_STATUS_SUCCESS) {
-      writeScriptInputFile("encrypted_script.txt", scriptIn);
+      mSemsFileOp.writeScriptInputFile("encrypted_script.txt", scriptIn);
       new SemsAsyncExecutor().start();
     } else {
       Log.e(TAG, "Setting SEMS script path failed, package not found");
@@ -550,22 +504,20 @@
    * @return {@code true} if the SW returned is 9000, {@code false} otherwise.
    */
   private void executeScript() {
-
-    String scriptIn = this.inputScript;
-    String scriptOut = this.outputScript;
+    String scriptIn = inputScript;
+    String scriptOut = outputScript;
     byte[] swReturned = sw9000;
+    response = sw6987;
     byte channelNumber = 0;
     SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
     mState = SEMS_STATE_SELECT;
     try {
-
       /*To handle If input is given as path
       String script = readScriptFile(scriptIn);*/
       String script = scriptIn;
       byte[] data = SemsUtil.parseHexString(script);
       if (data == null) {
-        putIntoLog(sw6987, ErrorResponse);
-        updateSemsStatus(sw6987);
+        mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
         Log.e(TAG, ">>>>>>>>>> parseHexString returned NULL <<<<<<<<<<");
         return;
       }
@@ -574,8 +526,7 @@
       byte[] rapdu;
 
       if ((scriptTlvs == null) || scriptTlvs.size() == 0) {
-        putIntoLog(sw6987, ErrorResponse);
-        updateSemsStatus(sw6987);
+        mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
         Log.e(TAG, ">>>>>>>>>> Error : Script size 0 <<<<<<<<<<");
         return;
       }
@@ -593,8 +544,7 @@
           case SEMS_STATE_SELECT: {
             status = SelectSems();
             if (status != SemsStatus.SEMS_STATUS_SUCCESS) {
-              closeLogicalChannel(channelNumber);
-              updateSemsStatus(sw6987);
+              response = sw6987;
               return;
             }
           }
@@ -603,17 +553,26 @@
             /*
              * STEP 3 of executeScript - Sending SHA1 of Caller package
              */
-            rapdu = sendSHA1OfCallerPackage(channelNumber,
-                                            callerPackageName.getBytes());
+            synchronized (SemsExecutor.this) {
+              if (shatype == "SHA256") {
+                synchronized (SemsFileOperation.class) {
+                  rapdu = sendSHA256OfCallerPackage(
+                      channelNumber, mSemsFileOp.mCallerPackageName.getBytes());
+                }
+              } else {
+                synchronized (SemsFileOperation.class) {
+                  rapdu = sendSHA1OfCallerPackage(
+                      channelNumber, mSemsFileOp.mCallerPackageName.getBytes());
+               }
+              }
+            }
             if (rapdu == null) {
               Log.e(TAG, "sendSHA1OfCallerPackage received incorrect rapdu");
-              closeLogicalChannel(channelNumber);
-              updateSemsStatus(rapdu);
+              response = rapdu;
               return;
             }
-            if (SemsUtil.getSW(rapdu) != (short)0x9000) {
-              closeLogicalChannel(channelNumber);
-              updateSemsStatus(rapdu);
+            if (SemsUtil.getSW(rapdu) != (short) 0x9000) {
+              response = rapdu;
               return;
             }
           }
@@ -650,23 +609,23 @@
          */
       } catch (Exception e) {
         e.printStackTrace();
-        putIntoLog(sw6F00, ErrorResponse);
-        closeLogicalChannel(channelNumber);
-        updateSemsStatus(sw6F00);
+        mSemsFileOp.putIntoLog(sw6F00, ErrorResponse);
+        response = sw6F00;
         return;
       }
-      putIntoLog(response, SWResponse);
-      closeLogicalChannel(channelNumber);
-      updateSemsStatus(response);
+      mSemsFileOp.putIntoLog(response, SWResponse);
       return;
     }catch(Exception e) {
-      putIntoLog(sw6987, ErrorResponse);
-      updateSemsStatus(sw6987);
-      Log.e(TAG, ">>>>>>>>>> Error : Invalid Script <<<<<<<<<<");
+      mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
+      response = sw6987;
+      Log.e(TAG, ">>>>>>>>>> Error : Invalid Script <<<<<<<<<< ");
+      e.printStackTrace();
       return;
     } finally {
+      closeLogicalChannel(channelNumber);
+      updateSemsStatus(response);
       /*Always write log file*/
-      writeScriptOutFile(scriptOut);
+      mSemsFileOp.writeScriptOutFile(scriptOut);
     }
   }
 
@@ -703,11 +662,11 @@
 
         if (secureCommand.getLength() > 4 /* DDD: was 32 */) {
           byte[] secCmd = secureCommand.getValue();
-
+          SemsAppletIdentifier.delayNthCommand();
           rapdu = sendProcessScript(channelNumber, secCmd);
           if (rapdu == null) {
             Log.e(TAG, "sendProcessScript received incorrect rapdu");
-            putIntoLog(sw6987, ErrorResponse);
+            mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
             rapdu = sw6987;
             break;
           }
@@ -716,37 +675,37 @@
             /*
              * STEP 8 of executeScript - Process SE Response
              */
-            putIntoLog(rapdu, SemsResponse);
+            mSemsFileOp.putIntoLog(rapdu, SemsResponse);
 
             if ((rapdu = sendToSE(rapdu)) == null) {
-              putIntoLog(sw6987, ErrorResponse);
+              mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
               rapdu = sw6987;
               break;
             }
-            putIntoLog(rapdu, SEResponse);
+            mSemsFileOp.putIntoLog(rapdu, SEResponse);
             {
               rapdu = sendProcessSEResponse(channelNumber, rapdu);
-              putIntoLog(rapdu, SemsResponse);
+              mSemsFileOp.putIntoLog(rapdu, SemsResponse);
 
               while (SemsUtil.getSW(rapdu) == (short)0x6310) {
 
                 if ((rapdu = sendToSE(rapdu)) == null) {
-                  putIntoLog(sw6987, ErrorResponse);
+                  mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
                   rapdu = sw6987;
                   break;
                 }
-                putIntoLog(rapdu, SEResponse);
+                mSemsFileOp.putIntoLog(rapdu, SEResponse);
                 rapdu = sendProcessSEResponse(channelNumber, rapdu);
-                putIntoLog(rapdu, SemsResponse);
+                mSemsFileOp.putIntoLog(rapdu, SemsResponse);
               }
             }
             if ((SemsUtil.getSW(rapdu) != (short)0x9000) &&
                 (SemsUtil.getSW(rapdu) != (short)0x6300)) {
-              putIntoLog(rapdu, ErrorResponse);
+              mSemsFileOp.putIntoLog(rapdu, ErrorResponse);
               break;
             }
           } else if (sw == (short)0x6320) {
-            putIntoLog(rapdu, SemsResponse);
+            mSemsFileOp.putIntoLog(rapdu, SemsResponse);
             closeLogicalChannel(channelNumber);
             AID_MEM = SemsUtil.getRDATA(rapdu);
             Log.d(TAG, "Received new AID need to switch");
@@ -771,14 +730,14 @@
             /*continue restart_execute_script;*/
             break;
           } else if ((sw != (short)0x9000) && (sw != (short)0x6300)) {
-            putIntoLog(rapdu, SemsResponse);
+            mSemsFileOp.putIntoLog(rapdu, SemsResponse);
             break;
           } else {
-            putIntoLog(rapdu, SemsResponse);
+            mSemsFileOp.putIntoLog(rapdu, SemsResponse);
           }
         } else {
           Log.e(TAG, "Invalid length for secure command");
-          putIntoLog(sw6987, ErrorResponse);
+          mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
           rapdu = sw6987;
           break;
         }
@@ -786,7 +745,7 @@
                  !(secureCommand.getTLV()[0] == (byte)0x7F &&
                    secureCommand.getTLV()[1] == (byte)0x21)) {
         Log.e(TAG, "Invalid tag found secure script");
-        putIntoLog(sw6987, ErrorResponse);
+        mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
         rapdu = sw6987;
         break;
       } else if (lsCommandSeen) {
@@ -803,7 +762,7 @@
          * there may be an issue in the LS Applet)*/
         rapdu = selectApplication(channelNumber, AID_MEM);
         if(rapdu == null) {
-          putIntoLog(sw6987, ErrorResponse);
+          mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
           rapdu = sw6987;
           break;
         }
@@ -855,7 +814,11 @@
       if (scriptTlvs.get(i).getTag() == 0x7F21) {
         certIndex = i;
         tlvCertInScript = scriptTlvs.get(i);
-
+        try {
+          SemsAppletIdentifier.validateTag73Support(tlvCertInScript);
+        } catch (Exception e) {
+          Log.e(TAG, "Exception in parsing TAG73 in CERT frame");
+        }
         byte[] cert = tlvCertInScript.getTLV();
         tlvs = SemsTLV.parse(tlvCertInScript.getValue());
         tlvSC42 = SemsTLV.find(tlvs, 0x42);
@@ -919,22 +882,19 @@
       rapdu = sendAPCertificate(channelNumber, APCert);
       if (rapdu == null) {
         Log.e(TAG, "sendAPCertificate received incorrect rapdu");
-        closeLogicalChannel(channelNumber);
-        updateSemsStatus(rapdu);
+        response = rapdu;
         return SemsStatus.SEMS_STATUS_FAILED;
       }
       if (SemsUtil.getSW(rapdu) != (short)0x9000) {
         Log.e(TAG, "certificate frame command failed");
-        putIntoLog(rapdu, ErrorResponse);
-        closeLogicalChannel(channelNumber);
-        updateSemsStatus(rapdu);
+        mSemsFileOp.putIntoLog(rapdu, ErrorResponse);
+        response = rapdu;
         stat = SemsStatus.SEMS_STATUS_FAILED;
       }
     } else {
       Log.e(TAG, "No valid certificate frame found");
-      closeLogicalChannel(channelNumber);
-      putIntoLog(sw6987, ErrorResponse);
-      updateSemsStatus(sw6987);
+      mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
+      response = sw6987;
     }
     Log.d(TAG, "Exit Certificate frame validation");
     return stat;
@@ -1039,57 +999,52 @@
 
     if (authFrame.getTag() != 0x60) {
       Log.e(TAG, "Authentication frame not found");
-      closeLogicalChannel(channelNumber);
-      putIntoLog(sw6987, ErrorResponse);
-      updateSemsStatus(sw6987);
+      mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
+      response = sw6987;
       return stat;
     }
     authFrame = SemsTLV.parse(authFrame.getValue()).get(0);
+    SemsAppletIdentifier.delayNthCommand();
     rapdu = sendAuthenticationFrame(channelNumber, authFrame.getValue());
     if (rapdu == null) {
       Log.e(TAG, "sendAuthenticationFrame received incorrect rapdu");
-      closeLogicalChannel(channelNumber);
-      putIntoLog(sw6987, ErrorResponse);
+      mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
       updateSemsStatus(sw6987);
       return stat;
     }
-    putIntoLog(rapdu, SemsAuthResponse);
+    mSemsFileOp.putIntoLog(rapdu, SemsAuthResponse);
 
     if (SemsUtil.getSW(rapdu) == (short)0x6310) { // begin_perso cleanup
       int i = 0;
 
       if ((rapdu = sendToSE(rapdu)) == null) {
-        putIntoLog(sw6987, ErrorResponse);
-        closeLogicalChannel(channelNumber);
-        updateSemsStatus(sw6987);
+        mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
+        response = sw6987;
         return stat;
       }
-      putIntoLog(rapdu, SEResponse);
+      mSemsFileOp.putIntoLog(rapdu, SEResponse);
       rapdu = sendProcessSEResponse(channelNumber, rapdu);
-      putIntoLog(rapdu, SemsResponse);
+      mSemsFileOp.putIntoLog(rapdu, SemsResponse);
       while (SemsUtil.getSW(rapdu) == (short)0x6310) {
 
         if ((rapdu = sendToSE(rapdu)) == null) {
-          putIntoLog(sw6987, ErrorResponse);
-          closeLogicalChannel(channelNumber);
-          updateSemsStatus(sw6987);
+          mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
+          response = sw6987;
           return stat;
         }
-        putIntoLog(rapdu, SEResponse);
+        mSemsFileOp.putIntoLog(rapdu, SEResponse);
         rapdu = sendProcessSEResponse(channelNumber, rapdu);
-        putIntoLog(rapdu, SemsResponse);
+        mSemsFileOp.putIntoLog(rapdu, SemsResponse);
 
         i++;
       }
     } else if (SemsUtil.getSW(rapdu) != (short)0x9000) {
       Log.e(TAG, "Processing Authentication frame failed");
-      putIntoLog(rapdu, SemsResponse);
-      closeLogicalChannel(channelNumber);
-      updateSemsStatus(rapdu);
-      return stat;
+      mSemsFileOp.putIntoLog(rapdu, SemsResponse);
     } else if (SemsUtil.getSW(rapdu) == (short)0x9000) {
       stat = SemsStatus.SEMS_STATUS_SUCCESS;
     }
+    response = rapdu;
     return stat;
   }
 
@@ -1134,7 +1089,7 @@
     }
     if (this.mSemsCallback != null) {
       this.mSemsCallback.onSemsComplete(updateStatus);
-      this.mSemsCallback.onSemsComplete(updateStatus, sRespOutlog);
+      this.mSemsCallback.onSemsComplete(updateStatus, mSemsFileOp.getRespOutLog());
     }
   }
   /**
diff --git a/sems/com/nxp/sems/semscore/SemsFileOperation.java b/sems/com/nxp/sems/semscore/SemsFileOperation.java
new file mode 100644
index 0000000..c695c4a
--- /dev/null
+++ b/sems/com/nxp/sems/semscore/SemsFileOperation.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2022 NXP
+ *
+ * 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.sems;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+public class SemsFileOperation {
+  public static final String TAG = "SEMS-SemsFileOperation";
+
+  private String mRespOutlog;
+  private String mEncryptedScriptDirectory = "";
+  private String mOutDirectory = "";
+  public String mCallerPackageName;
+
+  private static final byte SEMS_RESPONSE = 0x01;
+  //private static final byte SEResponse = 0x02;
+  private static final byte ERROR_RESPONSE = 0x03;
+  //private static final byte SWResponse = 0x04;
+  private static final byte SEMS_CERT_RESPONSE = 0x05;
+  private static final byte SEMS_AUTH_RESPONSE = 0x06;
+  private static final byte SEMS_RESPONSE_LOG_TAG = 0x61;
+  private static final byte SEMS_FRAME_TYPE_TAG = 0x44;  // (CERT/AUTH/SECURE_CMD frame)
+  private static final byte SEMS_RESPONSE_DATA_TAG = 0x43;
+
+  /**
+   * SemsFileOperation Constructor
+   * <br/>
+   * @param void
+   *
+   * @return void.
+   */
+  SemsFileOperation() {
+    // Update start time stamp at beginning
+    mRespOutlog = getCurrentTimeStamp();
+  }
+
+  /**
+   * Get current date and time stamp
+   * <br/>
+   * Used in response log for debug purpose
+   * @param void
+   *
+   * @return String current data and time stamp.
+   */
+  private String getCurrentTimeStamp() {
+    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
+    LocalDateTime now = LocalDateTime.now();
+    return "#######" + dtf.format(now) + "#######"
+        + "\r\n";
+  }
+
+  /**
+   * Logging the response APDU received during SEMS execution
+   * <br/>
+   * Agent to provide the SEMS Application with an identifier
+   * @param byte[] what The Input status bytes
+   *        Byte type The Input type of response
+   *
+   * @return void.
+   */
+  public void putIntoLog(byte[] what, byte type) {
+    byte[] data;
+
+    /*Skip SW "6310" as this is not relevant for debug */
+    if ((what[what.length - 2] == 0x63) && (what[what.length - 1] == 0x10)) {
+      return;
+    }
+
+    switch (type) {
+      case ERROR_RESPONSE:
+        return;
+      default:
+        return;
+      case SEMS_CERT_RESPONSE:
+        data = new byte[] {0x7F, 0x21};
+        break;
+      case SEMS_AUTH_RESPONSE:
+        data = new byte[] {0x60};
+        break;
+      case SEMS_RESPONSE:
+        data = new byte[] {0x40};
+        break;
+    }
+
+    data = SemsTLV.make(SEMS_RESPONSE_LOG_TAG,
+        SemsUtil.append(SemsTLV.make(SEMS_RESPONSE_DATA_TAG, data),
+        SemsTLV.make(SEMS_RESPONSE_DATA_TAG, what)));
+    mRespOutlog = mRespOutlog + SemsUtil.toHexString(data) + "\r\n";
+  }
+
+  /**
+   * Set the current application directory & caller information
+   * <br/>
+   * @param context caller application context info
+   *
+   * @return {@code SemsStatus} returns SEMS_STATUS_SUCCESS on success
+   * otherwise SEMS_STATUS_FAILED.
+   */
+  public SemsStatus setDirectories(Context context) {
+    SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
+    PackageInfo pInfo;
+    PackageManager pm = context.getPackageManager();
+    String str = context.getPackageName();
+    synchronized (SemsFileOperation.class) {
+      try {
+        pInfo = pm.getPackageInfo(str, 0);
+        str = pInfo.applicationInfo.dataDir;
+        mCallerPackageName = pInfo.packageName;
+        mEncryptedScriptDirectory = str;
+        mOutDirectory = str;
+        status = SemsStatus.SEMS_STATUS_SUCCESS;
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    return status;
+  }
+
+  /**
+   * Get path to locate and access file
+   * <br/>
+   * @param String dir initial part of path string
+   *        String file filename in the path
+   *
+   * @return Path The resulting path.
+   */
+  public Path getPath(String dir, String file) {
+    return (dir != null) ? FileSystems.getDefault().getPath(dir, file)
+                         : FileSystems.getDefault().getPath(file);
+  }
+
+  /**
+   * Write the content of accumulated response buffer to out file
+   * <br/>
+   * Agent to provide the SEMS Application with an identifier
+   * @param String scriptOut The file name to write response
+   *
+   * @return byte[] Response byte array.
+   */
+  public byte[] writeScriptOutFile(String scriptOut) {
+    Path p = getPath(mOutDirectory, scriptOut);
+    // Update finish time stamp at end
+    mRespOutlog = mRespOutlog + getCurrentTimeStamp();
+    try {
+      Files.write(p, mRespOutlog.getBytes());
+    } catch (IOException e) {
+      Log.e(TAG, "IOException during writeScriptOutfile: ");
+    }
+    return mRespOutlog.getBytes();
+  }
+
+  /**
+   * Write the content of String buffer to backup file
+   * <br/>
+   * @param String filename to which the buffer contents to be copied
+   *        String scriptBuffer input buffer content
+   *
+   * @return byte[] response buffer.
+   */
+  public byte[] writeScriptInputFile(String filename, String scriptBuffer) {
+    Path p = getPath(mOutDirectory, filename);
+    try {
+      Files.write(p, scriptBuffer.getBytes());
+    } catch (IOException e) {
+      Log.e(TAG, "IOException during writeScriptInputfile: ");
+    }
+    return scriptBuffer.getBytes();
+  }
+
+  /**
+   * Read the file content to String format
+   * <br/>
+   * The Input path of the SEMS encrypted script stored,
+   * @param String scriptIn input SEMS script with metadata
+   *
+   * @return String plain script with metadata removed.
+   */
+
+  public String readScriptFile(String scriptIn) throws Exception {
+    Path p = getPath(mEncryptedScriptDirectory, scriptIn);
+    String script = "";
+    Iterator<String> i;
+    try {
+      List<String> lines = Files.readAllLines(p, Charset.defaultCharset());
+      i = lines.iterator();
+      while (i.hasNext()) {
+        String s = i.next();
+        if (!s.startsWith("%%%")) {
+          script += s;
+        }
+      }
+    } catch (IOException e) {
+      Log.e(TAG, "IOException during reading script: ");
+      throw new Exception();
+    }
+    return script;
+  }
+
+  /**
+   * Get response log buffer string
+   * <br/>
+   * @param void
+   *
+   * @return String response log buffer.
+   */
+  public String getRespOutLog() {
+    return mRespOutlog;
+  }
+}
+
diff --git a/sems/com/nxp/sems/semscore/SemsTLV.java b/sems/com/nxp/sems/semscore/SemsTLV.java
index 6dbe9ac..4d4a608 100644
--- a/sems/com/nxp/sems/semscore/SemsTLV.java
+++ b/sems/com/nxp/sems/semscore/SemsTLV.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 NXP
+ * Copyright 2019, 2021 NXP
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -192,4 +192,73 @@
       return null;
     }
   }
+
+  public static int getTagOffset(byte[] buff, int tagReq) {
+    int sOffset = 0;
+    int nextOffset = 0;
+    ByteBuffer bb = ByteBuffer.wrap(buff);
+    for (;;) {
+      int tag;
+
+      try {
+        tag = bb.get() & 0xFF;
+        nextOffset++;
+      } catch (BufferUnderflowException e) {
+        Log.e("SemsTLV", "BufferUnderFlowException occured");
+        break;
+      }
+
+      boolean isPrimitive = (tag & 0x20) == 0x00;
+
+      if ((tag & 0x1F) == 0x1F) {
+        /* 2-byte tag*/
+        tag = (tag << 8) + (bb.get() & 0xFF);
+        nextOffset++;
+      }
+      if (tag == tagReq) {
+        return sOffset;
+      }
+
+      int length = bb.get() & 0xFF;
+      if (length <= 0x7F) {
+        /* 1-byte length*/
+        nextOffset++;
+      } else if (length == 0x81) {
+        length = bb.get() & 0xFF;
+        nextOffset += 2;
+      } else if (length == 0x82) {
+        int length1 = bb.get() & 0xFF;
+        int length2 = bb.get() & 0xFF;
+        length = (length1 << 8) + length2;
+        nextOffset += 3;
+      } else if (length == 0x83) {
+        int length1 = bb.get() & 0xFF;
+        int length2 = bb.get() & 0xFF;
+        int length3 = bb.get() & 0xFF;
+        length = (length1 << 16) + (length2 << 8) + length3;
+        nextOffset += 4;
+      } else if (length == 0x84) {
+        int length1 = bb.get() & 0xFF;
+        int length2 = bb.get() & 0xFF;
+        int length3 = bb.get() & 0xFF;
+        int length4 = bb.get() & 0xFF;
+        length = (length1 << 24) + (length2 << 16) + (length3 << 8) + length4;
+        nextOffset += 5;
+      } else {
+        throw new RuntimeException("Bad length field");
+      }
+
+      if (isPrimitive) {
+        byte[] value = new byte[length];
+        nextOffset += length;
+        bb.get(value, 0, value.length);
+      } else {
+        /* In case of constructed TLV only tag, length counted, value skipped
+        for offset calculation*/
+      }
+      // Update offset pointing to next tag
+      sOffset = nextOffset;
+    }
+    return -1;
+  }
 }
diff --git a/sems/com/nxp/sems/semscore/SemsUtil.java b/sems/com/nxp/sems/semscore/SemsUtil.java
index 28274ad..c54dd79 100644
--- a/sems/com/nxp/sems/semscore/SemsUtil.java
+++ b/sems/com/nxp/sems/semscore/SemsUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 NXP
+ * Copyright 2019,2021 NXP
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -92,6 +92,19 @@
     return sha1.digest();
   }
 
+  public static byte[] SHA256(byte[] in) {
+    MessageDigest sha256 = null;
+    try {
+      sha256 = MessageDigest.getInstance("SHA256");
+    } catch (Exception e) {
+      System.err.println("SHA-256 not supported");
+      System.exit(1);
+    }
+    sha256.reset();
+    sha256.update(in);
+    return sha256.digest();
+  }
+
   public static byte[] toUTF8(String s) {
     if (s != null) {
       try {
diff --git a/sems/sems.jar b/sems/sems.jar
index 66184ae..20d23cc 100644
--- a/sems/sems.jar
+++ b/sems/sems.jar
Binary files differ