Merge "Add empty aid as the last entry of aid routing"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 7fb1636..657c8df 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -3,4 +3,8 @@
 
 [Builtin Hooks]
 clang_format = true
+rustfmt = true
+
+[Builtin Hooks Options]
+rustfmt = --config-path=rustfmt.toml
 
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000..617d425
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1,5 @@
+# Android Format Style
+
+edition = "2018"
+use_small_heuristics = "Max"
+newline_style = "Unix"
diff --git a/src/Android.bp b/src/Android.bp
index f691c4a..b16696a 100644
--- a/src/Android.bp
+++ b/src/Android.bp
@@ -185,3 +185,46 @@
         "fuzzers/llcp/*.cc",
     ],
 }
+
+genrule {
+    name: "NfcGeneratedPackets_rust",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --include=system/nfc/src --out=$(genDir) $(in) --rust",
+    srcs: [
+        "nci_packets.pdl",
+    ],
+    out: [
+        "nci_packets.rs",
+    ],
+}
+
+rust_library {
+    name: "libnfc_packets",
+    defaults: ["nfc_rust_defaults"],
+    crate_name: "nfc_packets",
+    srcs: ["rust/packets/lib.rs", ":NfcGeneratedPackets_rust"],
+    host_supported: true,
+    proc_macros: ["libnum_derive"],
+    rustlibs: [
+        "libbytes",
+        "libnum_traits",
+        "libthiserror",
+        "liblog_rust",
+    ],
+}
+
+rust_test_host {
+    name: "libnfc_packets_test",
+    defaults: ["nfc_rust_defaults"],
+    srcs: ["rust/packets/lib.rs", ":NfcGeneratedPackets_rust"],
+    test_suites: ["general-tests"],
+    proc_macros: ["libnum_derive"],
+    rustlibs: [
+        "libbytes",
+        "libnum_traits",
+        "libthiserror",
+        "liblog_rust",
+    ],
+}
diff --git a/src/adaptation/NfcAdaptation.cc b/src/adaptation/NfcAdaptation.cc
index 7c6a857..1567b0c 100644
--- a/src/adaptation/NfcAdaptation.cc
+++ b/src/adaptation/NfcAdaptation.cc
@@ -89,14 +89,9 @@
   nfc_debug_enabled =
       (NfcConfig::getUnsigned(NAME_NFC_DEBUG_ENABLED, 0) != 0) ? true : false;
 
-  char valueStr[PROPERTY_VALUE_MAX] = {0};
-  int len = property_get("nfc.debug_enabled", valueStr, "");
-  if (len > 0) {
-    // let Android property override .conf variable
-    unsigned debug_enabled = 0;
-    sscanf(valueStr, "%u", &debug_enabled);
-    nfc_debug_enabled = (debug_enabled == 0) ? false : true;
-  }
+  bool debug_enabled = property_get_bool("persist.nfc.debug_enabled", false);
+
+  nfc_debug_enabled = (nfc_debug_enabled || debug_enabled);
 
   DLOG_IF(INFO, nfc_debug_enabled)
       << StringPrintf("%s: level=%u", __func__, nfc_debug_enabled);
@@ -147,13 +142,24 @@
       const wp<::android::hidl::base::V1_0::IBase>& /* who */) {
     ALOGE(
         "NfcHalDeathRecipient::serviceDied - Nfc-Hal service died. Killing "
-        "NfcServie");
+        "NfcService");
     if (mNfcDeathHal) {
       mNfcDeathHal->unlinkToDeath(this);
     }
     mNfcDeathHal = NULL;
     abort();
   }
+  void finalize() {
+    if (mNfcDeathHal) {
+      mNfcDeathHal->unlinkToDeath(this);
+    } else {
+      DLOG_IF(INFO, nfc_debug_enabled)
+          << StringPrintf("%s: mNfcDeathHal is not set", __func__);
+    }
+
+    ALOGI("NfcHalDeathRecipient::destructor - NfcService");
+    mNfcDeathHal = NULL;
+  }
 };
 
 /*******************************************************************************
@@ -166,7 +172,6 @@
 **
 *******************************************************************************/
 NfcAdaptation::NfcAdaptation() {
-  mNfcHalDeathRecipient = new NfcHalDeathRecipient(mHal);
   memset(&mHalEntryFuncs, 0, sizeof(mHalEntryFuncs));
 }
 
@@ -393,6 +398,7 @@
 
   NfcConfig::clear();
 
+  mNfcHalDeathRecipient->finalize();
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", func);
   delete this;
 }
@@ -529,6 +535,7 @@
                             mHal.get(),
                             (mHal->isRemote() ? "remote" : "local"));
   if (mHal) {
+    mNfcHalDeathRecipient = new NfcHalDeathRecipient(mHal);
     mHal->linkToDeath(mNfcHalDeathRecipient, 0);
   }
 }
diff --git a/src/fuzzers/ce/t3t.cc b/src/fuzzers/ce/t3t.cc
index 490f6b7..e774df7 100644
--- a/src/fuzzers/ce/t3t.cc
+++ b/src/fuzzers/ce/t3t.cc
@@ -93,13 +93,13 @@
   for (auto it = ctx.Data.cbegin(); it != ctx.Data.cend(); ++it) {
     NFC_HDR* p_msg;
     p_msg = (NFC_HDR*)GKI_getbuf(sizeof(NFC_HDR) + it->size());
-    if (p_msg == nullptr) {
+    if (p_msg == nullptr || it->size() < 1) {
       FUZZLOG(MODULE_NAME ": GKI_getbuf returns null, size=%zu", it->size());
       return;
     }
 
     /* Initialize NFC_HDR */
-    p_msg->len = it->size();
+    p_msg->len = it->size() - 1;
     p_msg->offset = 0;
 
     uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset;
diff --git a/src/fuzzers/rw/t1t.cc b/src/fuzzers/rw/t1t.cc
index 94670fd..f2c4f80 100644
--- a/src/fuzzers/rw/t1t.cc
+++ b/src/fuzzers/rw/t1t.cc
@@ -213,13 +213,13 @@
   for (auto it = ctx.Data.cbegin() + 1; it != ctx.Data.cend(); ++it) {
     NFC_HDR* p_msg;
     p_msg = (NFC_HDR*)GKI_getbuf(sizeof(NFC_HDR) + it->size());
-    if (p_msg == nullptr) {
+    if (p_msg == nullptr || it->size() < 1) {
       FUZZLOG(MODULE_NAME ": GKI_getbuf returns null, size=%zu", it->size());
       return;
     }
 
     /* Initialize NFC_HDR */
-    p_msg->len = it->size();
+    p_msg->len = it->size() - 1;
     p_msg->offset = 0;
 
     uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset;
diff --git a/src/fuzzers/rw/t2t.cc b/src/fuzzers/rw/t2t.cc
index 57ddb0f..f829e35 100644
--- a/src/fuzzers/rw/t2t.cc
+++ b/src/fuzzers/rw/t2t.cc
@@ -172,13 +172,13 @@
   for (auto it = ctx.Data.cbegin() + 1; it != ctx.Data.cend(); ++it) {
     NFC_HDR* p_msg;
     p_msg = (NFC_HDR*)GKI_getbuf(sizeof(NFC_HDR) + it->size());
-    if (p_msg == nullptr) {
+    if (p_msg == nullptr || it->size() < 1) {
       FUZZLOG(MODULE_NAME ": GKI_getbuf returns null, size=%zu", it->size());
       return;
     }
 
     /* Initialize NFC_HDR */
-    p_msg->len = it->size();
+    p_msg->len = it->size() - 1;
     p_msg->offset = 0;
 
     uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset;
diff --git a/src/fuzzers/rw/t3t.cc b/src/fuzzers/rw/t3t.cc
index b64ffc5..8853cf7 100644
--- a/src/fuzzers/rw/t3t.cc
+++ b/src/fuzzers/rw/t3t.cc
@@ -251,6 +251,11 @@
                         .status = NFC_STATUS_OK,
                         .p_data = p_msg,
                     }};
+  if (p_msg->len < 1) {
+    FUZZLOG(MODULE_NAME ": Ivalid message length=%hu", p_msg->len);
+    return;
+  }
+  p_msg->len--;
 
   rf_cback(NFC_RF_CONN_ID, NFC_DATA_CEVT, &conn);
 }
diff --git a/src/fuzzers/rw/t5t.cc b/src/fuzzers/rw/t5t.cc
index 1358bed..bd61f66 100644
--- a/src/fuzzers/rw/t5t.cc
+++ b/src/fuzzers/rw/t5t.cc
@@ -290,13 +290,13 @@
   for (auto it = ctx.Data.cbegin() + 1; it != ctx.Data.cend(); ++it) {
     NFC_HDR* p_msg;
     p_msg = (NFC_HDR*)GKI_getbuf(sizeof(NFC_HDR) + it->size());
-    if (p_msg == nullptr) {
+    if (p_msg == nullptr || it->size() < 1) {
       FUZZLOG(MODULE_NAME ": GKI_getbuf returns null, size=%zu", it->size());
       return;
     }
 
     /* Initialize NFC_HDR */
-    p_msg->len = it->size();
+    p_msg->len = it->size() - 1;
     p_msg->offset = 0;
 
     uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset;
diff --git a/src/gki/common/gki_buffer.cc b/src/gki/common/gki_buffer.cc
index 0ad8427..1aeb3ae 100644
--- a/src/gki/common/gki_buffer.cc
+++ b/src/gki/common/gki_buffer.cc
@@ -1306,10 +1306,35 @@
 **
 *******************************************************************************/
 uint16_t GKI_get_pool_bufsize(uint8_t pool_id) {
+#if defined(DYN_ALLOC) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+  uint16_t size = 0;
+  switch (pool_id) {
+    case GKI_POOL_ID_0:
+      size = GKI_BUF0_SIZE;
+      break;
+    case GKI_POOL_ID_1:
+      size = GKI_BUF1_SIZE;
+      break;
+    case GKI_POOL_ID_2:
+      size = GKI_BUF2_SIZE;
+      break;
+    case GKI_POOL_ID_3:
+      size = GKI_BUF3_SIZE;
+      break;
+      /* Here could be more pool ids, but they are not used in the current
+       * implementation */
+    default:
+      LOG(ERROR) << StringPrintf("Unknown pool ID: %d", pool_id);
+      return (0);
+      break;
+  }
+  return (size);
+#else
   if (pool_id < GKI_NUM_TOTAL_BUF_POOLS)
     return (gki_cb.com.freeq[pool_id].size);
 
   return (0);
+#endif
 }
 
 /*******************************************************************************
diff --git a/src/gki/ulinux/gki_ulinux.cc b/src/gki/ulinux/gki_ulinux.cc
index 57c2402..64983fd 100644
--- a/src/gki/ulinux/gki_ulinux.cc
+++ b/src/gki/ulinux/gki_ulinux.cc
@@ -95,8 +95,8 @@
   /* Call the actual thread entry point */
   (p_pthread_info->task_entry)(p_pthread_info->params);
 
-  LOG(ERROR) << StringPrintf("gki_task task_id=%i terminating",
-                             p_pthread_info->task_id);
+  LOG(WARNING) << StringPrintf("gki_task task_id=%i terminating",
+                               p_pthread_info->task_id);
   gki_cb.os.thread_id[p_pthread_info->task_id] = 0;
 
   return nullptr;
@@ -290,8 +290,6 @@
    * GKI_exception problem due to btu->hci sleep request events  */
   for (task_id = GKI_MAX_TASKS; task_id > 0; task_id--) {
     if (gki_cb.com.OSRdyTbl[task_id - 1] != TASK_DEAD) {
-      gki_cb.com.OSRdyTbl[task_id - 1] = TASK_DEAD;
-
       /* paranoi settings, make sure that we do not execute any mailbox events
        */
       gki_cb.com.OSWaitEvt[task_id - 1] &=
@@ -629,13 +627,13 @@
     if (gki_cb.com.OSTaskQFirst[rtask][3])
       gki_cb.com.OSWaitEvt[rtask] |= TASK_MBOX_3_EVT_MASK;
 
-    if (gki_cb.com.OSRdyTbl[rtask] == TASK_DEAD) {
+    if (gki_cb.com.OSWaitEvt[rtask] == EVENT_MASK(GKI_SHUTDOWN_EVT)) {
       gki_cb.com.OSWaitEvt[rtask] = 0;
       /* unlock thread_evt_mutex as pthread_cond_wait() does auto lock when cond
        * is met */
       pthread_mutex_unlock(&gki_cb.os.thread_evt_mutex[rtask]);
-      LOG(ERROR) << StringPrintf("GKI TASK_DEAD received. exit thread %d...",
-                                 rtask);
+      LOG(WARNING) << StringPrintf("GKI TASK_DEAD received. exit thread %d...",
+                                   rtask);
 
       gki_cb.os.thread_id[rtask] = 0;
       return (EVENT_MASK(GKI_SHUTDOWN_EVT));
@@ -1073,6 +1071,11 @@
     return;
   }
   GKI_disable();
+  if (gki_cb.com.OSRdyTbl[task_id] == TASK_DEAD) {
+      GKI_enable();
+      LOG(WARNING) << StringPrintf("%s: task_id %d was already stopped.", __func__, task_id);
+      return;
+  }
   gki_cb.com.OSRdyTbl[task_id] = TASK_DEAD;
 
   /* Destroy mutex and condition variable objects */
diff --git a/src/nci_packets.pdl b/src/nci_packets.pdl
new file mode 100644
index 0000000..f94a35d
--- /dev/null
+++ b/src/nci_packets.pdl
@@ -0,0 +1,441 @@
+little_endian_packets
+
+enum PacketBoundaryFlag : 1 {
+  COMPLETE_OR_FINAL = 0,
+  INCOMPLETE = 1,
+}
+
+enum NciMsgType : 3 {
+  DATA = 0,
+  COMMAND = 1,
+  RESPONSE = 2,
+  NOTIFICATION = 3,
+}
+
+enum Opcode : 8 {
+  CORE_RESET = 0x0,
+  CORE_INIT = 0x1,
+  CORE_SET_CONFIG = 0x2,
+  CORE_GET_CONFIG = 0x3,
+  CORE_CONN_CREATE = 0x4,
+  CORE_CONN_CLOSE = 0x5,
+  CORE_CONN_CREDITS = 0x6,
+  CORE_GENERIC_ERROR = 0x7,
+  CORE_INTERFACE_ERROR = 0x8,
+  CORE_SET_POWER_SUBSTATE = 0x9,
+  RF_DISCOVER_MAP = 0x40,
+  RF_SET_LISTEN_MODE_ROUTING = 0x41,
+  RF_GET_LISTEN_MODE_ROUTING = 0x42,
+  RF_DISCOVER = 0x43,
+  RF_DISCOVER_SELECT = 0x44,
+  RF_INTF_ACTIVATED = 0x45,
+  RF_DIACTIVATE = 0x46,
+  RF_FIELD_INFO = 0x47,
+  RF_T3T_POLLING = 0x48,
+  RF_NFCEE_ACTION = 0x49,
+  RF_NFCEE_DISCOVERY_REQ = 0x4A,
+  RF_PARAMETER_UPDATE = 0x4B,
+  RF_INTF_EXT_START = 0x4C,
+  RF_INTF_EXT_STOP = 0x4D,
+  RF_EXT_AGG_ABORT = 0x4E,
+  RF_NDEF_ABORT = 0x4F,
+  RF_ISO_DEP_NAK_PRESENCE = 0x50,
+  RF_SET_FORCED_NFCEE_ROUTING_CMD = 0x51,
+}
+
+enum Status : 8 {
+  OK = 0x00,
+  REJECTED = 0x01,
+  FAILED = 0x03,
+  NOT_INITIALIZED = 0x04,
+  SYNTAX_ERROR = 0x05,
+  SEMANTIC_ERROR = 0x06,
+  INVALID_PARAM = 0x09,
+  MESSAGE_SIZE_EXCEEDED = 0x0A,
+  OK_1_BIT = 0x11,
+  OK_2_BIT = 0x12,
+  OK_3_BIT = 0x13,
+  OK_4_BIT = 0x14,
+  OK_5_BIT = 0x15,
+  OK_6_BIT = 0x16,
+  OK_7_BIT = 0x17,
+  DISCOVERY_ALREADY_STARTED = 0xA0,
+  DISCOVERY_TARGET_ACTIVATION_FAILED = 0xA1,
+  DISCOVERY_TEAR_DOWN = 0xA2,
+  RF_FRAME_CORRUPTED = 0x02,
+  RF_TRANSMISSION_EXCEPTION = 0xB0,
+  RF_PROTOCOL_EXCEPTION = 0xB1,
+  RF_TIMEOUT_EXCEPTION = 0xB2,
+  RF_UNEXPECTED_DATA = 0xB3,
+  NFCEE_INTERFACE_ACTIVATION_FAILED = 0xC0,
+  NFCEE_TRANSMISSION_ERROR = 0xC1,
+  NFCEE_PROTOCOL_ERROR = 0xC2,
+  NFCEE_TIMEOUT_ERROR = 0xC3,
+}
+
+packet Nci {
+  gid : 4,
+  pbf : PacketBoundaryFlag,
+  mt : NciMsgType,
+  _payload_,
+}
+
+packet Command : Nci (mt = COMMAND) {
+  op : Opcode,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+packet Response : Nci (mt = RESPONSE) {
+  cmd_op : Opcode,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+packet Notification : Nci (mt = NOTIFICATION) {
+  cmd_op : Opcode,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+packet Data {
+  conn_id : 4,
+  pbf : PacketBoundaryFlag,
+  _fixed_ =  0x0 : 3,
+  cr : 8,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+enum ResetType : 8 {
+  KEEP_CONFIG = 0,
+  RESET_CONFIG = 1,
+}
+
+packet ResetCommand : Command (op = CORE_RESET) {
+  reset_type: ResetType,
+}
+
+test ResetCommand {
+  "\x20\x00\x01\x01",
+}
+
+packet ResetResponse : Response (cmd_op = CORE_RESET) {
+  status: Status,
+}
+
+test ResetResponse {
+  "\x40\x00\x01\x00",
+}
+
+enum ResetTrigger : 8 {
+  UNRECOVERABLE_ERROR = 0,
+  POWER_ON = 1,
+  RESET_COMMAND = 2,
+}
+
+enum NciVersion : 8 {
+  VERSION_1_0 = 0x10,
+  VERSION_1_1 = 0x11,
+  VERSION_2_0 = 0x20,
+}
+
+enum ConfigStatus : 8 {
+  CONFIG_KEPT = 0x00,
+  CONFIG_RESET = 0x01,
+}
+
+packet ResetNotification : Notification (cmd_op = CORE_RESET) {
+  trigger : ResetTrigger,
+  config_status : ConfigStatus,
+  nci_version : NciVersion,
+  manufacturer_id: 8,
+  _size_(mfsi) : 8,
+  mfsi : 8[],
+}
+
+test ResetNotification {
+  "\x60\x00\x1f\x02\x01\x20\x02\x1a\x05\x03\x03\x06\x88\x97\x01\x06\x00\x00\x44\x64\xd6\x00\x00\xec\x10\x00\x00\x00\x01\x00\x00\xee\xe7\x02",
+}
+
+enum FeatureEnable : 16 {
+  RFU = 0,
+}
+
+
+enum DiscConfMode : 2 {
+  DH_ONLY = 0x0,
+  DH_AND_EE = 0x1,
+}
+
+enum FeatureState : 1 {
+  DISABLED = 0,
+  AVAILABLE = 1,
+}
+
+struct PropCaps {
+  b7 : 1,
+  b6 : 1,
+  b5 : 1,
+  b4 : 1,
+  b3 : 1,
+  b2 : 1,
+  b1 : 1,
+  b0 : 1,
+}
+
+struct NfccFeatures {
+  disc_freq_conf : FeatureState,
+  disc_conf_mode : DiscConfMode,
+  hci_net_support : FeatureState,
+  active_comm_mode : FeatureState,
+  _reserved_ : 3,
+  _reserved_: 1,
+  tech_routing : FeatureState,
+  proto_routing : FeatureState,
+  aid_routing : FeatureState,
+  syc_code_routing : FeatureState,
+  apdu_pttn_routing : FeatureState,
+  forced_nfcee_routing : FeatureState,
+  _reserved_ : 1,
+  batt_off_st : FeatureState,
+  soff_st : FeatureState,
+  swon_subst : FeatureState,
+  rf_conf_soff: FeatureState,
+  _reserved_ : 4,
+  prop_caps: PropCaps,
+}
+
+enum Intf : 8 {
+  NFCEE_DIRECT_RF = 0x00,
+  FRAME_RF = 0x01,
+  ISO_DEP_RF = 0x02,
+  NFC_DEP_RF = 0x03,
+  NDEF_RF = 0x06,
+}
+
+enum Extns : 8 {
+  FR_AGREG_RF_EXT = 0x00,
+  LLCP_SYM_RF_EXT = 0x01,
+}
+
+struct ExtList {
+  ext : Extns,
+}
+
+struct RfInterface {
+  intf : 8,
+  _size_(extns) : 8,
+  extns : 8[],
+}
+
+packet InitCommand : Command (op = CORE_INIT) {
+  feature_eneble : FeatureEnable,
+}
+
+test InitCommand {
+  "\x20\x01\x02\x00\x00",
+}
+
+packet InitResponse : Response (cmd_op = CORE_INIT) {
+  status : Status,
+  nfcc_features : NfccFeatures,
+  max_log_conns : 4, //TODO set max to 0x0E
+  _reserved_ : 4,
+  max_rout_tbls_size : 16,
+  max_ctrl_payload : 8,  //TODO 32 <= val <= 255
+  max_data_payload : 8,
+  num_of_credits : 8,
+  max_nfcv_rf_frame_sz : 16,
+  _count_(rf_interface) : 8,
+  rf_interface: RfInterface[],
+}
+
+test InitResponse {
+  "\x40\x01\x18\x00\x1a\x7e\x06\x00\x01\x00\x04\xff\xff\x00\x0c\x01\x05\x01\x00\x02\x00\x03\x00\x00\x00\x90\x00",
+}
+
+enum ParamIds : 8 {
+  TOTAL_DURATION = 0x00,
+  CON_DISCOVERY_PARAM = 0x02,
+  POWER_STATE = 0x03,
+  PA_BAIL_OUT = 0x08,
+  PA_DEVICES_LIMIT = 0x09,
+  PB_AFI = 0x10,
+  PB_BAIL_OUT = 0x11,
+  PB_ATTRIB_PARAM1 = 0x12,
+  PB_SENSB_REQ_PARAM = 0x13,
+  PB_DEVICES_LIMIT = 0x14,
+  PF_BIT_RATE = 0x18,
+  PF_BAIL_OUT = 0x19,
+  PF_DEVICES_LIMIT = 0x1A,
+  PI_B_H_INFO = 0x20,
+  PI_BIT_RATE = 0x21,
+  PN_NFC_DEP_PSL = 0x28,
+  PN_ATR_REQ_GEN_BYTES = 0x29,
+  PN_ATR_REQ_CONFIG = 0x2A,
+  PV_DEVICES_LIMIT = 0x2F,
+  LA_BIT_FRAME_SDD = 0x30,
+  LA_PLATFORM_CONFIG = 0x31,
+  LA_SEL_INFO = 0x32,
+  LA_NFCID1 = 0x33,
+  LB_SENSB_INFO = 0x38,
+  LB_NFCID0 = 0x39,
+  LB_APPLICATION_DATA = 0x3A,
+  LB_SFGI = 0x3B,
+  LB_FWI_ADC_FO = 0x3C,
+  LB_BIT_RATE = 0x3E,
+  LF_T3T_IDENTIFIERS_1 = 0x40,
+  LF_T3T_IDENTIFIERS_2 = 0x41,
+  LF_T3T_IDENTIFIERS_3 = 0x42,
+  LF_T3T_IDENTIFIERS_4 = 0x43,
+  LF_T3T_IDENTIFIERS_5 = 0x44,
+  LF_T3T_IDENTIFIERS_6 = 0x45,
+  LF_T3T_IDENTIFIERS_7 = 0x46,
+  LF_T3T_IDENTIFIERS_8 = 0x47,
+  LF_T3T_IDENTIFIERS_9 = 0x48,
+  LF_T3T_IDENTIFIERS_10 = 0x49,
+  LF_T3T_IDENTIFIERS_11 = 0x4A,
+  LF_T3T_IDENTIFIERS_12 = 0x4B,
+  LF_T3T_IDENTIFIERS_13 = 0x4C,
+  LF_T3T_IDENTIFIERS_14 = 0x4D,
+  LF_T3T_IDENTIFIERS_15 = 0x4E,
+  LF_T3T_IDENTIFIERS_16 = 0x4F,
+  LF_T3T_MAX = 0x52,
+  LF_T3T_FLAGS = 0x53,
+  LF_T3T_RD_ALLOWED = 0x55,
+  LF_PROTOCOL_TYPE = 0x50,
+  LI_A_RATS_TB1 = 0x58,
+  LI_A_HIST_BY = 0x59,
+  LI_B_H_INFO_RESP = 0x5A,
+  LI_A_BIT_RATE = 0x5B,
+  LI_A_RATS_TC1 = 0x5C,
+  LN_WT = 0x60,
+  LN_ATR_RES_GEN_BYTES = 0x61,
+  LN_ATR_RES_CONFIG = 0x62,
+  PACM_BIT_RATE = 0x68,
+  RF_FIELD_INFO = 0x80,
+  RF_NFCEE_ACTION = 0x81,
+  NFCDEP_OP = 0x82,
+  LLCP_VERSION = 0x83,
+  NFCC_CONFIG_CONTROL = 0x85,
+}
+
+struct ConfigParams {
+  paramid : ParamIds,
+  _size_(valm) : 8,
+  valm : 8[],
+}
+
+struct ParamList {
+  pids : ParamIds,
+}
+
+packet SetConfigCommand : Command (op = CORE_SET_CONFIG) {
+  _count_(params) : 8,
+  params : ConfigParams[],
+}
+
+packet SetConfigResponse : Response (cmd_op = CORE_SET_CONFIG) {
+  status : Status,
+  _count_(paramids) : 8,
+  paramids : ParamList[],
+}
+
+packet GetConfigCommand : Command (op = CORE_GET_CONFIG) {
+  _count_(paramids) : 8,
+  paramids : ParamList[],
+}
+
+packet GetConfigResponse : Response (cmd_op = CORE_GET_CONFIG) {
+  status : Status,
+  _count_(params) : 8,
+  params : ConfigParams[],
+}
+
+enum RfProtocols : 8 {
+  PROTOCOL_UNDETERMINED = 0x00,
+  PROTOCOL_T1T = 0x01,
+  PROTOCOL_T2T = 0x02,
+  PROTOCOL_T3T = 0x03,
+  PROTOCOL_ISO_DEP = 0x04,
+  PROTOCOL_NFC_DEP = 0x05,
+  PROTOCOL_T5T = 0x06,
+  PROTOCOL_NDEF = 0x07,
+}
+
+enum NfceeProtocols : 8 {
+  APDU = 0x00,
+  RFU = 0x01,
+  T3CS = 0x02,
+  TRANSPARENT = 0x04,
+}
+
+enum DestTypes : 8 {
+  RFU = 0x00,
+  NFCC_LPBK = 0x01,
+  REMOTE = 0x02,
+  NFCEE = 0x03,
+}
+
+enum DestParamTypes : 8 {
+  RF_DISC = 0x00,
+  NFCEE = 0x01,
+}
+
+struct RfDiscType {
+  id : 8,
+  proto: RfProtocols,
+}
+
+struct NfceeType {
+  id : 8,
+  proto : NfceeProtocols,
+}
+
+struct DestParams {
+  ptype : DestParamTypes,
+  _size_(valdsp) : 8,
+  valdsp : 8[],
+}
+
+packet ConnCreateCommand : Command (op = CORE_CONN_CREATE) {
+  dt : DestTypes,
+  _count_(destparams) : 8,
+  destparams : DestParams[],
+}
+
+packet ConnCreateResponse : Response (cmd_op = CORE_CONN_CREATE) {
+  status : Status,
+  mpps : 8,
+  ncreds : 8,
+  conn_id : 8,
+}
+
+packet ConnCloseCommand : Command (op = CORE_CONN_CLOSE) {
+  conn_id : 8,
+}
+
+packet ConnCloseResponse : Response (cmd_op = CORE_CONN_CLOSE) {
+  status : Status,
+}
+
+struct CreditsPerConn {
+  conn_id : 8,
+  ncredits : 8,
+}
+
+packet ConnCreditsNotification : Notification (cmd_op = CORE_CONN_CREDITS) {
+  _count_(conns) : 8,
+  conns : CreditsPerConn[],
+}
+
+packet GenericError : Notification (cmd_op = CORE_GENERIC_ERROR) {
+  status : Status,
+}
+
+packet InterfaceError : Notification (cmd_op = CORE_INTERFACE_ERROR) {
+  status : Status,
+  conn_id : 8,
+}
+
diff --git a/src/nfa/dm/nfa_dm_act.cc b/src/nfa/dm/nfa_dm_act.cc
index e89ff94..51c841d 100644
--- a/src/nfa/dm/nfa_dm_act.cc
+++ b/src/nfa/dm/nfa_dm_act.cc
@@ -36,9 +36,10 @@
 
 #if (NFC_NFCEE_INCLUDED == TRUE)
 #include "nfa_ee_int.h"
-
 #endif
 
+#include "nfc_int.h"
+
 #if (NFA_SNEP_INCLUDED == TRUE)
 #include "nfa_snep_int.h"
 #endif
@@ -950,6 +951,14 @@
       poll_disc_mask |= NFA_DM_DISC_MASK_P_KOVIO;
     }
 
+    if (!(nfc_cb.nci_interfaces & (1 << NCI_INTERFACE_NFC_DEP))) {
+      /* Remove NFC-DEP related Discovery mask, if NFC_DEP interface is not
+       * supported */
+      poll_disc_mask &=
+          ~(NFA_DM_DISC_MASK_PACM_NFC_DEP | NFA_DM_DISC_MASK_PAA_NFC_DEP |
+            NFA_DM_DISC_MASK_PFA_NFC_DEP | NFA_DM_DISC_MASK_PF_NFC_DEP);
+    }
+
     nfa_dm_cb.poll_disc_handle = nfa_dm_add_rf_discover(
         poll_disc_mask, NFA_DM_DISC_HOST_ID_DH, nfa_dm_poll_disc_cback);
 
diff --git a/src/nfa/dm/nfa_dm_api.cc b/src/nfa/dm/nfa_dm_api.cc
index 20b1730..87f6e80 100644
--- a/src/nfa/dm/nfa_dm_api.cc
+++ b/src/nfa/dm/nfa_dm_api.cc
@@ -760,7 +760,8 @@
   DLOG_IF(INFO, nfc_debug_enabled) << __func__;
 
   /* Post the API message */
-  p_msg = (tNFA_DM_API_SET_RF_DISC_DUR*)GKI_getbuf(sizeof(NFC_HDR));
+  p_msg = (tNFA_DM_API_SET_RF_DISC_DUR*)GKI_getbuf(
+      sizeof(tNFA_DM_API_SET_RF_DISC_DUR));
   if (p_msg != nullptr) {
     p_msg->hdr.event = NFA_DM_API_SET_RF_DISC_DURATION_EVT;
 
diff --git a/src/nfa/ee/nfa_ee_act.cc b/src/nfa/ee/nfa_ee_act.cc
index 5d22fb5..cc2c904 100644
--- a/src/nfa/ee/nfa_ee_act.cc
+++ b/src/nfa/ee/nfa_ee_act.cc
@@ -30,6 +30,7 @@
 #include "nfa_dm_int.h"
 #include "nfa_ee_int.h"
 #include "nfa_hci_int.h"
+#include "nfc_int.h"
 
 #include <statslog.h>
 #include "metrics.h"
@@ -383,11 +384,16 @@
       }
       if (p_cb->nfcee_id == NFC_DH_ID &&
           nfa_ee_proto_mask_list[xx] == NFA_PROTOCOL_MASK_NFC_DEP) {
-        /* add NFC-DEP routing to HOST */
-        add_route_tech_proto_tlv(&pp, NFC_ROUTE_TAG_PROTO, NFC_DH_ID,
-                                 NCI_ROUTE_PWR_STATE_ON, NFC_PROTOCOL_NFC_DEP);
-        DLOG_IF(INFO, nfc_debug_enabled)
-            << StringPrintf("%s - NFC DEP added for DH!!!", __func__);
+        /* add NFC-DEP routing to HOST if NFC_DEP interface is supported */
+        if (nfc_cb.nci_interfaces & (1 << NCI_INTERFACE_NFC_DEP)) {
+          add_route_tech_proto_tlv(&pp, NFC_ROUTE_TAG_PROTO, NFC_DH_ID,
+                                   NCI_ROUTE_PWR_STATE_ON,
+                                   NFC_PROTOCOL_NFC_DEP);
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s - NFC DEP added for DH!!!", __func__);
+        } else {
+          continue;
+        }
       } else {
         add_route_tech_proto_tlv(&pp, proto_tag, p_cb->nfcee_id, power_cfg,
                                  nfa_ee_proto_list[xx]);
@@ -1310,7 +1316,7 @@
     /* report NFA_EE_REMOVE_AID_EVT to the callback associated the NFCEE */
     p_cback = p_cb->p_ee_cback;
   } else {
-    LOG(ERROR) << StringPrintf(
+    LOG(WARNING)  << StringPrintf(
         "nfa_ee_api_remove_aid The AID entry is not in the database");
     evt_data.status = NFA_STATUS_INVALID_PARAM;
   }
@@ -2165,8 +2171,8 @@
     /* Start routing table update debounce timer */
     nfa_ee_start_timer();
   }
-  LOG(ERROR) << StringPrintf("%s p_rsp->status:0x%02x", __func__,
-                             p_rsp->status);
+  LOG(WARNING) << StringPrintf("%s p_rsp->status:0x%02x", __func__,
+                               p_rsp->status);
   if (p_rsp->status == NFA_STATUS_OK) {
     if (p_rsp->mode == NFA_EE_MD_ACTIVATE) {
       p_cb->ee_status = NFC_NFCEE_STATUS_ACTIVE;
diff --git a/src/nfa/ee/nfa_ee_api.cc b/src/nfa/ee/nfa_ee_api.cc
index 12b5844..428ac21 100644
--- a/src/nfa/ee/nfa_ee_api.cc
+++ b/src/nfa/ee/nfa_ee_api.cc
@@ -127,6 +127,9 @@
     p_info->ee_status = p_cb->ee_status;
     p_info->num_interface = p_cb->num_interface;
     p_info->num_tlvs = p_cb->num_tlvs;
+    p_info->la_protocol = p_cb->la_protocol;
+    p_info->lb_protocol = p_cb->lb_protocol;
+    p_info->lf_protocol = p_cb->lf_protocol;
     memcpy(p_info->ee_interface, p_cb->ee_interface, p_cb->num_interface);
     memcpy(p_info->ee_tlv, p_cb->ee_tlv, p_cb->num_tlvs * sizeof(tNFA_EE_TLV));
     p_info->ee_power_supply_status = p_cb->ee_power_supply_status;
diff --git a/src/nfa/include/nfa_ee_api.h b/src/nfa/include/nfa_ee_api.h
index 30a0867..ec9b20a 100644
--- a/src/nfa/include/nfa_ee_api.h
+++ b/src/nfa/include/nfa_ee_api.h
@@ -119,6 +119,9 @@
   uint8_t num_tlvs;                       /* number of TLVs           */
   tNFA_EE_TLV ee_tlv[NFC_MAX_EE_TLVS];    /* the TLV                  */
   uint8_t ee_power_supply_status;         /* The NFCEE Power supply */
+  tNFA_NFC_PROTOCOL la_protocol;          /* Listen A protocol    */
+  tNFA_NFC_PROTOCOL lb_protocol;          /* Listen B protocol    */
+  tNFA_NFC_PROTOCOL lf_protocol;          /* Listen F protocol    */
 } tNFA_EE_INFO;
 
 typedef struct {
diff --git a/src/nfa/rw/nfa_rw_act.cc b/src/nfa/rw/nfa_rw_act.cc
index 3ed8c7c..4721b68 100644
--- a/src/nfa/rw/nfa_rw_act.cc
+++ b/src/nfa/rw/nfa_rw_act.cc
@@ -776,7 +776,7 @@
       break;
 
     case RW_T2T_TLV_DETECT_EVT: /* Lock control/Mem/Prop tlv detection complete
-                                   */
+                                 */
       nfa_rw_handle_tlv_detect(p_rw_data);
       break;
 
@@ -1699,8 +1699,7 @@
       status = RW_T3tUpdateNDef(nfa_rw_cb.ndef_wr_len, nfa_rw_cb.p_ndef_wr_buf);
     } else if (NFC_PROTOCOL_ISO_DEP == protocol) {
       /* ISODEP/4A,4B- NFC-A or NFC-B */
-      status = RW_T4tUpdateNDef((uint16_t)nfa_rw_cb.ndef_wr_len,
-                                nfa_rw_cb.p_ndef_wr_buf);
+      status = RW_T4tUpdateNDef(nfa_rw_cb.ndef_wr_len, nfa_rw_cb.p_ndef_wr_buf);
     } else if (NFC_PROTOCOL_T5T == protocol) {
       /* ISO 15693 */
       status = RW_I93UpdateNDef((uint16_t)nfa_rw_cb.ndef_wr_len,
@@ -1793,8 +1792,8 @@
   } else if (nfa_rw_cb.ndef_st == NFA_RW_NDEF_ST_FALSE) {
     if (nfa_rw_cb.protocol == NFC_PROTOCOL_T1T) {
       /* For Type 1 tag, NDEF can be written on Initialized tag
-      *  Perform ndef detection first to check if tag is in Initialized state to
-      * Write NDEF */
+       *  Perform ndef detection first to check if tag is in Initialized state
+       * to Write NDEF */
       write_status = nfa_rw_start_ndef_detection();
     } else {
       /* Tag is not NDEF */
@@ -2715,8 +2714,23 @@
       /* Tag-it HF-I Plus Chip/Inlay supports Get System Information Command */
       /* just try for others */
 
-      if (RW_I93GetSysInfo(nfa_rw_cb.i93_uid) != NFC_STATUS_OK) {
-        /* notify activation without AFI/MEM size/IC-Ref */
+      if (!appl_dta_mode_flag) {
+        if (RW_I93GetSysInfo(nfa_rw_cb.i93_uid) != NFC_STATUS_OK) {
+          /* notify activation without AFI/MEM size/IC-Ref */
+          nfa_rw_cb.flags &= ~NFA_RW_FL_ACTIVATION_NTF_PENDING;
+          activate_notify = true;
+
+          tag_params.i93.info_flags = I93_INFO_FLAG_DSFID;
+          tag_params.i93.dsfid = nfa_rw_cb.i93_dsfid;
+          tag_params.i93.block_size = 0;
+          tag_params.i93.num_block = 0;
+          memcpy(tag_params.i93.uid, nfa_rw_cb.i93_uid, I93_UID_BYTE_LEN);
+        } else {
+          /* reset memory size */
+          nfa_rw_cb.i93_block_size = 0;
+          nfa_rw_cb.i93_num_block = 0;
+        }
+      } else {
         nfa_rw_cb.flags &= ~NFA_RW_FL_ACTIVATION_NTF_PENDING;
         activate_notify = true;
 
@@ -2725,10 +2739,6 @@
         tag_params.i93.block_size = 0;
         tag_params.i93.num_block = 0;
         memcpy(tag_params.i93.uid, nfa_rw_cb.i93_uid, I93_UID_BYTE_LEN);
-      } else {
-        /* reset memory size */
-        nfa_rw_cb.i93_block_size = 0;
-        nfa_rw_cb.i93_num_block = 0;
       }
     }
   }
diff --git a/src/nfc/include/nfc_int.h b/src/nfc/include/nfc_int.h
index f357965..5ecc879 100644
--- a/src/nfc/include/nfc_int.h
+++ b/src/nfc/include/nfc_int.h
@@ -199,6 +199,8 @@
   bool reassembly; /* Reassemble fragmented data pkt */
   uint8_t last_hdr[NFC_SAVED_HDR_SIZE]; /* part of last NCI command header */
   uint8_t last_cmd[NFC_SAVED_CMD_SIZE]; /* part of last NCI command payload */
+  uint8_t
+      last_nfcee_cmd[NFC_SAVED_CMD_SIZE]; /* part of last NCI command payload */
   void* p_vsc_cback;       /* the callback function for last VSC command */
   BUFFER_Q nci_cmd_xmit_q; /* NCI command queue */
   TIMER_LIST_ENT
diff --git a/src/nfc/include/rw_api.h b/src/nfc/include/rw_api.h
index 91382e3..242b568 100644
--- a/src/nfc/include/rw_api.h
+++ b/src/nfc/include/rw_api.h
@@ -936,7 +936,7 @@
 **                  NFC_STATUS_FAILED if T4T is busy or other error
 **
 *******************************************************************************/
-extern tNFC_STATUS RW_T4tUpdateNDef(uint16_t length, uint8_t* p_data);
+extern tNFC_STATUS RW_T4tUpdateNDef(uint32_t length, uint8_t* p_data);
 
 /*****************************************************************************
 **
diff --git a/src/nfc/include/rw_int.h b/src/nfc/include/rw_int.h
index 9bc861a..e25f48e 100644
--- a/src/nfc/include/rw_int.h
+++ b/src/nfc/include/rw_int.h
@@ -492,6 +492,8 @@
 #define RW_T3T_FL_W4_FMT_FELICA_LITE_POLL_RSP 0x10
 /* Waiting for POLL response for RW_T3tSetReadOnly */
 #define RW_T3T_FL_W4_SRO_FELICA_LITE_POLL_RSP 0x20
+/* Waiting for POLL response for RW_T3tPoll */
+#define RW_T3T_FL_W4_USER_POLL_RSP 0x40
 
 typedef struct {
   uint32_t cur_tout; /* Current command timeout */
@@ -538,16 +540,34 @@
 
 /* Max data size using a single UpdateBinary. 6 bytes are for CLA, INS, P1, P2,
  * Lc */
+/* Use worst case where Extended Field Coding and ODO format are used */
 #define RW_T4T_MAX_DATA_PER_WRITE                              \
   (NFC_RW_POOL_BUF_SIZE - NFC_HDR_SIZE - NCI_MSG_OFFSET_SIZE - \
-   NCI_DATA_HDR_SIZE - T4T_CMD_MAX_HDR_SIZE)
+   NCI_DATA_HDR_SIZE - T4T_CMD_MAX_EXT_HDR_SIZE)
 
+#define RW_T4T_EXT_FIELD_CODING 0x01
+#define RW_T4T_DDO_LC_FIELD_CODING 0x02
+
+#define RW_T4T_BER_TLV_LENGTH_1_BYTE 0x01
+#define RW_T4T_BER_TLV_LENGTH_2_BYTES 0x02
+#define RW_T4T_BER_TLV_LENGTH_3_BYTES 0x03
+
+/* Minimum data header in command APDU data:
+ * ODO: 54 00 xxyyzz: tag '54' with 3-byte offset xxyyzz
+ * DDO: 53 Ld {data to be written to the ENDEF File}
+ * Ld (data length) can be 1, 2 or 3 bytes
+ */
+#define RW_T4T_ODO_DDO_HEADER_MIN_LENGTH 0x06 /* ODO + tag '53' */
+/* Ld encoded on two bytes with '81' tag and N=0 to 255
+ * for data field length coded on one byte */
+#define RW_T4T_ODO_DDO_HEADER_2BYTES_LENGTH 8 /* ODO + tag '53' + '81' + N */
 /* Mandatory NDEF file control */
 typedef struct {
   uint16_t file_id;       /* File Identifier          */
-  uint16_t max_file_size; /* Max NDEF file size       */
+  uint32_t max_file_size; /* Max NDEF file size       */
   uint8_t read_access;    /* read access condition    */
   uint8_t write_access;   /* write access condition   */
+  uint8_t nlen_size;      /* (E)NLEN size (2 or 4 bytes) */
 } tRW_T4T_NDEF_FC;
 
 /* Capability Container */
@@ -569,10 +589,11 @@
   uint8_t version;               /* currently effective version      */
   TIMER_LIST_ENT timer;          /* timeout for each API call        */
 
-  uint16_t ndef_length;    /* length of NDEF data              */
-  uint8_t* p_update_data;  /* pointer of data to update        */
-  uint16_t rw_length;      /* remaining bytes to read/write    */
-  uint16_t rw_offset;      /* remaining offset to read/write   */
+  uint32_t ndef_length;   /* length of NDEF data              */
+  uint8_t* p_update_data; /* pointer of data to update        */
+  uint32_t rw_length;     /* remaining bytes to read/write    */
+  uint32_t rw_offset;     /* remaining offset to read/write   */
+
   NFC_HDR* p_data_to_free; /* GKI buffet to delete after done  */
 
   tRW_T4T_CC cc_file; /* Capability Container File        */
@@ -589,6 +610,7 @@
   uint16_t max_update_size; /* max updating size per a command  */
   uint16_t card_size;
   uint8_t card_type;
+  uint8_t intl_flags; /* flags for internal information   */
 } tRW_T4T_CB;
 
 /* RW retransmission statistics */
@@ -738,6 +760,7 @@
   RW_I93_STM_M24LR64_R,              /* STM M24LR64-R                    */
   RW_I93_STM_M24LR04E_R,             /* STM M24LR04E-R                   */
   RW_I93_STM_M24LR16E_R,             /* STM M24LR16E-R                   */
+  RW_I93_STM_M24LR16D_W,             /* STM M24LR16D-W                   */
   RW_I93_STM_M24LR64E_R,             /* STM M24LR64E-R                   */
   RW_I93_STM_ST25DV04K,              /* STM ST25DV04K                    */
   RW_I93_STM_ST25DVHIK,              /* STM ST25DV 16K OR 64K            */
diff --git a/src/nfc/include/tags_defs.h b/src/nfc/include/tags_defs.h
index a9b5df2..9572286 100644
--- a/src/nfc/include/tags_defs.h
+++ b/src/nfc/include/tags_defs.h
@@ -366,15 +366,24 @@
 #define T4T_CMD_MIN_HDR_SIZE 4 /* CLA, INS, P1, P2 */
 #define T4T_CMD_MAX_HDR_SIZE 5 /* CLA, INS, P1, P2, Lc */
 
+/* CLA, INS, P1, P2, Data ODO */
+#define T4T_CMD_MIN_EXT_HDR_SIZE 9
+/* CLA, INS, P1, P2, Lc, Data ODO, Le
+ * with Lc and Le coded using Extended Field Coding */
+#define T4T_CMD_MAX_EXT_HDR_SIZE 15
+
+#define T4T_VERSION_3_0 0x30 /* version 3.0 */
 #define T4T_VERSION_2_0 0x20 /* version 2.0 */
 #define T4T_VERSION_1_0 0x10 /* version 1.0 */
-#define T4T_MY_VERSION T4T_VERSION_2_0
+#define T4T_MY_VERSION T4T_VERSION_3_0
 #define T4T_GET_MAJOR_VERSION(x) ((x) >> 4)
 
 #define T4T_CMD_CLASS 0x00
 #define T4T_CMD_INS_SELECT 0xA4
 #define T4T_CMD_INS_READ_BINARY 0xB0
 #define T4T_CMD_INS_UPDATE_BINARY 0xD6
+#define T4T_CMD_INS_READ_BINARY_ODO 0xB1
+#define T4T_CMD_INS_UPDATE_BINARY_ODO 0xD7
 #define T4T_CMD_DES_CLASS 0x90
 #define T4T_CMD_INS_GET_HW_VERSION 0x60
 #define T4T_CMD_CREATE_AID 0xCA
@@ -415,6 +424,8 @@
 
 #define T4T_VERSION_OFFSET_IN_CC 0x02
 #define T4T_FC_TLV_OFFSET_IN_CC 0x07
+/* size of T(1),L(1),V(8) for extended NDEF file control */
+#define T4T_ENDEF_FC_V_FIELD_OFFSET 0x09
 /* Offset of Write access byte from type field in CC */
 #define T4T_FC_WRITE_ACCESS_OFFSET_IN_TLV 0x07
 
@@ -425,8 +436,18 @@
 /* size of V(6) for file control */
 #define T4T_FILE_CONTROL_LENGTH 0x06
 
+#define T4T_ENDEF_FILE_CONTROL_TYPE 0x06 /* Extended NDEF File Control Type */
+/* size of T(1),L(1),V(8) for extended NDEF file control */
+#define T4T_ENDEF_FILE_CONTROL_TLV_SIZE 0x0A
+/* size of V(8) for extended NDEF file control */
+#define T4T_ENDEF_FILE_CONTROL_LENGTH 0x08
+
 /* read access granted without any security */
 #define T4T_FC_READ_ACCESS 0x00
+/* no read access granted at all */
+#define T4T_FC_NO_READ_ACCESS 0xFF
+/* proprietary read access range start */
+#define T4T_FC_READ_ACCESS_PROP_START 0x80
 /* write access granted without any security */
 #define T4T_FC_WRITE_ACCESS 0x00
 /* proprietary write access range start */
@@ -435,6 +456,7 @@
 #define T4T_FC_NO_WRITE_ACCESS 0xFF
 
 #define T4T_FILE_LENGTH_SIZE 0x02
+#define T4T_EFILE_LENGTH_SIZE 0x04
 #define T4T_ADDI_FRAME_RESP 0xAFU
 #define T4T_DES_GET_VERSION_LEN 0x09
 #define T4T_SIZE_IDENTIFIER_2K 0x16U
@@ -660,7 +682,10 @@
 /* IC Reference for M24LR16E-R: 01001110(b), blockSize: 4, numberBlocks: 0x200
  */
 #define I93_IC_REF_STM_M24LR16E_R 0x4E
-/* IC Reference for M24LR64E-R: 01011110(b), blockSize: 4, numberBlocks: 0x800
+/* IC Reference for M24LR16D-W: 01001101(b), blockSize: 4, numberBlocks: 0x200
+ */
+#define I93_IC_REF_STM_M24LR16D_W 0x4D
+/* IC Reference for M24LR64D-W: 01011110(b), blockSize: 4, numberBlocks: 0x800
  */
 #define I93_IC_REF_STM_M24LR64E_R 0x5E
 /* IC Reference for ST25DV04K: 00100100(b), blockSize: 4, numberBlocks: 0x80
diff --git a/src/nfc/nci/nci_hrcv.cc b/src/nfc/nci/nci_hrcv.cc
index 6dc1ae4..7be4c21 100644
--- a/src/nfc/nci/nci_hrcv.cc
+++ b/src/nfc/nci/nci_hrcv.cc
@@ -275,6 +275,10 @@
       break;
 
     case NCI_MSG_RF_FIELD:
+      if (p_msg->len < 4) {
+        android_errorWriteLog(0x534e4554, "176582502");
+        return;
+      }
       nfc_ncif_proc_rf_field_ntf(*pp);
       break;
 
@@ -299,6 +303,10 @@
 #endif
 #endif
     case NCI_MSG_RF_ISO_DEP_NAK_PRESENCE:
+      if (p_msg->len < 4) {
+        android_errorWriteLog(0x534e4554, "176582502");
+        return;
+      }
       nfc_ncif_proc_isodep_nak_presence_check_status(*pp, true);
       break;
     default:
@@ -325,7 +333,7 @@
   tNFC_RESPONSE_CBACK* p_cback = nfc_cb.p_resp_cback;
   tNFC_RESPONSE nfc_response;
   tNFC_RESPONSE_EVT event = NFC_NFCEE_INFO_REVT;
-  uint8_t* p_old = nfc_cb.last_cmd;
+  uint8_t* p_old = nfc_cb.last_nfcee_cmd;
 
   /* find the start of the NCI message and parse the NCI header */
   p = (uint8_t*)(p_msg + 1) + p_msg->offset;
@@ -356,6 +364,8 @@
         nfc_response.mode_set.status = *pp;
       } else {
         nfc_response.mode_set.status = NFC_STATUS_FAILED;
+        android_errorWriteLog(0x534e4554, "176203800");
+        return;
       }
       nfc_response.mode_set.nfcee_id = *p_old++;
       nfc_response.mode_set.mode = *p_old++;
@@ -402,7 +412,7 @@
   tNFC_RESPONSE_CBACK* p_cback = nfc_cb.p_resp_cback;
   tNFC_RESPONSE nfc_response;
   tNFC_RESPONSE_EVT event = NFC_NFCEE_INFO_REVT;
-  uint8_t* p_old = nfc_cb.last_cmd;
+  uint8_t* p_old = nfc_cb.last_nfcee_cmd;
   uint8_t xx;
   uint8_t yy;
   tNFC_NFCEE_TLV* p_tlv;
diff --git a/src/nfc/nfc/nfc_ncif.cc b/src/nfc/nfc/nfc_ncif.cc
index c786af1..0fc0fbf 100644
--- a/src/nfc/nfc/nfc_ncif.cc
+++ b/src/nfc/nfc/nfc_ncif.cc
@@ -282,6 +282,11 @@
       ps = (uint8_t*)(p_buf + 1) + p_buf->offset;
       memcpy(nfc_cb.last_hdr, ps, NFC_SAVED_HDR_SIZE);
       memcpy(nfc_cb.last_cmd, ps + NCI_MSG_HDR_SIZE, NFC_SAVED_CMD_SIZE);
+      // Check first byte to check if this is an NFCEE command
+      if (*ps == ((NCI_MT_CMD << NCI_MT_SHIFT) | NCI_GID_EE_MANAGE)) {
+        memcpy(nfc_cb.last_nfcee_cmd, ps + NCI_MSG_HDR_SIZE,
+               NFC_SAVED_CMD_SIZE);
+      }
       if (p_buf->layer_specific == NFC_WAIT_RSP_VSC) {
         /* save the callback for NCI VSCs)  */
         nfc_cb.p_vsc_cback = (void*)((tNFC_NCI_VS_MSG*)p_buf)->p_cback;
@@ -1647,7 +1652,7 @@
 
   status = *p_len > 0 ? *p++ : NCI_STATUS_FAILED;
   if (*p_len > 2 && is_ntf) {
-    LOG(ERROR) << StringPrintf("reset notification!!:0x%x ", status);
+    LOG(WARNING) << StringPrintf("reset notification!!:0x%x ", status);
     /* clean up, if the state is OPEN
      * FW does not report reset ntf right now */
     if (status == NCI2_0_RESET_TRIGGER_TYPE_CORE_RESET_CMD_RECEIVED ||
@@ -1663,7 +1668,7 @@
       status = NCI_STATUS_OK;
     } else {
       /* CORE_RESET_NTF received error case , trigger recovery*/
-      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+      LOG(ERROR) << StringPrintf(
           "CORE_RESET_NTF Received status nfc_state : 0x%x : 0x%x", status,
           nfc_cb.nfc_state);
       nfc_ncif_cmd_timeout();
@@ -2046,7 +2051,7 @@
   LOG(ERROR) << StringPrintf("%s", __func__);
   tNFC_RESPONSE nfc_response;
   nfc_response.mode_set.status = NCI_STATUS_FAILED;
-  nfc_response.mode_set.nfcee_id = *nfc_cb.last_cmd;
+  nfc_response.mode_set.nfcee_id = *nfc_cb.last_nfcee_cmd;
   nfc_response.mode_set.mode = NCI_NFCEE_MD_DEACTIVATE;
 
   tNFC_RESPONSE_CBACK* p_cback = nfc_cb.p_resp_cback;
diff --git a/src/nfc/tags/rw_i93.cc b/src/nfc/tags/rw_i93.cc
index de48f95..d4391d2 100644
--- a/src/nfc/tags/rw_i93.cc
+++ b/src/nfc/tags/rw_i93.cc
@@ -39,6 +39,7 @@
 using android::base::StringPrintf;
 
 extern bool nfc_debug_enabled;
+extern unsigned char appl_dta_mode_flag;
 
 /* Response timeout     */
 #define RW_I93_TOUT_RESP 1000
@@ -109,6 +110,8 @@
       p_i93->product_version = RW_I93_STM_M24LR04E_R;
     else if (p_i93->ic_reference == I93_IC_REF_STM_M24LR16E_R)
       p_i93->product_version = RW_I93_STM_M24LR16E_R;
+    else if (p_i93->ic_reference == I93_IC_REF_STM_M24LR16D_W)
+      p_i93->product_version = RW_I93_STM_M24LR16D_W;
     else if (p_i93->ic_reference == I93_IC_REF_STM_M24LR64E_R)
       p_i93->product_version = RW_I93_STM_M24LR64E_R;
     else if (p_i93->ic_reference == I93_IC_REF_STM_ST25DVHIK)
@@ -361,12 +364,14 @@
         **  M24LR64-R:  001011xx(b), blockSize: 4, numberBlocks: 0x800
         **  M24LR04E-R: 01011010(b), blockSize: 4, numberBlocks: 0x80
         **  M24LR16E-R: 01001110(b), blockSize: 4, numberBlocks: 0x200
+        **  M24LR16D-W: 01001101(b), blockSize: 4, numberBlocks: 0x200
         **  M24LR64E-R: 01011110(b), blockSize: 4, numberBlocks: 0x800
         */
         if ((p_i93->product_version == RW_I93_STM_M24LR16E_R) ||
+            (p_i93->product_version == RW_I93_STM_M24LR16D_W) ||
             (p_i93->product_version == RW_I93_STM_M24LR64E_R)) {
           /*
-          ** M24LR16E-R or M24LR64E-R returns system information
+          ** M24LR16E-R or M24LR16D-W or M24LR64E-R returns system information
           ** without memory size, if option flag is not set.
           ** LRIS64K and M24LR64-R return error if option flag is not
           ** set.
@@ -652,11 +657,17 @@
     rw_cb.tcb.i93.p_retry_cmd = nullptr;
   }
 
+  uint16_t msg_size = sizeof(NFC_HDR) + p_msg->offset + p_msg->len;
+
   rw_cb.tcb.i93.p_retry_cmd = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID);
 
-  if (rw_cb.tcb.i93.p_retry_cmd) {
-    memcpy(rw_cb.tcb.i93.p_retry_cmd, p_msg,
-           sizeof(NFC_HDR) + p_msg->offset + p_msg->len);
+  if (rw_cb.tcb.i93.p_retry_cmd &&
+      GKI_get_pool_bufsize(NFC_RW_POOL_ID) >= msg_size) {
+    memcpy(rw_cb.tcb.i93.p_retry_cmd, p_msg, msg_size);
+  } else {
+    LOG(ERROR) << StringPrintf("Memory allocation error");
+    android_errorWriteLog(0x534e4554, "157650357");
+    return false;
   }
 
   if (NFC_SendData(NFC_RF_CONN_ID, p_msg) != NFC_STATUS_OK) {
@@ -960,8 +971,8 @@
 **
 ** Description      Send Lock Block Request to VICC
 **
-**                  STM LRIS64K, M24LR64-R, M24LR04E-R, M24LR16E-R, M24LR64E-R
-**                  do not support.
+**                  STM LRIS64K, M24LR64-R, M24LR04E-R, M24LR16E-R, M24LR64E-R,
+**                  M24LR16D-W do not support.
 **
 ** Returns          tNFC_STATUS
 **
@@ -1678,7 +1689,8 @@
     }
 
     if (p_i93->uid[1] == I93_UID_IC_MFG_CODE_STM) {
-      /* LRIS64K, M24LR64-R, M24LR04E-R, M24LR16E-R, M24LR64E-R requires
+      /* LRIS64K, M24LR64-R, M24LR04E-R, M24LR16E-R, M24LR16D-W, M24LR64E-R
+      ** require
       ** - The max number of blocks is 32 and they are all located in the
       **   same sector.
       ** - The sector is 32 blocks of 4 bytes.
@@ -1687,6 +1699,7 @@
           (p_i93->product_version == RW_I93_STM_M24LR64_R) ||
           (p_i93->product_version == RW_I93_STM_M24LR04E_R) ||
           (p_i93->product_version == RW_I93_STM_M24LR16E_R) ||
+          (p_i93->product_version == RW_I93_STM_M24LR16D_W) ||
           (p_i93->product_version == RW_I93_STM_M24LR64E_R)) {
         if (num_block > I93_STM_MAX_BLOCKS_PER_READ)
           num_block = I93_STM_MAX_BLOCKS_PER_READ;
@@ -2274,6 +2287,9 @@
 
     if (p_resp->len > 0) {
       (*(rw_cb.p_cback))(RW_I93_NDEF_READ_EVT, &rw_data);
+    } else {
+       // free buffer, if len == 0
+       GKI_freebuf(p_resp);
     }
 
     /* this will make read data from next block */
@@ -3957,8 +3973,10 @@
   if (rw_cb.tcb.i93.uid[0] != I93_UID_FIRST_BYTE) {
     status = rw_i93_send_cmd_inventory(nullptr, false, 0x00);
     sub_state = RW_I93_SUBSTATE_WAIT_UID;
-  } else if ((rw_cb.tcb.i93.num_block == 0) ||
-             (rw_cb.tcb.i93.block_size == 0)) {
+
+  } else if (((rw_cb.tcb.i93.num_block == 0) ||
+              (rw_cb.tcb.i93.block_size == 0)) &&
+             (!appl_dta_mode_flag)) {
     status =
         rw_i93_send_cmd_get_sys_info(rw_cb.tcb.i93.uid, I93_FLAG_PROT_EXT_NO);
     sub_state = RW_I93_SUBSTATE_WAIT_SYS_INFO;
@@ -4417,6 +4435,8 @@
       return "M24LR04E";
     case RW_I93_STM_M24LR16E_R:
       return "M24LR16E";
+    case RW_I93_STM_M24LR16D_W:
+      return "M24LR16D-W";
     case RW_I93_STM_M24LR64E_R:
       return "M24LR64E";
     case RW_I93_STM_ST25DV04K:
diff --git a/src/nfc/tags/rw_main.cc b/src/nfc/tags/rw_main.cc
index 071b966..abd8bbe 100644
--- a/src/nfc/tags/rw_main.cc
+++ b/src/nfc/tags/rw_main.cc
@@ -34,6 +34,7 @@
 #include "bt_types.h"
 #include "nci_hmsgs.h"
 #include "nfc_api.h"
+#include "nfc_int.h"
 #include "rw_api.h"
 #include "rw_int.h"
 
@@ -220,6 +221,38 @@
     return (NFC_STATUS_FAILED);
   }
 
+  switch (rw_cb.tcb_type) {
+    case RW_CB_TYPE_T1T: {
+      nfc_stop_quick_timer(&rw_cb.tcb.t1t.timer);
+      break;
+    }
+    case RW_CB_TYPE_T2T: {
+      nfc_stop_quick_timer(&rw_cb.tcb.t2t.t2_timer);
+      break;
+    }
+    case RW_CB_TYPE_T3T: {
+      nfc_stop_quick_timer(&rw_cb.tcb.t3t.timer);
+      nfc_stop_quick_timer(&rw_cb.tcb.t3t.poll_timer);
+      break;
+    }
+    case RW_CB_TYPE_T4T: {
+      nfc_stop_quick_timer(&rw_cb.tcb.t4t.timer);
+      break;
+    }
+    case RW_CB_TYPE_T5T: {
+      nfc_stop_quick_timer(&rw_cb.tcb.i93.timer);
+      break;
+    }
+    case RW_CB_TYPE_MIFARE: {
+      nfc_stop_quick_timer(&rw_cb.tcb.mfc.timer);
+      nfc_stop_quick_timer(&rw_cb.tcb.mfc.mfc_timer);
+      break;
+    }
+    case RW_CB_TYPE_UNKNOWN: {
+      break;
+    }
+  }
+
   /* Reset tag-specific area of control block */
   memset(&rw_cb.tcb, 0, sizeof(tRW_TCB));
 
diff --git a/src/nfc/tags/rw_mfc.cc b/src/nfc/tags/rw_mfc.cc
index a77ac63..8803f2d 100644
--- a/src/nfc/tags/rw_mfc.cc
+++ b/src/nfc/tags/rw_mfc.cc
@@ -22,6 +22,7 @@
  ******************************************************************************/
 #include <android-base/stringprintf.h>
 #include <base/logging.h>
+#include <log/log.h>
 #include <string.h>
 #include "bt_types.h"
 #include "nfc_target.h"
@@ -693,7 +694,9 @@
   }
 
   if ((p_mfc->state != RW_MFC_STATE_IDLE) && (mfc_data == NULL)) {
-    LOG(ERROR) << StringPrintf("%s NULL pointer", __func__);
+    if (p_mfc->state != RW_MFC_STATE_NOT_ACTIVATED) {
+      LOG(ERROR) << StringPrintf("%s NULL pointer", __func__);
+    }
     return;
   }
 
@@ -1004,6 +1007,7 @@
   NFC_HDR* mfc_data;
   uint16_t len;
   uint16_t offset;
+  uint16_t saved_length;
   bool failed = false;
   bool done = false;
   tRW_READ_DATA evt_data;
@@ -1025,6 +1029,7 @@
       /* On the first read, adjust for any partial block offset */
       offset = 0;
       len = RW_MFC_1K_BLOCK_SIZE;
+      saved_length = p_mfc->ndef_length;
 
       if (p_mfc->work_offset == 0) {
         /* The Ndef Message offset may be present in the read 16 bytes */
@@ -1036,14 +1041,18 @@
         }
       }
 
-      /* Skip all reserved and lock bytes */
-      while ((offset < len) && (p_mfc->work_offset < p_mfc->ndef_length))
+      if (!failed && saved_length >= p_mfc->ndef_length) {
+        /* Skip all reserved and lock bytes */
+        while ((offset < len) && (p_mfc->work_offset < p_mfc->ndef_length))
 
-      {
-        /* Collect the NDEF Message */
-        p_mfc->p_ndef_buffer[p_mfc->work_offset] = p[offset];
-        p_mfc->work_offset++;
-        offset++;
+        {
+          /* Collect the NDEF Message */
+          p_mfc->p_ndef_buffer[p_mfc->work_offset] = p[offset];
+          p_mfc->work_offset++;
+          offset++;
+        }
+      } else {
+        android_errorWriteLog(0x534e4554, "178725766");
       }
 
       if (p_mfc->work_offset >= p_mfc->ndef_length) {
diff --git a/src/nfc/tags/rw_t3t.cc b/src/nfc/tags/rw_t3t.cc
index 7a4f9a4..0e91498 100644
--- a/src/nfc/tags/rw_t3t.cc
+++ b/src/nfc/tags/rw_t3t.cc
@@ -248,6 +248,14 @@
       /* For GetSystemCode: tag did not respond to requested POLL */
       rw_t3t_handle_get_system_codes_cplt();
       return;
+    } else if ((p_cb->flags & (RW_T3T_FL_W4_PRESENCE_CHECK_POLL_RSP |
+                               RW_T3T_FL_W4_GET_SC_POLL_RSP |
+                               RW_T3T_FL_W4_FMT_FELICA_LITE_POLL_RSP |
+                               RW_T3T_FL_W4_SRO_FELICA_LITE_POLL_RSP |
+                               RW_T3T_FL_W4_NDEF_DETECT_POLL_RSP |
+                               RW_T3T_FL_W4_USER_POLL_RSP))) {
+      /* Tag did not respond correctly to requested POLL */
+      return;
     }
     /* Retry sending command if retry-count < max */
     else if (rw_cb.cur_retry < RW_MAX_RETRIES) {
@@ -271,8 +279,7 @@
                                 p_cb->cur_tout);
           return;
         } else {
-          /* failure - could not send buffer */
-          GKI_freebuf(p_cmd_buf);
+          android_errorWriteLog(0x534e4554, "179687208");
         }
       }
     } else {
@@ -368,6 +375,7 @@
     rw_t3t_handle_ndef_detect_poll_rsp(p_cb, nci_status, num_responses);
   } else {
     /* Handle POLL ntf in response to RW_T3tPoll */
+    p_cb->flags &= ~RW_T3T_FL_W4_USER_POLL_RSP;
     evt_data.t3t_poll.status = nci_status;
     if (evt_data.t3t_poll.status == NCI_STATUS_OK) {
       evt_data.t3t_poll.rc = p_cb->cur_poll_rc;
@@ -2844,6 +2852,7 @@
     /* start timer for waiting for responses */
     p_cb->cur_poll_rc = rc;
     p_cb->rw_state = RW_T3T_STATE_COMMAND_PENDING;
+    p_cb->flags |= RW_T3T_FL_W4_USER_POLL_RSP;
     rw_t3t_start_poll_timer(p_cb);
   }
 
diff --git a/src/nfc/tags/rw_t4t.cc b/src/nfc/tags/rw_t4t.cc
index b7b6144..e5d3ef2 100644
--- a/src/nfc/tags/rw_t4t.cc
+++ b/src/nfc/tags/rw_t4t.cc
@@ -39,6 +39,7 @@
 using android::base::StringPrintf;
 
 extern bool nfc_debug_enabled;
+extern unsigned char appl_dta_mode_flag;
 
 /* main state */
 /* T4T is not activated                 */
@@ -78,7 +79,8 @@
 #define RW_T4T_SUBSTATE_WAIT_UPDATE_NLEN 0x07
 /* waiting for response of updating CC      */
 #define RW_T4T_SUBSTATE_WAIT_UPDATE_CC 0x08
-
+/* waiting for response of reading CC       */
+#define RW_T4T_SUBSTATE_WAIT_ENDEF_FILE_CTRL_TLV 0x11
 #define RW_T4T_SUBSTATE_WAIT_GET_HW_VERSION 0x09
 #define RW_T4T_SUBSTATE_WAIT_GET_SW_VERSION 0x0A
 #define RW_T4T_SUBSTATE_WAIT_GET_UID 0x0B
@@ -93,9 +95,9 @@
 
 static bool rw_t4t_send_to_lower(NFC_HDR* p_c_apdu);
 static bool rw_t4t_select_file(uint16_t file_id);
-static bool rw_t4t_read_file(uint16_t offset, uint16_t length,
+static bool rw_t4t_read_file(uint32_t offset, uint32_t length,
                              bool is_continue);
-static bool rw_t4t_update_nlen(uint16_t ndef_len);
+static bool rw_t4t_update_nlen(uint32_t ndef_len);
 static bool rw_t4t_update_file(void);
 static bool rw_t4t_update_cc_to_readonly(void);
 static bool rw_t4t_select_application(uint8_t version);
@@ -141,6 +143,159 @@
 
 /*******************************************************************************
 **
+** Function         rw_t4t_set_ber_tlv
+**
+** Description      Send UpdateBinary Command with ODO and DDO
+**
+** Returns          TRUE if success
+**
+*******************************************************************************/
+static bool rw_t4t_set_ber_tlv(NFC_HDR* p_c_apdu, uint8_t* p, uint32_t length) {
+  tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t;
+  uint32_t data_length, tmp_length, tmp_offset;
+  uint8_t length_size, data_header = 0;
+
+  p_c_apdu->len = T4T_CMD_MIN_EXT_HDR_SIZE + 1; /* tag 53 */
+  /* Remove min data header for encoding offset and data length */
+  /* length is Lc data length */
+  /* data_length is the length of the data to be written to the ENDEF
+   * File */
+  data_length = length;
+  if (length <= 0x7F) {
+    /* Default Short Field Coding can be used */
+    /* BER-TLV length coded on one byte */
+    length_size = RW_T4T_BER_TLV_LENGTH_1_BYTE;
+
+  } else if ((length + RW_T4T_ODO_DDO_HEADER_2BYTES_LENGTH) <= 0xFF) {
+    /* Default Short Field Coding can be used */
+    /* BER-TLV length coded on two bytes: (81h+N) with N=0 to 255 */
+    length_size = RW_T4T_BER_TLV_LENGTH_2_BYTES;
+
+  } else {
+    if (p_t4t->intl_flags & RW_T4T_EXT_FIELD_CODING) {
+      /* Extended Field Coding can be used */
+      if (length <= 0xFF) {
+        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+            "%s - Extended Field Coding used, 2-byte coding "
+            "for BER-TLV",
+            __func__);
+        /* BER-TLV length coded on two bytes still possible */
+        length_size = RW_T4T_BER_TLV_LENGTH_2_BYTES;
+
+      } else {
+        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+            "%s - Extended Field Coding used, 3-byte coding "
+            "for BER-TLV",
+            __func__);
+        /* BER-TLV length coded on three bytes:
+         * (82h+N) with N=0 to 65535 */
+        length_size = RW_T4T_BER_TLV_LENGTH_3_BYTES;
+      }
+    } else {
+      DLOG_IF(INFO, nfc_debug_enabled)
+          << StringPrintf("%s - Short Field Coding used", __func__);
+      /* Only Short Field Coding can be used */
+      /* Write a max of 255 bytes in data field,
+       * as Lc=00 is reserved for Extended Field coding */
+      /* BER-TLV length coded on two bytes */
+      length_size = RW_T4T_BER_TLV_LENGTH_2_BYTES;
+      length = 0;
+    }
+  }
+
+  data_header = RW_T4T_ODO_DDO_HEADER_MIN_LENGTH + length_size;
+  if (length == 0) {
+    length = T4T_MAX_LENGTH_LC;
+    if (length <= p_t4t->max_update_size) {
+      data_length = T4T_MAX_LENGTH_LC - data_header;
+    } else {
+      /* Realign with MLc (taking into account header now) */
+      length = p_t4t->max_update_size;
+      data_length = p_t4t->max_update_size - data_header;
+    }
+  } else {
+    if ((length + data_header) <= p_t4t->max_update_size) {
+      length += data_header;
+    } else {
+      /* Realign with MLc (taking into account header now) */
+      length = p_t4t->max_update_size;
+      data_length = p_t4t->max_update_size - data_header;
+    }
+  }
+
+  UINT8_TO_BE_STREAM(p, T4T_CMD_CLASS);
+  UINT8_TO_BE_STREAM(p, T4T_CMD_INS_UPDATE_BINARY_ODO);
+  /* P1 P2 field */
+  UINT16_TO_BE_STREAM(p, 0x0000);
+  /* Lc field */
+  if (p_t4t->intl_flags & RW_T4T_EXT_FIELD_CODING) {
+    /* Coded over three bytes */
+    UINT8_TO_BE_STREAM(p, 0x00);
+    tmp_length = length;
+    tmp_length >>= 8;
+    UINT8_TO_BE_STREAM(p, ((uint8_t)tmp_length));
+    tmp_length = length;
+    UINT8_TO_BE_STREAM(p, ((uint8_t)tmp_length));
+    p_c_apdu->len += 3;
+  } else {
+    /* Coded over 1 byte */
+    UINT8_TO_BE_STREAM(p, ((uint8_t)length));
+    p_c_apdu->len += 1;
+  }
+
+  /* Data field containing data offset coded over 3 bytes
+   * followed by data to be written to the ENDEF File */
+  UINT16_TO_BE_STREAM(p, 0x5403);
+  tmp_offset = p_t4t->rw_offset;
+  tmp_offset >>= 16;
+  UINT8_TO_BE_STREAM(p, ((uint8_t)tmp_offset));
+  tmp_offset = p_t4t->rw_offset;
+  tmp_offset >>= 8;
+  UINT8_TO_BE_STREAM(p, ((uint8_t)tmp_offset));
+  tmp_offset = p_t4t->rw_offset;
+  UINT8_TO_BE_STREAM(p, ((uint8_t)tmp_offset));
+
+  UINT8_TO_BE_STREAM(p, 0x53);
+  /* Data length */
+  if (length_size == RW_T4T_BER_TLV_LENGTH_1_BYTE) {
+    /* Length coded over 1 byte */
+    UINT8_TO_BE_STREAM(p, data_length);
+    p_c_apdu->len += 1;
+  } else if (length_size == RW_T4T_BER_TLV_LENGTH_2_BYTES) {
+    UINT8_TO_BE_STREAM(p, 0x81);
+    UINT8_TO_BE_STREAM(p, data_length);
+    p_c_apdu->len += 2;
+  } else if ((length_size == RW_T4T_BER_TLV_LENGTH_3_BYTES) &&
+             (data_length <= 0xFFFF)) {
+    /* Length coded over 3 bytes */
+    UINT8_TO_BE_STREAM(p, 0x82);
+    UINT16_TO_BE_STREAM(p, (uint16_t)data_length);
+    p_c_apdu->len += 3;
+  } else {
+    LOG(ERROR) << StringPrintf(
+        "%s - Data to be written to MV3.0 tag exceeds 0xFFFF", __func__);
+    return false;
+  }
+
+  memcpy(p, p_t4t->p_update_data, data_length);
+
+  p_c_apdu->len += data_length;
+
+  if (!rw_t4t_send_to_lower(p_c_apdu)) {
+    return false;
+  }
+  /* Le field not present */
+
+  /* adjust offset, length and pointer for remaining data */
+  p_t4t->rw_offset += data_length;
+  p_t4t->rw_length -= data_length;
+  p_t4t->p_update_data += data_length;
+
+  return true;
+}
+
+/*******************************************************************************
+**
 ** Function         rw_t4t_get_hw_version
 **
 ** Description      Send get hw version cmd to peer
@@ -610,12 +765,13 @@
   NFC_HDR* p_c_apdu;
   uint8_t* p;
 
-  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("File ID:0x%04X", file_id);
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s - File ID:0x%04X", __func__, file_id);
 
   p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID);
 
   if (!p_c_apdu) {
-    LOG(ERROR) << StringPrintf("Cannot allocate buffer");
+    LOG(ERROR) << StringPrintf("%s - Cannot allocate buffer", __func__);
     return false;
   }
 
@@ -627,7 +783,8 @@
   UINT8_TO_BE_STREAM(p, T4T_CMD_P1_SELECT_BY_FILE_ID);
 
   /* if current version mapping is V2.0 */
-  if (rw_cb.tcb.t4t.version == T4T_VERSION_2_0) {
+  if ((rw_cb.tcb.t4t.version == T4T_VERSION_2_0) ||
+      (rw_cb.tcb.t4t.version == T4T_VERSION_3_0)) {
     UINT8_TO_BE_STREAM(p, T4T_CMD_P2_FIRST_OR_ONLY_0CH);
   } else /* version 1.0 */
   {
@@ -655,19 +812,21 @@
 ** Returns          TRUE if success
 **
 *******************************************************************************/
-static bool rw_t4t_read_file(uint16_t offset, uint16_t length,
+static bool rw_t4t_read_file(uint32_t offset, uint32_t length,
                              bool is_continue) {
   tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t;
   NFC_HDR* p_c_apdu;
   uint8_t* p;
+  uint32_t tmp_offset;
 
-  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-      "offset:%d, length:%d, is_continue:%d, ", offset, length, is_continue);
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s - offset:%d, length:%d, is_continue:%d, ", __func__,
+                      offset, length, is_continue);
 
   p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID);
 
   if (!p_c_apdu) {
-    LOG(ERROR) << StringPrintf("Cannot allocate buffer");
+    LOG(ERROR) << StringPrintf("%s - Cannot allocate buffer", __func__);
     return false;
   }
 
@@ -682,18 +841,97 @@
   /* adjust reading length if payload is bigger than max size per single command
    */
   if (length > p_t4t->max_read_size) {
-    length = (uint8_t)(p_t4t->max_read_size);
+    length = (uint32_t)(p_t4t->max_read_size);
   }
 
   p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
   p = (uint8_t*)(p_c_apdu + 1) + p_c_apdu->offset;
 
   UINT8_TO_BE_STREAM(p, (T4T_CMD_CLASS | rw_cb.tcb.t4t.channel));
-  UINT8_TO_BE_STREAM(p, T4T_CMD_INS_READ_BINARY);
-  UINT16_TO_BE_STREAM(p, offset);
-  UINT8_TO_BE_STREAM(p, length); /* Le */
+  if ((p_t4t->rw_offset + p_t4t->rw_length) > 0x7FFF) {
+    /* ReadBinary with ODO must be used */
+    if (p_t4t->cc_file.version >= T4T_VERSION_3_0) {
+      /* MV 3.0 tag */
+      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+          "%s - Read above 0x7FFF address offset detected", __func__);
 
-  p_c_apdu->len = T4T_CMD_MIN_HDR_SIZE + 1; /* adding Le */
+      p_c_apdu->len = T4T_CMD_MIN_EXT_HDR_SIZE;
+
+      UINT8_TO_BE_STREAM(p, T4T_CMD_INS_READ_BINARY_ODO);
+      /* P1 P2 field */
+      UINT16_TO_BE_STREAM(p, 0x0000);
+      /* Lc field */
+      if (p_t4t->intl_flags & RW_T4T_EXT_FIELD_CODING) {
+        /* Coded over three bytes */
+        UINT16_TO_BE_STREAM(p, 0x0000);
+        UINT8_TO_BE_STREAM(p, 0x05);
+        p_c_apdu->len += 3;
+      } else {
+        /* Coded over 1 byte */
+        UINT8_TO_BE_STREAM(p, 0x05);
+        p_c_apdu->len += 1;
+      }
+      p_t4t->intl_flags |= RW_T4T_DDO_LC_FIELD_CODING;
+
+      /* Data field containing address offset */
+      UINT16_TO_BE_STREAM(p, 0x5403);
+      tmp_offset = offset;
+      tmp_offset >>= 16;
+      UINT8_TO_BE_STREAM(p, ((uint8_t)tmp_offset));
+      tmp_offset = offset;
+      tmp_offset >>= 8;
+      UINT8_TO_BE_STREAM(p, ((uint8_t)tmp_offset));
+      tmp_offset = offset;
+      UINT8_TO_BE_STREAM(p, ((uint8_t)tmp_offset));
+
+      /* Le field */
+      if (length < p_t4t->max_read_size) {
+        /* For the last R-APDU, must consider the DDO '53h' tag and data length
+         * size in the response. As difficult to know which coding will be used
+         * for BER-TLV, safer to request the remaining maximum number of bytes
+         * the tag can send */
+        length = 0x0000;
+      }
+      if (p_t4t->intl_flags & RW_T4T_EXT_FIELD_CODING) {
+        /* If Lc is coded over 3 bytes, Le is coded over 2 bytes */
+        p_c_apdu->len += 2;
+        UINT16_TO_BE_STREAM(p, length);
+      } else {
+        /* Coded over 1 byte */
+        p_c_apdu->len += 1;
+        UINT8_TO_BE_STREAM(p, length);
+      }
+    } else {
+      LOG(ERROR) << StringPrintf("%s - Cannot read above 0x7FFF for MV2.0",
+                                 __func__);
+      return false;
+    }
+  } else {
+    /* MV 2.0 tag or MV 3.0 tag read below 32kB */
+    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+        "%s - Read below 0x8000 address offset detected", __func__);
+
+    UINT8_TO_BE_STREAM(p, T4T_CMD_INS_READ_BINARY);
+    /* Lc and Data fields absent */
+    UINT16_TO_BE_STREAM(p, offset);
+    if (p_t4t->intl_flags & RW_T4T_EXT_FIELD_CODING) {
+      /* Coded over three bytes with first one null */
+      p_c_apdu->len = T4T_CMD_MIN_HDR_SIZE + 3; /* adding Le */
+      UINT8_TO_BE_STREAM(p, 0x00);
+      UINT16_TO_BE_STREAM(p, length); /* Le */
+    } else {
+      /* If MLe=256 bytes, using UINT8_TO_BE_STREAM casts the length
+       * to Le=0x00 which is accepted by the specifications but not
+       * by all tags in the field. Force Le to 255 bytes to read the
+       * remaining bytes in two times
+       */
+      if (length == (T4T_MAX_LENGTH_LE + 1)) {
+        length = T4T_MAX_LENGTH_LE;
+      }
+      p_c_apdu->len = T4T_CMD_MIN_HDR_SIZE + 1; /* adding Le */
+      UINT8_TO_BE_STREAM(p, length);            /* Le */
+    }
+  }
 
   if (!rw_t4t_send_to_lower(p_c_apdu)) {
     return false;
@@ -711,16 +949,18 @@
 ** Returns          TRUE if success
 **
 *******************************************************************************/
-static bool rw_t4t_update_nlen(uint16_t ndef_len) {
+static bool rw_t4t_update_nlen(uint32_t ndef_len) {
+  tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t;
   NFC_HDR* p_c_apdu;
   uint8_t* p;
 
-  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("NLEN:%d", ndef_len);
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s - NLEN:%d", __func__, ndef_len);
 
   p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID);
 
   if (!p_c_apdu) {
-    LOG(ERROR) << StringPrintf("Cannot allocate buffer");
+    LOG(ERROR) << StringPrintf("%s - Cannot allocate buffer", __func__);
     return false;
   }
 
@@ -730,10 +970,14 @@
   UINT8_TO_BE_STREAM(p, T4T_CMD_CLASS);
   UINT8_TO_BE_STREAM(p, T4T_CMD_INS_UPDATE_BINARY);
   UINT16_TO_BE_STREAM(p, 0x0000); /* offset for NLEN */
-  UINT8_TO_BE_STREAM(p, T4T_FILE_LENGTH_SIZE);
-  UINT16_TO_BE_STREAM(p, ndef_len);
+  UINT8_TO_BE_STREAM(p, p_t4t->cc_file.ndef_fc.nlen_size);
+  if (p_t4t->cc_file.ndef_fc.nlen_size == T4T_FILE_LENGTH_SIZE) {
+    UINT16_TO_BE_STREAM(p, ndef_len);
+  } else {
+    UINT32_TO_BE_STREAM(p, ndef_len);
+  }
 
-  p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + T4T_FILE_LENGTH_SIZE;
+  p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + p_t4t->cc_file.ndef_fc.nlen_size;
 
   if (!rw_t4t_send_to_lower(p_c_apdu)) {
     return false;
@@ -755,48 +999,83 @@
   tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t;
   NFC_HDR* p_c_apdu;
   uint8_t* p;
-  uint16_t length;
+  uint32_t length;
 
-  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-      "rw_offset:%d, rw_length:%d", p_t4t->rw_offset, p_t4t->rw_length);
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s - rw_offset:%d, rw_length:%d", __func__,
+                      p_t4t->rw_offset, p_t4t->rw_length);
 
   p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID);
 
   if (!p_c_apdu) {
-    LOG(ERROR) << StringPrintf("Cannot allocate buffer");
+    LOG(ERROR) << StringPrintf("%s - Cannot allocate buffer", __func__);
     return false;
   }
 
   /* try to send all of remaining data */
   length = p_t4t->rw_length;
 
+  if (length == 0) {
+    LOG(ERROR) << StringPrintf("%s - Length to write can not be null",
+                               __func__);
+    return false;
+  }
+
   /* adjust updating length if payload is bigger than max size per single
    * command */
   if (length > p_t4t->max_update_size) {
-    length = (uint8_t)(p_t4t->max_update_size);
+    length = (uint32_t)(p_t4t->max_update_size);
   }
 
   p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
   p = (uint8_t*)(p_c_apdu + 1) + p_c_apdu->offset;
 
-  UINT8_TO_BE_STREAM(p, T4T_CMD_CLASS);
-  UINT8_TO_BE_STREAM(p, T4T_CMD_INS_UPDATE_BINARY);
-  UINT16_TO_BE_STREAM(p, p_t4t->rw_offset);
-  UINT8_TO_BE_STREAM(p, length);
+  if ((p_t4t->rw_offset + p_t4t->rw_length) > 0x7FFF) {
+    /* UpdateBinary with ODO and DDO */
+    if (p_t4t->cc_file.version >= T4T_VERSION_3_0) {
+      /* MV 3.0 tag */
+      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+          "%s - MV 3.0 detected, update NDEF Message size > 0x7FFF", __func__);
 
-  memcpy(p, p_t4t->p_update_data, length);
+      return rw_t4t_set_ber_tlv(p_c_apdu, p, length);
 
-  p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + length;
+    } else {
+      LOG(ERROR) << StringPrintf("%s - Cannot write above 0x7FFF for MV2.0",
+                                 __func__);
+      return false;
+    }
+  } else {
+    /* MV 2.0 or MV 3.0 tag */
+    /* ReadBinary with Standard Data structure used */
+    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+        "%s - NDEF Message to write < 0x8000, MV2.0 or MV3.0 tag", __func__);
 
-  if (!rw_t4t_send_to_lower(p_c_apdu)) {
-    return false;
+    UINT8_TO_BE_STREAM(p, T4T_CMD_CLASS);
+    UINT8_TO_BE_STREAM(p, T4T_CMD_INS_UPDATE_BINARY);
+    UINT16_TO_BE_STREAM(p, p_t4t->rw_offset);
+
+    /* Lc field encoded using Short Field Coding */
+    if (length > T4T_MAX_LENGTH_LC) {
+      /* Write a max of 255 bytes,
+       * as Lc=00 is reserved for Extended Field coding */
+      length = T4T_MAX_LENGTH_LC;
+    }
+    UINT8_TO_BE_STREAM(p, length);
+
+    memcpy(p, p_t4t->p_update_data, length);
+
+    p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + length;
+
+    if (!rw_t4t_send_to_lower(p_c_apdu)) {
+      return false;
+    }
+
+    /* adjust offset, length and pointer for remaining data */
+    p_t4t->rw_offset += length;
+    p_t4t->rw_length -= length;
+    p_t4t->p_update_data += length;
   }
 
-  /* adjust offset, length and pointer for remaining data */
-  p_t4t->rw_offset += length;
-  p_t4t->rw_length -= length;
-  p_t4t->p_update_data += length;
-
   return true;
 }
 
@@ -814,12 +1093,12 @@
   uint8_t* p;
 
   DLOG_IF(INFO, nfc_debug_enabled)
-      << StringPrintf("Remove Write access from CC");
+      << StringPrintf("%s - Remove Write access from CC", __func__);
 
   p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID);
 
   if (!p_c_apdu) {
-    LOG(ERROR) << StringPrintf("Cannot allocate buffer");
+    LOG(ERROR) << StringPrintf("%s - Cannot allocate buffer", __func__);
     return false;
   }
 
@@ -859,6 +1138,7 @@
 **                        CLA INS P1 P2 Lc Data(AID)      Le
 **                  V1.0: 00  A4  04 00 07 D2760000850100 -
 **                  V2.0: 00  A4  04 00 07 D2760000850101 00
+**                  V3.0: 00  A4  04 00 07 D2760000850101 00
 **
 ** Returns          TRUE if success
 **
@@ -867,12 +1147,13 @@
   NFC_HDR* p_c_apdu;
   uint8_t* p;
 
-  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("version:0x%X", version);
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s - version:0x%X", __func__, version);
 
   p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID);
 
   if (!p_c_apdu) {
-    LOG(ERROR) << StringPrintf("Cannot allocate buffer");
+    LOG(ERROR) << StringPrintf("%s - Cannot allocate buffer", __func__);
     return false;
   }
 
@@ -891,7 +1172,8 @@
     memcpy(p, t4t_v10_ndef_tag_aid, T4T_V10_NDEF_TAG_AID_LEN);
 
     p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + T4T_V10_NDEF_TAG_AID_LEN;
-  } else if (version == T4T_VERSION_2_0) /* this is for V2.0 */
+  } else if ((version == T4T_VERSION_2_0) || /* this is for V2.0 */
+             (version == T4T_VERSION_3_0))   /* this is for V3.0 */
   {
     UINT8_TO_BE_STREAM(p, T4T_V20_NDEF_TAG_AID_LEN);
 
@@ -927,61 +1209,71 @@
   DLOG_IF(INFO, nfc_debug_enabled) << __func__;
 
   if (p_t4t->cc_file.cclen < T4T_CC_FILE_MIN_LEN) {
-    LOG(ERROR) << StringPrintf("CCLEN (%d) is too short", p_t4t->cc_file.cclen);
+    LOG(ERROR) << StringPrintf("%s - CCLEN (%d) is too short", __func__,
+                               p_t4t->cc_file.cclen);
     return false;
   }
 
-  if (T4T_GET_MAJOR_VERSION(p_t4t->cc_file.version) !=
+  if (T4T_GET_MAJOR_VERSION(p_t4t->cc_file.version) >
       T4T_GET_MAJOR_VERSION(p_t4t->version)) {
     LOG(ERROR) << StringPrintf(
-        "Peer version (0x%02X) is matched to ours "
+        "%s - Peer version (0x%02X) mismatched to ours "
         "(0x%02X)",
-        p_t4t->cc_file.version, p_t4t->version);
+        __func__, p_t4t->cc_file.version, p_t4t->version);
+
     return false;
   }
 
   if (p_t4t->cc_file.max_le < 0x000F) {
-    LOG(ERROR) << StringPrintf("MaxLe (%d) is too small",
+    LOG(ERROR) << StringPrintf("%s - MaxLe (%d) is too small", __func__,
                                p_t4t->cc_file.max_le);
     return false;
   }
 
-  if (p_t4t->cc_file.max_lc < 0x0001) {
-    LOG(ERROR) << StringPrintf("MaxLc (%d) is too small",
+  if (p_t4t->cc_file.max_lc < 0x0001 ||
+      ((p_t4t->cc_file.max_lc < 0x000D) && appl_dta_mode_flag)) {
+    LOG(ERROR) << StringPrintf("%s - MaxLc (%d) is too small", __func__,
                                p_t4t->cc_file.max_lc);
     return false;
   }
 
   if ((p_t4t->cc_file.ndef_fc.file_id == T4T_CC_FILE_ID) ||
       (p_t4t->cc_file.ndef_fc.file_id == 0xE102) ||
-      (p_t4t->cc_file.ndef_fc.file_id == 0xE103) ||
       ((p_t4t->cc_file.ndef_fc.file_id == 0x0000) &&
-       (p_t4t->cc_file.version == 0x20)) ||
+       ((p_t4t->cc_file.version == 0x20) ||
+        (p_t4t->cc_file.version == 0x30))) ||
       (p_t4t->cc_file.ndef_fc.file_id == 0x3F00) ||
       (p_t4t->cc_file.ndef_fc.file_id == 0x3FFF) ||
       (p_t4t->cc_file.ndef_fc.file_id == 0xFFFF)) {
-    LOG(ERROR) << StringPrintf("File ID (0x%04X) is invalid",
+    LOG(ERROR) << StringPrintf("%s - File ID (0x%04X) is invalid", __func__,
                                p_t4t->cc_file.ndef_fc.file_id);
     return false;
   }
 
-  if ((p_t4t->cc_file.ndef_fc.max_file_size < 0x0005) ||
-      (p_t4t->cc_file.ndef_fc.max_file_size == 0xFFFF)) {
-    LOG(ERROR) << StringPrintf("max_file_size (%d) is reserved",
+  if (((p_t4t->cc_file.version == 0x20) &&
+       ((p_t4t->cc_file.ndef_fc.max_file_size < 0x0005) ||
+        (p_t4t->cc_file.ndef_fc.max_file_size > 0x7FFF))) ||
+      ((p_t4t->cc_file.version == 0x30) &&
+       ((p_t4t->cc_file.ndef_fc.max_file_size < 0x00000007) ||
+        (p_t4t->cc_file.ndef_fc.max_file_size == 0xFFFFFFFF)))) {
+    LOG(ERROR) << StringPrintf("%s - max_file_size (%d) is reserved", __func__,
                                p_t4t->cc_file.ndef_fc.max_file_size);
     return false;
   }
 
-  if (p_t4t->cc_file.ndef_fc.read_access != T4T_FC_READ_ACCESS) {
-    LOG(ERROR) << StringPrintf("Read Access (0x%02X) is invalid",
+  if (((p_t4t->cc_file.ndef_fc.read_access > T4T_FC_READ_ACCESS) &&
+       (p_t4t->cc_file.ndef_fc.read_access < T4T_FC_READ_ACCESS_PROP_START)) ||
+      (p_t4t->cc_file.ndef_fc.read_access == T4T_FC_NO_READ_ACCESS)) {
+    LOG(ERROR) << StringPrintf("%s - Read Access (0x%02X) is invalid", __func__,
                                p_t4t->cc_file.ndef_fc.read_access);
     return false;
   }
 
-  if ((p_t4t->cc_file.ndef_fc.write_access != T4T_FC_WRITE_ACCESS) &&
-      (p_t4t->cc_file.ndef_fc.write_access < T4T_FC_WRITE_ACCESS_PROP_START)) {
-    LOG(ERROR) << StringPrintf("Write Access (0x%02X) is invalid",
-                               p_t4t->cc_file.ndef_fc.write_access);
+  if (((p_t4t->cc_file.ndef_fc.write_access > T4T_FC_WRITE_ACCESS) &&
+       (p_t4t->cc_file.ndef_fc.write_access <
+        T4T_FC_WRITE_ACCESS_PROP_START))) {
+    LOG(ERROR) << StringPrintf("%s - Write Access (0x%02X) is invalid",
+                               __func__, p_t4t->cc_file.ndef_fc.write_access);
     return false;
   }
 
@@ -1003,9 +1295,9 @@
   tRW_EVENT event;
 
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-      "status:0x%02X, sw1:0x%02X, sw2:0x%02X, "
+      "%s - status:0x%02X, sw1:0x%02X, sw2:0x%02X, "
       "state:0x%X",
-      status, sw1, sw2, p_t4t->state);
+      __func__, status, sw1, sw2, p_t4t->state);
 
   nfc_stop_quick_timer(&p_t4t->timer);
 
@@ -1252,12 +1544,15 @@
 static void rw_t4t_sm_detect_ndef(NFC_HDR* p_r_apdu) {
   tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t;
   uint8_t *p, type, length;
-  uint16_t status_words, nlen;
+  uint32_t nlen;
+  uint32_t cc_file_offset = 0x00;
+  uint16_t status_words;
+  uint8_t cc_file_rsp_len = T4T_CC_FILE_MIN_LEN;
   tRW_DATA rw_data;
 
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-      "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(),
-      p_t4t->sub_state);
+      "%s - sub_state:%s (%d)", __func__,
+      rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), p_t4t->sub_state);
 
   /* get status words */
   p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
@@ -1265,13 +1560,14 @@
   BE_STREAM_TO_UINT16(status_words, p);
 
   if (status_words != T4T_RSP_CMD_CMPLTED) {
-    /* try V1.0 after failing of V2.0 */
+    /* try V1.0 after failing of V2.0 or V3.0 */
     if ((p_t4t->sub_state == RW_T4T_SUBSTATE_WAIT_SELECT_APP) &&
-        (p_t4t->version == T4T_VERSION_2_0)) {
+        ((p_t4t->version == T4T_VERSION_2_0) ||
+         (p_t4t->version == T4T_VERSION_3_0))) {
       p_t4t->version = T4T_VERSION_1_0;
 
-      DLOG_IF(INFO, nfc_debug_enabled)
-          << StringPrintf("retry with version=0x%02X", p_t4t->version);
+      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+          "%s - retry with version=0x%02X", __func__, p_t4t->version);
 
       if (!rw_t4t_select_application(T4T_VERSION_1_0)) {
         rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
@@ -1298,7 +1594,8 @@
     case RW_T4T_SUBSTATE_WAIT_SELECT_CC:
 
       /* CC file has been selected then read mandatory part of CC file */
-      if (!rw_t4t_read_file(0x00, T4T_CC_FILE_MIN_LEN, false)) {
+      cc_file_offset = 0x00;
+      if (!rw_t4t_read_file(cc_file_offset, cc_file_rsp_len, false)) {
         rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
       } else {
         p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_CC_FILE;
@@ -1308,7 +1605,7 @@
     case RW_T4T_SUBSTATE_WAIT_CC_FILE:
 
       /* CC file has been read then validate and select mandatory NDEF file */
-      if (p_r_apdu->len >= T4T_CC_FILE_MIN_LEN + T4T_RSP_STATUS_WORDS_SIZE) {
+      if (p_r_apdu->len >= cc_file_rsp_len + T4T_RSP_STATUS_WORDS_SIZE) {
         p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
 
         BE_STREAM_TO_UINT16(p_t4t->cc_file.cclen, p);
@@ -1327,34 +1624,107 @@
           BE_STREAM_TO_UINT8(p_t4t->cc_file.ndef_fc.write_access, p);
 
           DLOG_IF(INFO, nfc_debug_enabled)
-              << StringPrintf("Capability Container (CC) file");
-          DLOG_IF(INFO, nfc_debug_enabled)
-              << StringPrintf("  CCLEN:  0x%04X", p_t4t->cc_file.cclen);
-          DLOG_IF(INFO, nfc_debug_enabled)
-              << StringPrintf("  Version:0x%02X", p_t4t->cc_file.version);
-          DLOG_IF(INFO, nfc_debug_enabled)
-              << StringPrintf("  MaxLe:  0x%04X", p_t4t->cc_file.max_le);
-          DLOG_IF(INFO, nfc_debug_enabled)
-              << StringPrintf("  MaxLc:  0x%04X", p_t4t->cc_file.max_lc);
-          DLOG_IF(INFO, nfc_debug_enabled)
-              << StringPrintf("  NDEF File Control TLV");
+              << StringPrintf("%s - Capability Container (CC) file", __func__);
           DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-              "    FileID:      0x%04X", p_t4t->cc_file.ndef_fc.file_id);
+              "%s -   CCLEN:  0x%04X", __func__, p_t4t->cc_file.cclen);
           DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-              "    MaxFileSize: 0x%04X", p_t4t->cc_file.ndef_fc.max_file_size);
+              "%s -   Version:0x%02X", __func__, p_t4t->cc_file.version);
           DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-              "    ReadAccess:  0x%02X", p_t4t->cc_file.ndef_fc.read_access);
+              "%s -  MaxLe:  0x%04X", __func__, p_t4t->cc_file.max_le);
           DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-              "    WriteAccess: 0x%02X", p_t4t->cc_file.ndef_fc.write_access);
+              "%s -   MaxLc:  0x%04X", __func__, p_t4t->cc_file.max_lc);
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s -  NDEF File Control TLV", __func__);
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s -    FileID:      0x%04X", __func__,
+                              p_t4t->cc_file.ndef_fc.file_id);
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s -    MaxFileSize: 0x%04X", __func__,
+                              p_t4t->cc_file.ndef_fc.max_file_size);
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s -     ReadAccess:  0x%02X", __func__,
+                              p_t4t->cc_file.ndef_fc.read_access);
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s -    WriteAccess: 0x%02X", __func__,
+                              p_t4t->cc_file.ndef_fc.write_access);
 
           if (rw_t4t_validate_cc_file()) {
             if (!rw_t4t_select_file(p_t4t->cc_file.ndef_fc.file_id)) {
               rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
             } else {
+              p_t4t->cc_file.ndef_fc.nlen_size = T4T_FILE_LENGTH_SIZE;
               p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE;
             }
             break;
           }
+        } else if ((type == T4T_ENDEF_FILE_CONTROL_TYPE) &&
+                   (length == T4T_ENDEF_FILE_CONTROL_LENGTH)) {
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s - Capability Container (CC) file", __func__);
+          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+              "%s -   CCLEN:  0x%04X", __func__, p_t4t->cc_file.cclen);
+          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+              "%s -   Version:0x%02X", __func__, p_t4t->cc_file.version);
+          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+              "%s -   MaxLe:  0x%04X", __func__, p_t4t->cc_file.max_le);
+          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+              "%s -   MaxLc:  0x%04X", __func__, p_t4t->cc_file.max_lc);
+
+          cc_file_offset = T4T_ENDEF_FC_V_FIELD_OFFSET;
+          cc_file_rsp_len = T4T_ENDEF_FILE_CONTROL_TLV_SIZE - 2;
+
+          /* CC file has been selected then now read from control TLV area part
+           * of CC file */
+          /* assume ENDEF Ctrl TLV is the first one */
+          /* read again the TLV as 2 bytes missing */
+          if (!rw_t4t_read_file(cc_file_offset, cc_file_rsp_len, false)) {
+            rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
+          } else {
+            p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_ENDEF_FILE_CTRL_TLV;
+          }
+          break;
+        }
+      }
+
+      /* invalid response or CC file */
+      p_t4t->ndef_status &= ~(RW_T4T_NDEF_STATUS_NDEF_DETECTED);
+      rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
+      break;
+
+    case RW_T4T_SUBSTATE_WAIT_ENDEF_FILE_CTRL_TLV:
+
+      if (p_r_apdu->len >=
+          T4T_ENDEF_FILE_CONTROL_LENGTH + T4T_RSP_STATUS_WORDS_SIZE) {
+        p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
+
+        BE_STREAM_TO_UINT16(p_t4t->cc_file.ndef_fc.file_id, p);
+        BE_STREAM_TO_UINT32(p_t4t->cc_file.ndef_fc.max_file_size, p);
+        BE_STREAM_TO_UINT8(p_t4t->cc_file.ndef_fc.read_access, p);
+        BE_STREAM_TO_UINT8(p_t4t->cc_file.ndef_fc.write_access, p);
+
+        DLOG_IF(INFO, nfc_debug_enabled)
+            << StringPrintf("%s -  ENDEF File Control TLV", __func__);
+        DLOG_IF(INFO, nfc_debug_enabled)
+            << StringPrintf("%s -    FileID:      0x%04X", __func__,
+                            p_t4t->cc_file.ndef_fc.file_id);
+        DLOG_IF(INFO, nfc_debug_enabled)
+            << StringPrintf("%s -    MaxFileSize: 0x%08X", __func__,
+                            p_t4t->cc_file.ndef_fc.max_file_size);
+        DLOG_IF(INFO, nfc_debug_enabled)
+            << StringPrintf("%s -    ReadAccess:  0x%02X", __func__,
+                            p_t4t->cc_file.ndef_fc.read_access);
+        DLOG_IF(INFO, nfc_debug_enabled)
+            << StringPrintf("%s -    WriteAccess: 0x%02X", __func__,
+                            p_t4t->cc_file.ndef_fc.write_access);
+
+        if (rw_t4t_validate_cc_file()) {
+          if (!rw_t4t_select_file(p_t4t->cc_file.ndef_fc.file_id)) {
+            rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
+          } else {
+            p_t4t->cc_file.ndef_fc.nlen_size = T4T_EFILE_LENGTH_SIZE;
+            p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE;
+          }
+          break;
         }
       }
 
@@ -1366,7 +1736,7 @@
     case RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE:
 
       /* NDEF file has been selected then read the first 2 bytes (NLEN) */
-      if (!rw_t4t_read_file(0, T4T_FILE_LENGTH_SIZE, false)) {
+      if (!rw_t4t_read_file(0, p_t4t->cc_file.ndef_fc.nlen_size, false)) {
         rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
       } else {
         p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_READ_NLEN;
@@ -1376,16 +1746,21 @@
     case RW_T4T_SUBSTATE_WAIT_READ_NLEN:
 
       /* NLEN has been read then report upper layer */
-      if (p_r_apdu->len == T4T_FILE_LENGTH_SIZE + T4T_RSP_STATUS_WORDS_SIZE) {
+      if (p_r_apdu->len ==
+          p_t4t->cc_file.ndef_fc.nlen_size + T4T_RSP_STATUS_WORDS_SIZE) {
         /* get length of NDEF */
         p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
-        BE_STREAM_TO_UINT16(nlen, p);
+        if (p_t4t->cc_file.ndef_fc.nlen_size == T4T_FILE_LENGTH_SIZE) {
+          BE_STREAM_TO_UINT16(nlen, p);
+        } else {
+          BE_STREAM_TO_UINT32(nlen, p);
+        }
 
-        if (nlen <=
-            p_t4t->cc_file.ndef_fc.max_file_size - T4T_FILE_LENGTH_SIZE) {
+        if (nlen <= p_t4t->cc_file.ndef_fc.max_file_size -
+                        p_t4t->cc_file.ndef_fc.nlen_size) {
           p_t4t->ndef_status = RW_T4T_NDEF_STATUS_NDEF_DETECTED;
 
-          if (p_t4t->cc_file.ndef_fc.write_access != T4T_FC_WRITE_ACCESS) {
+          if (p_t4t->cc_file.ndef_fc.write_access == T4T_FC_NO_WRITE_ACCESS) {
             p_t4t->ndef_status |= RW_T4T_NDEF_STATUS_NDEF_READ_ONLY;
           }
 
@@ -1396,9 +1771,14 @@
             p_t4t->max_read_size = p_t4t->cc_file.max_le;
           }
 
-          /* Le: valid range is 0x01 to 0xFF */
-          if (p_t4t->max_read_size >= T4T_MAX_LENGTH_LE) {
-            p_t4t->max_read_size = T4T_MAX_LENGTH_LE;
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s -    max_read_size:      0x%04X", __func__,
+                              p_t4t->max_read_size);
+
+          /* Le: valid range is 0x0001 to 0xFFFF */
+          if (p_t4t->max_read_size > T4T_MAX_LENGTH_LE + 1) {
+            /* Extended Field Coding supported by the tag */
+            p_t4t->intl_flags |= RW_T4T_EXT_FIELD_CODING;
           }
 
           /* Get max bytes to update per command */
@@ -1408,9 +1788,10 @@
             p_t4t->max_update_size = p_t4t->cc_file.max_lc;
           }
 
-          /* Lc: valid range is 0x01 to 0xFF */
-          if (p_t4t->max_update_size >= T4T_MAX_LENGTH_LC) {
-            p_t4t->max_update_size = T4T_MAX_LENGTH_LC;
+          /* Lc: valid range is 0x0001 to 0xFFFF */
+          if (p_t4t->max_update_size > T4T_MAX_LENGTH_LC) {
+            /* Extended Field Coding supported by the tag */
+            p_t4t->intl_flags |= RW_T4T_EXT_FIELD_CODING;
           }
 
           p_t4t->ndef_length = nlen;
@@ -1421,7 +1802,7 @@
             rw_data.ndef.protocol = NFC_PROTOCOL_ISO_DEP;
             rw_data.ndef.max_size =
                 (uint32_t)(p_t4t->cc_file.ndef_fc.max_file_size -
-                           (uint16_t)T4T_FILE_LENGTH_SIZE);
+                           (uint16_t)p_t4t->cc_file.ndef_fc.nlen_size);
             rw_data.ndef.cur_size = nlen;
             rw_data.ndef.flags = RW_NDEF_FL_SUPPORTED | RW_NDEF_FL_FORMATED;
             if (p_t4t->cc_file.ndef_fc.write_access != T4T_FC_WRITE_ACCESS) {
@@ -1431,23 +1812,23 @@
             (*(rw_cb.p_cback))(RW_T4T_NDEF_DETECT_EVT, &rw_data);
 
             DLOG_IF(INFO, nfc_debug_enabled)
-                << StringPrintf("Sent RW_T4T_NDEF_DETECT_EVT");
+                << StringPrintf("%s - Sent RW_T4T_NDEF_DETECT_EVT", __func__);
           }
         } else {
           /* NLEN should be less than max file size */
           LOG(ERROR) << StringPrintf(
-              "NLEN (%d) + 2 must be <= max file "
+              "%s - NLEN (%d) + 2 must be <= max file "
               "size (%d)",
-              nlen, p_t4t->cc_file.ndef_fc.max_file_size);
+              __func__, nlen, p_t4t->cc_file.ndef_fc.max_file_size);
 
           p_t4t->ndef_status &= ~(RW_T4T_NDEF_STATUS_NDEF_DETECTED);
           rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
         }
       } else {
-        /* response payload size should be T4T_FILE_LENGTH_SIZE */
+        /* response payload size should be T4T_(E)FILE_LENGTH_SIZE */
         LOG(ERROR) << StringPrintf(
-            "Length (%d) of R-APDU must be %d", p_r_apdu->len,
-            T4T_FILE_LENGTH_SIZE + T4T_RSP_STATUS_WORDS_SIZE);
+            "%s - Length (%d) of R-APDU must be %d", __func__, p_r_apdu->len,
+            p_t4t->cc_file.ndef_fc.nlen_size + T4T_RSP_STATUS_WORDS_SIZE);
 
         p_t4t->ndef_status &= ~(RW_T4T_NDEF_STATUS_NDEF_DETECTED);
         rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
@@ -1455,7 +1836,8 @@
       break;
 
     default:
-      LOG(ERROR) << StringPrintf("unknown sub_state=%d", p_t4t->sub_state);
+      LOG(ERROR) << StringPrintf("%s - unknown sub_state=%d", __func__,
+                                 p_t4t->sub_state);
       rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
       break;
   }
@@ -1474,11 +1856,12 @@
   tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t;
   uint8_t* p;
   uint16_t status_words;
+  uint16_t r_apdu_len;
   tRW_DATA rw_data;
 
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-      "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(),
-      p_t4t->sub_state);
+      "%s - sub_state:%s (%d)", __func__,
+      rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), p_t4t->sub_state);
 
   /* get status words */
   p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
@@ -1497,46 +1880,141 @@
       /* Read partial or complete data */
       p_r_apdu->len -= T4T_RSP_STATUS_WORDS_SIZE;
 
-      if ((p_r_apdu->len > 0) && (p_r_apdu->len <= p_t4t->rw_length)) {
-        p_t4t->rw_length -= p_r_apdu->len;
-        p_t4t->rw_offset += p_r_apdu->len;
-
-        if (rw_cb.p_cback) {
-          rw_data.data.status = NFC_STATUS_OK;
-          rw_data.data.p_data = p_r_apdu;
-
-          /* if need to read more data */
-          if (p_t4t->rw_length > 0) {
-            (*(rw_cb.p_cback))(RW_T4T_NDEF_READ_EVT, &rw_data);
-
-            if (!rw_t4t_read_file(p_t4t->rw_offset, p_t4t->rw_length, true)) {
-              rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
+      p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
+      if (p_t4t->intl_flags & RW_T4T_DDO_LC_FIELD_CODING) {
+        if (*p == 0x53) {
+          /* ReadBinary command with ODO */
+          if (*(p + 1) <= 0x7F) {
+            p_r_apdu->len -= 2;
+            p_r_apdu->offset += 2;
+            /* Content read length coded over 1 byte in 1st byte
+             * of BER-TLV length field */
+            DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+                "%s - Content read length expected: 0x%02X, returned: 0x%02X",
+                __func__, *(p + 1), p_r_apdu->len);
+            if (*(p + 1) == p_r_apdu->len) {
+              if ((p_r_apdu->len > 0) && (p_r_apdu->len <= p_t4t->rw_length)) {
+                p_t4t->rw_length -= p_r_apdu->len;
+                p_t4t->rw_offset += p_r_apdu->len;
+              }
+            } else {
+              LOG(ERROR) << StringPrintf(
+                  "%s - invalid payload length (%d), rw_length (%d)", __func__,
+                  p_r_apdu->len, p_t4t->rw_length);
+              rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
+              break;
+            }
+          } else if (*(p + 1) == 0x81) {
+            if (*(p + 2) <= 0xFD) {
+              p_r_apdu->len -= 3;
+              p_r_apdu->offset += 3;
+              /* Content read length coded over 1 byte in 2nd byte
+               * of BER-TLV length field */
+              DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+                  "%s - Content read length expected: 0x%02X, returned: 0x%02X",
+                  __func__, *(p + 2), p_r_apdu->len);
+              if (*(p + 2) == p_r_apdu->len) {
+                if ((p_r_apdu->len > 0) &&
+                    (p_r_apdu->len <= p_t4t->rw_length)) {
+                  p_t4t->rw_length -= p_r_apdu->len;
+                  p_t4t->rw_offset += p_r_apdu->len;
+                }
+              } else {
+                LOG(ERROR) << StringPrintf(
+                    "%s - invalid payload length (%d), rw_length "
+                    "(%d)",
+                    __func__, p_r_apdu->len, p_t4t->rw_length);
+                rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
+                break;
+              }
+            } else {
+              LOG(ERROR) << StringPrintf(
+                  "%s - invalid DDO length content length received (1)",
+                  __func__);
+              rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
+              break;
+            }
+          } else if (*(p + 1) == 0x82) {
+            /* Content read length coded over 2 bytes in 2nd and 3rd bytes
+             * of BER-TLV length field*/
+            r_apdu_len = (uint16_t)(*(p + 2) << 8);
+            r_apdu_len |= (uint8_t) * (p + 3);
+            if (r_apdu_len <= (p_t4t->max_read_size - 4)) {
+              p_r_apdu->len -= 4;
+              p_r_apdu->offset += 4;
+              DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+                  "%s - Content read length expected: 0x%02X%02X, returned: "
+                  "0x%02X%02X ",
+                  __func__, *(p + 3), *(p + 2), (uint8_t)(p_r_apdu->len >> 8),
+                  (uint8_t)p_r_apdu->len);
+              if (r_apdu_len == p_r_apdu->len) {
+                if ((p_r_apdu->len > 0) &&
+                    (p_r_apdu->len <= p_t4t->rw_length)) {
+                  p_t4t->rw_length -= p_r_apdu->len;
+                  p_t4t->rw_offset += p_r_apdu->len;
+                }
+              } else {
+                LOG(ERROR) << StringPrintf(
+                    "%s - invalid payload length (%d), rw_length "
+                    "(%d)",
+                    __func__, p_r_apdu->len, p_t4t->rw_length);
+                rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
+                break;
+              }
             }
           } else {
-            p_t4t->state = RW_T4T_STATE_IDLE;
-
-            (*(rw_cb.p_cback))(RW_T4T_NDEF_READ_CPLT_EVT, &rw_data);
-
-            DLOG_IF(INFO, nfc_debug_enabled)
-                << StringPrintf("Sent RW_T4T_NDEF_READ_CPLT_EVT");
+            LOG(ERROR) << StringPrintf(
+                "%s - invalid DDO length content length received (2)",
+                __func__);
+            rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
+            break;
           }
-
-          p_r_apdu = nullptr;
         } else {
-          p_t4t->rw_length = 0;
-          p_t4t->state = RW_T4T_STATE_IDLE;
+          LOG(ERROR) << StringPrintf("%s - invalid DDO tag", __func__);
+          rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
+          break;
         }
+      } else if ((p_r_apdu->len > 0) && (p_r_apdu->len <= p_t4t->rw_length)) {
+        p_t4t->rw_length -= p_r_apdu->len;
+        p_t4t->rw_offset += p_r_apdu->len;
       } else {
         LOG(ERROR) << StringPrintf(
-            "invalid payload length (%d), rw_length "
+            "%s - invalid payload length (%d), rw_length "
             "(%d)",
-            p_r_apdu->len, p_t4t->rw_length);
+            __func__, p_r_apdu->len, p_t4t->rw_length);
         rw_t4t_handle_error(NFC_STATUS_BAD_RESP, 0, 0);
+        break;
+      }
+      if (rw_cb.p_cback) {
+        rw_data.data.status = NFC_STATUS_OK;
+        rw_data.data.p_data = p_r_apdu;
+
+        /* if need to read more data */
+        if (p_t4t->rw_length > 0) {
+          (*(rw_cb.p_cback))(RW_T4T_NDEF_READ_EVT, &rw_data);
+
+          if (!rw_t4t_read_file(p_t4t->rw_offset, p_t4t->rw_length, true)) {
+            rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
+          }
+        } else {
+          p_t4t->state = RW_T4T_STATE_IDLE;
+
+          (*(rw_cb.p_cback))(RW_T4T_NDEF_READ_CPLT_EVT, &rw_data);
+
+          DLOG_IF(INFO, nfc_debug_enabled)
+              << StringPrintf("%s - Sent RW_T4T_NDEF_READ_CPLT_EVT", __func__);
+        }
+
+        p_r_apdu = nullptr;
+      } else {
+        p_t4t->rw_length = 0;
+        p_t4t->state = RW_T4T_STATE_IDLE;
       }
       break;
 
     default:
-      LOG(ERROR) << StringPrintf("unknown sub_state = %d", p_t4t->sub_state);
+      LOG(ERROR) << StringPrintf("%s - unknown sub_state = %d", __func__,
+                                 p_t4t->sub_state);
       rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
       break;
   }
@@ -1560,8 +2038,8 @@
   tRW_DATA rw_data;
 
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-      "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(),
-      p_t4t->sub_state);
+      "%s - sub_state:%s (%d)", __func__,
+      rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), p_t4t->sub_state);
 
   /* Get status words */
   p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
@@ -1593,8 +2071,8 @@
           rw_data.status = NFC_STATUS_OK;
 
           (*(rw_cb.p_cback))(RW_T4T_NDEF_UPDATE_CPLT_EVT, &rw_data);
-          DLOG_IF(INFO, nfc_debug_enabled)
-              << StringPrintf("Sent RW_T4T_NDEF_UPDATE_CPLT_EVT");
+          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+              "%s - Sent RW_T4T_NDEF_UPDATE_CPLT_EVT", __func__);
         }
       }
       break;
@@ -1620,7 +2098,8 @@
       break;
 
     default:
-      LOG(ERROR) << StringPrintf("unknown sub_state = %d", p_t4t->sub_state);
+      LOG(ERROR) << StringPrintf("%s - unknown sub_state = %d", __func__,
+                                 p_t4t->sub_state);
       rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
       break;
   }
@@ -1642,8 +2121,8 @@
   tRW_DATA rw_data;
 
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-      "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(),
-      p_t4t->sub_state);
+      "%s - sub_state:%s (%d)", __func__,
+      rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), p_t4t->sub_state);
 
   /* Get status words */
   p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
@@ -1687,13 +2166,14 @@
         rw_data.status = NFC_STATUS_OK;
 
         DLOG_IF(INFO, nfc_debug_enabled)
-            << StringPrintf("Sent RW_T4T_SET_TO_RO_EVT");
+            << StringPrintf("%s - Sent RW_T4T_SET_TO_RO_EVT", __func__);
         (*(rw_cb.p_cback))(RW_T4T_SET_TO_RO_EVT, &rw_data);
       }
       break;
 
     default:
-      LOG(ERROR) << StringPrintf("unknown sub_state = %d", p_t4t->sub_state);
+      LOG(ERROR) << StringPrintf("%s - unknown sub_state = %d", __func__,
+                                 p_t4t->sub_state);
       rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0);
       break;
   }
@@ -1757,7 +2237,8 @@
 
   uint8_t begin_state = p_t4t->state;
 
-  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("event = 0x%X", event);
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s - event = 0x%X", __func__, event);
   nfc_stop_quick_timer(&p_t4t->timer);
 
   switch (event) {
@@ -1794,8 +2275,8 @@
   }
 
   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-      "RW T4T state: <%s (%d)>", rw_t4t_get_state_name(p_t4t->state).c_str(),
-      p_t4t->state);
+      "%s - RW T4T state: <%s (%d)>", __func__,
+      rw_t4t_get_state_name(p_t4t->state).c_str(), p_t4t->state);
 
   if (p_t4t->state != RW_T4T_STATE_IDLE &&
       p_t4t->state != RW_T4T_STATE_PRESENCE_CHECK &&
@@ -1812,8 +2293,8 @@
       /* Unexpected R-APDU, it should be raw frame response */
       /* forward to upper layer without parsing */
       DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
-          "RW T4T Raw Frame: Len [0x%X] Status [%s]", p_r_apdu->len,
-          NFC_GetStatusName(p_data->data.status).c_str());
+          "%s - RW T4T Raw Frame: Len [0x%X] Status [%s]", __func__,
+          p_r_apdu->len, NFC_GetStatusName(p_data->data.status).c_str());
       if (rw_cb.p_cback) {
         rw_data.raw_frame.status = p_data->data.status;
         rw_data.raw_frame.p_data = p_r_apdu;
@@ -1851,14 +2332,15 @@
       GKI_freebuf(p_r_apdu);
       break;
     default:
-      LOG(ERROR) << StringPrintf("invalid state=%d", p_t4t->state);
+      LOG(ERROR) << StringPrintf("%s - invalid state=%d", __func__,
+                                 p_t4t->state);
       GKI_freebuf(p_r_apdu);
       break;
   }
 
   if (begin_state != p_t4t->state) {
     DLOG_IF(INFO, nfc_debug_enabled)
-        << StringPrintf("RW T4T state changed:<%s> -> <%s>",
+        << StringPrintf("%s - RW T4T state changed:<%s> -> <%s>", __func__,
                         rw_t4t_get_state_name(begin_state).c_str(),
                         rw_t4t_get_state_name(p_t4t->state).c_str());
   }
@@ -1939,8 +2421,8 @@
   DLOG_IF(INFO, nfc_debug_enabled) << __func__;
 
   if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) {
-    LOG(ERROR) << StringPrintf("Unable to start command at state (0x%X)",
-                               rw_cb.tcb.t4t.state);
+    LOG(ERROR) << StringPrintf("%s - Unable to start command at state (0x%X)",
+                               __func__, rw_cb.tcb.t4t.state);
     return NFC_STATUS_FAILED;
   }
 
@@ -1984,16 +2466,16 @@
   DLOG_IF(INFO, nfc_debug_enabled) << __func__;
 
   if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) {
-    LOG(ERROR) << StringPrintf("Unable to start command at state (0x%X)",
-                               rw_cb.tcb.t4t.state);
+    LOG(ERROR) << StringPrintf("%s - Unable to start command at state (0x%X)",
+                               __func__, rw_cb.tcb.t4t.state);
     return NFC_STATUS_FAILED;
   }
 
   /* if NDEF has been detected */
   if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_DETECTED) {
     /* start reading NDEF */
-    if (!rw_t4t_read_file(T4T_FILE_LENGTH_SIZE, rw_cb.tcb.t4t.ndef_length,
-                          false)) {
+    if (!rw_t4t_read_file(rw_cb.tcb.t4t.cc_file.ndef_fc.nlen_size,
+                          rw_cb.tcb.t4t.ndef_length, false)) {
       return NFC_STATUS_FAILED;
     }
 
@@ -2002,7 +2484,7 @@
 
     return NFC_STATUS_OK;
   } else {
-    LOG(ERROR) << StringPrintf("No NDEF detected");
+    LOG(ERROR) << StringPrintf("%s - No NDEF detected", __func__);
     return NFC_STATUS_FAILED;
   }
 }
@@ -2024,12 +2506,13 @@
 **                  NFC_STATUS_FAILED if T4T is busy or other error
 **
 *******************************************************************************/
-tNFC_STATUS RW_T4tUpdateNDef(uint16_t length, uint8_t* p_data) {
-  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("length:%d", length);
+tNFC_STATUS RW_T4tUpdateNDef(uint32_t length, uint8_t* p_data) {
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("%s - length:%d", __func__, length);
 
   if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) {
-    LOG(ERROR) << StringPrintf("Unable to start command at state (0x%X)",
-                               rw_cb.tcb.t4t.state);
+    LOG(ERROR) << StringPrintf("%s - Unable to start command at state (0x%X)",
+                               __func__, rw_cb.tcb.t4t.state);
     return NFC_STATUS_FAILED;
   }
 
@@ -2037,16 +2520,16 @@
   if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_DETECTED) {
     /* if read-only */
     if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_READ_ONLY) {
-      LOG(ERROR) << StringPrintf("NDEF is read-only");
+      LOG(ERROR) << StringPrintf("%s - NDEF is read-only", __func__);
       return NFC_STATUS_FAILED;
     }
 
     if (rw_cb.tcb.t4t.cc_file.ndef_fc.max_file_size <
-        length + T4T_FILE_LENGTH_SIZE) {
+        length + rw_cb.tcb.t4t.cc_file.ndef_fc.nlen_size) {
       LOG(ERROR) << StringPrintf(
-          "data (%d bytes) plus NLEN is more than max file "
+          "%s - data (%d bytes) plus NLEN is more than max file "
           "size (%d)",
-          length, rw_cb.tcb.t4t.cc_file.ndef_fc.max_file_size);
+          __func__, length, rw_cb.tcb.t4t.cc_file.ndef_fc.max_file_size);
       return NFC_STATUS_FAILED;
     }
 
@@ -2054,7 +2537,7 @@
     rw_cb.tcb.t4t.ndef_length = length;
     rw_cb.tcb.t4t.p_update_data = p_data;
 
-    rw_cb.tcb.t4t.rw_offset = T4T_FILE_LENGTH_SIZE;
+    rw_cb.tcb.t4t.rw_offset = rw_cb.tcb.t4t.cc_file.ndef_fc.nlen_size;
     rw_cb.tcb.t4t.rw_length = length;
 
     /* set NLEN to 0x0000 for the first step */
@@ -2067,7 +2550,7 @@
 
     return NFC_STATUS_OK;
   } else {
-    LOG(ERROR) << StringPrintf("No NDEF detected");
+    LOG(ERROR) << StringPrintf("%s - No NDEF detected", __func__);
     return NFC_STATUS_FAILED;
   }
 }
@@ -2096,7 +2579,7 @@
   bool status;
   NFC_HDR* p_data;
 
-  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%d", option);
+  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s - %d", __func__, option);
 
   /* If RW_SelectTagType was not called (no conn_callback) return failure */
   if (!rw_cb.p_cback) {
@@ -2158,8 +2641,8 @@
   DLOG_IF(INFO, nfc_debug_enabled) << __func__;
 
   if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) {
-    LOG(ERROR) << StringPrintf("Unable to start command at state (0x%X)",
-                               rw_cb.tcb.t4t.state);
+    LOG(ERROR) << StringPrintf("%s - Unable to start command at state (0x%X)",
+                               __func__, rw_cb.tcb.t4t.state);
     return NFC_STATUS_FAILED;
   }
 
@@ -2168,7 +2651,7 @@
     /* if read-only */
     if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_READ_ONLY) {
       DLOG_IF(INFO, nfc_debug_enabled)
-          << StringPrintf("NDEF is already read-only");
+          << StringPrintf("%s - NDEF is already read-only", __func__);
 
       evt_data.status = NFC_STATUS_OK;
       (*rw_cb.p_cback)(RW_T4T_SET_TO_RO_EVT, &evt_data);
@@ -2185,7 +2668,7 @@
 
     return NFC_STATUS_OK;
   } else {
-    LOG(ERROR) << StringPrintf("No NDEF detected");
+    LOG(ERROR) << StringPrintf("%s - No NDEF detected", __func__);
     return NFC_STATUS_FAILED;
   }
   return (retval);
@@ -2268,6 +2751,8 @@
       return "WAIT_WRITE_CC";
     case RW_T4T_SUBSTATE_WAIT_WRITE_NDEF:
       return "WAIT_WRITE_NDEF";
+    case RW_T4T_SUBSTATE_WAIT_ENDEF_FILE_CTRL_TLV:
+      return "WAIT_ENDEF_FILE_CTRL_TLV";
     default:
       return "???? UNKNOWN SUBSTATE";
   }
diff --git a/src/rust/Android.bp b/src/rust/Android.bp
new file mode 100644
index 0000000..f91266d
--- /dev/null
+++ b/src/rust/Android.bp
@@ -0,0 +1,106 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_nfc_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_nfc_license"],
+}
+
+rust_defaults {
+    name: "nfc_rust_defaults",
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    host_supported: true,
+}
+
+cc_defaults {
+    name: "nfc_ffi_defaults",
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+rust_library {
+    name: "libnfc_rnci",
+    defaults: ["nfc_rust_defaults"],
+    crate_name: "nfc_rnci",
+    srcs: ["nci/nci.rs"],
+    host_supported: true,
+    rustlibs: [
+        "libnfc_packets",
+        "libnfc_hal",
+        "libtokio",
+        "libcxx",
+        "liblazy_static",
+        "liblog_rust",
+    ],
+    proc_macros: ["libnum_derive"],
+}
+
+rust_library {
+    name: "libnfc_hal",
+    defaults: ["nfc_rust_defaults"],
+    crate_name: "nfc_hal",
+    srcs: ["hal/hal.rs"],
+    host_supported: true,
+    rustlibs: [
+        "libnfc_packets",
+        "libbytes",
+        "libthiserror",
+        "libtokio",
+        "libcxx",
+        "liblazy_static",
+        "liblog_rust",
+    ],
+    proc_macros: ["libnum_derive"],
+    target: {
+        android: {
+                whole_static_libs: ["libnfc_hidl_hal_cxx"],
+                shared_libs: [
+                    "android.hardware.nfc@1.0",
+                    "android.hardware.nfc@1.1",
+                    "android.hardware.nfc@1.2",
+                    "libhidlbase",
+                    "libutils",
+                ],
+        },
+    },
+}
+
+genrule {
+    name: "libnfc_hidl_hal_bridge_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header > $(out)",
+    srcs: ["hal/hidl_hal.rs"],
+    out: ["hal/hidl_hal.rs.h"],
+}
+
+genrule {
+    name: "libnfc_hidl_hal_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) >> $(out)",
+    srcs: ["hal/hidl_hal.rs"],
+    out: ["hidl_hal_generated.cc"],
+}
+
+cc_library_static {
+    name: "libnfc_hidl_hal_cxx",
+    defaults: ["nfc_ffi_defaults"],
+    srcs: ["hal/ffi/hidl.cc"],
+    local_include_dirs: ["hal/ffi"],
+    generated_headers: ["libnfc_hidl_hal_bridge_header", "cxx-bridge-header"],
+    generated_sources: ["libnfc_hidl_hal_bridge_code"],
+    shared_libs: [
+        "android.hardware.nfc@1.0",
+        "android.hardware.nfc@1.1",
+        "android.hardware.nfc@1.2",
+        "libhidlbase",
+        "libutils",
+    ],
+}
diff --git a/src/rust/hal/ffi/hidl.cc b/src/rust/hal/ffi/hidl.cc
new file mode 100644
index 0000000..fb3b5b3
--- /dev/null
+++ b/src/rust/hal/ffi/hidl.cc
@@ -0,0 +1,115 @@
+#include "hal/ffi/hidl.h"
+
+#include <log/log.h>
+#include <stdlib.h>
+
+using ::android::wp;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hidl::base::V1_0::IBase;
+
+using android::OK;
+using android::sp;
+using android::status_t;
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using android::hardware::nfc::V1_0::INfc;
+using INfcV1_1 = android::hardware::nfc::V1_1::INfc;
+using INfcV1_2 = android::hardware::nfc::V1_2::INfc;
+using android::hardware::nfc::V1_1::INfcClientCallback;
+
+namespace nfc {
+namespace hal {
+namespace {
+
+class NfcHalDeathRecipient : public hidl_death_recipient {
+ public:
+  virtual void serviceDied(
+      uint64_t /*cookie*/,
+      const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+    LOG_FATAL("Nfc HAL service died!");
+    abort();
+  }
+};
+
+class NfcCallbackTrampoline : public INfcClientCallback {
+ public:
+  NfcCallbackTrampoline() {}
+
+  Return<void> sendEvent_1_1(
+      ::android::hardware::nfc::V1_1::NfcEvent event,
+      ::android::hardware::nfc::V1_0::NfcStatus event_status) override {
+    on_event(event, event_status);
+    return Void();
+  }
+  Return<void> sendEvent(
+      ::android::hardware::nfc::V1_0::NfcEvent event,
+      ::android::hardware::nfc::V1_0::NfcStatus event_status) override {
+    on_event((::android::hardware::nfc::V1_1::NfcEvent)event, event_status);
+    return Void();
+  }
+
+  Return<void> sendData(const ::android::hardware::nfc::V1_0::NfcData& data) {
+    on_data(rust::Slice(&data[0], data.size()));
+    return Void();
+  }
+};
+
+android::sp<NfcHalDeathRecipient> nfc_death_recipient_;
+android::sp<INfc> nci_;
+android::sp<INfcV1_1> nci_1_1_;
+android::sp<INfcV1_2> nci_1_2_;
+android::sp<NfcCallbackTrampoline> trampoline_;
+
+}  // namespace
+
+void start_hal() {
+  ALOG_ASSERT(nci_ != nullptr, "Stale value of the NCI port");
+
+  nci_ = nci_1_1_ = nci_1_2_ = INfcV1_2::getService();
+  if (nci_1_2_ == nullptr) {
+    nci_ = nci_1_1_ = INfcV1_1::getService();
+    if (nci_1_1_ == nullptr) {
+      nci_ = INfc::getService();
+    }
+  }
+  LOG_FATAL_IF(nci_ == nullptr, "Failed to retrieve the NFC HAL!");
+  ALOGI("%s: INfc::getService() returned %p (%s)", __func__, nci_.get(),
+        (nci_->isRemote() ? "remote" : "local"));
+  if (nci_) {
+    nfc_death_recipient_ = new NfcHalDeathRecipient();
+    auto death_link = nci_->linkToDeath(nfc_death_recipient_, 0);
+    ALOG_ASSERT(death_link.isOk(),
+                "Unable to set the death recipient for the Nfc HAL");
+  }
+
+  trampoline_ = new NfcCallbackTrampoline();
+  if (nci_1_1_ != nullptr) {
+    nci_1_1_->open_1_1(trampoline_);
+  } else {
+    nci_->open(trampoline_);
+  }
+}
+
+void stop_hal() {
+  ALOG_ASSERT(nci_ == nullptr, "The NCI communication was already closed");
+
+  auto death_unlink = nci_->unlinkToDeath(nfc_death_recipient_);
+  if (!death_unlink.isOk()) {
+    ALOGE("Error unlinking death recipient from the Bluetooth HAL");
+  }
+  nci_->close();
+  nci_ = nullptr;
+  nci_1_1_ = nullptr;
+  nci_1_2_ = nullptr;
+  trampoline_ = nullptr;
+}
+
+void send_command(rust::Slice<const uint8_t> data) {
+  ALOG_ASSERT(nci_ == nullptr, "The NCI communication was already closed");
+  nci_->write(hidl_vec<uint8_t>(data.data(), data.data() + data.length()));
+}
+
+}  // namespace hal
+}  // namespace nfc
diff --git a/src/rust/hal/ffi/hidl.h b/src/rust/hal/ffi/hidl.h
new file mode 100644
index 0000000..b4b7761
--- /dev/null
+++ b/src/rust/hal/ffi/hidl.h
@@ -0,0 +1,25 @@
+#pragma once
+#include <android/hardware/nfc/1.0/INfc.h>
+#include <android/hardware/nfc/1.1/INfc.h>
+#include <android/hardware/nfc/1.2/INfc.h>
+
+namespace nfc {
+namespace hal {
+
+using android::hardware::nfc::V1_0::NfcStatus;
+using android::hardware::nfc::V1_1::NfcEvent;
+
+}  // namespace hal
+}  // namespace nfc
+
+#include "hal/hidl_hal.rs.h"
+
+namespace nfc {
+namespace hal {
+
+void start_hal();
+void stop_hal();
+void send_command(rust::Slice<const uint8_t> data);
+
+}  // namespace hal
+}  // namespace nfc
diff --git a/src/rust/hal/hal.rs b/src/rust/hal/hal.rs
new file mode 100644
index 0000000..a1689b3
--- /dev/null
+++ b/src/rust/hal/hal.rs
@@ -0,0 +1,78 @@
+//! NCI Hardware Abstraction Layer
+//! Supports sending NCI commands to the HAL and receiving
+//! NCI events from the HAL
+
+use nfc_packets::nci::{DataPacket, NciPacket};
+use thiserror::Error;
+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
+
+#[cfg(target_os = "android")]
+#[path = "hidl_hal.rs"]
+pub mod ihal;
+
+#[cfg(not(target_os = "android"))]
+#[path = "rootcanal_hal.rs"]
+pub mod ihal;
+
+/// HAL module interface
+pub struct Hal {
+    /// HAL outbound channel for Command messages
+    pub out_cmd_tx: UnboundedSender<NciPacket>,
+    /// HAL inbound channel for Response and Notification messages
+    pub in_cmd_rx: UnboundedReceiver<NciPacket>,
+    /// HAL outbound channel for Data messages
+    pub out_data_tx: UnboundedSender<DataPacket>,
+    /// HAL inbound channel for Data messages
+    pub in_data_rx: UnboundedReceiver<DataPacket>,
+}
+
+/// Initialize the module and connect the channels
+pub async fn init() -> Hal {
+    ihal::init().await
+}
+
+mod internal {
+    use crate::Hal;
+    use nfc_packets::nci::{DataPacket, NciPacket};
+    use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
+
+    pub struct InnerHal {
+        pub out_cmd_rx: UnboundedReceiver<NciPacket>,
+        pub in_cmd_tx: UnboundedSender<NciPacket>,
+        pub out_data_rx: UnboundedReceiver<DataPacket>,
+        pub in_data_tx: UnboundedSender<DataPacket>,
+    }
+
+    impl InnerHal {
+        pub fn new() -> (Hal, Self) {
+            let (out_cmd_tx, out_cmd_rx) = unbounded_channel();
+            let (in_cmd_tx, in_cmd_rx) = unbounded_channel();
+            let (out_data_tx, out_data_rx) = unbounded_channel();
+            let (in_data_tx, in_data_rx) = unbounded_channel();
+            (
+                Hal { out_cmd_tx, in_cmd_rx, out_data_tx, in_data_rx },
+                Self { out_cmd_rx, in_cmd_tx, out_data_rx, in_data_tx },
+            )
+        }
+    }
+}
+
+/// Is this NCI control stream or data response
+pub fn is_control_packet(data: &[u8]) -> bool {
+    // Check the MT bits
+    (data[0] >> 5) & 0x7 != 0
+}
+
+/// Result type
+type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+
+/// Errors that can be encountered while dealing with the HAL
+#[derive(Error, Debug)]
+pub enum HalError {
+    /// Invalid rootcanal host error
+    #[error("Invalid rootcanal host")]
+    InvalidAddressError,
+    /// Error while connecting to rootcanal
+    #[error("Connection to rootcanal failed: {0}")]
+    RootcanalConnectError(#[from] tokio::io::Error),
+}
diff --git a/src/rust/hal/hidl_hal.rs b/src/rust/hal/hidl_hal.rs
new file mode 100644
index 0000000..feb3913
--- /dev/null
+++ b/src/rust/hal/hidl_hal.rs
@@ -0,0 +1,121 @@
+//! Implementation of the HAl that talks to NFC controller over Android's HIDL
+use crate::internal::InnerHal;
+#[allow(unused)]
+use crate::{is_control_packet, Hal, Result};
+use lazy_static::lazy_static;
+use log::error;
+use nfc_packets::nci::{DataPacket, NciPacket, Packet};
+use std::sync::Mutex;
+use tokio::select;
+use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
+
+/// Initialize the module
+pub async fn init() -> Hal {
+    let (raw_hal, inner_hal) = InnerHal::new();
+    let (hal_open_evt_tx, mut hal_open_evt_rx) = unbounded_channel();
+    *CALLBACKS.lock().unwrap() = Some(Callbacks {
+        hal_open_evt_tx,
+        in_cmd_tx: inner_hal.in_cmd_tx,
+        in_data_tx: inner_hal.in_data_tx,
+    });
+    ffi::start_hal();
+    hal_open_evt_rx.recv().await.unwrap();
+
+    tokio::spawn(dispatch_outgoing(inner_hal.out_cmd_rx, inner_hal.out_data_rx));
+
+    raw_hal
+}
+
+#[cxx::bridge(namespace = nfc::hal)]
+// TODO Either use or remove these functions, this shouldn't be the long term state
+#[allow(dead_code)]
+mod ffi {
+
+    #[repr(u32)]
+    #[derive(Debug)]
+    enum NfcEvent {
+        OPEN_CPLT = 0,
+        CLOSE_CPLT = 1,
+        POST_INIT_CPLT = 2,
+        PRE_DISCOVER_CPLT = 3,
+        REQUEST_CONTROL = 4,
+        RELEASE_CONTROL = 5,
+        ERROR = 6,
+        HCI_NETWORK_RESET = 7,
+    }
+
+    #[repr(u32)]
+    #[derive(Debug)]
+    enum NfcStatus {
+        OK = 0,
+        FAILED = 1,
+        ERR_TRANSPORT = 2,
+        ERR_CMD_TIMEOUT = 3,
+        REFUSED = 4,
+    }
+
+    unsafe extern "C++" {
+        include!("hal/ffi/hidl.h");
+        fn start_hal();
+        fn stop_hal();
+        fn send_command(data: &[u8]);
+
+        #[namespace = "android::hardware::nfc::V1_1"]
+        type NfcEvent;
+
+        #[namespace = "android::hardware::nfc::V1_0"]
+        type NfcStatus;
+    }
+
+    extern "Rust" {
+        fn on_event(evt: NfcEvent, status: NfcStatus);
+        fn on_data(data: &[u8]);
+    }
+}
+
+struct Callbacks {
+    hal_open_evt_tx: UnboundedSender<()>,
+    in_cmd_tx: UnboundedSender<NciPacket>,
+    in_data_tx: UnboundedSender<DataPacket>,
+}
+
+lazy_static! {
+    static ref CALLBACKS: Mutex<Option<Callbacks>> = Mutex::new(None);
+}
+
+fn on_event(evt: ffi::NfcEvent, status: ffi::NfcStatus) {
+    error!("got event: {:?} with status {:?}", evt, status);
+    let callbacks = CALLBACKS.lock().unwrap();
+    if evt == ffi::NfcEvent::OPEN_CPLT {
+        callbacks.as_ref().unwrap().hal_open_evt_tx.send(()).unwrap();
+    }
+}
+
+fn on_data(data: &[u8]) {
+    error!("got packet: {:02x?}", data);
+    let callbacks = CALLBACKS.lock().unwrap();
+    if is_control_packet(data) {
+        match NciPacket::parse(data) {
+            Ok(p) => callbacks.as_ref().unwrap().in_cmd_tx.send(p).unwrap(),
+            Err(e) => error!("failure to parse response: {:?} data: {:02x?}", e, data),
+        }
+    } else {
+        match DataPacket::parse(data) {
+            Ok(p) => callbacks.as_ref().unwrap().in_data_tx.send(p).unwrap(),
+            Err(e) => error!("failure to parse response: {:?} data: {:02x?}", e, data),
+        }
+    }
+}
+
+async fn dispatch_outgoing(
+    mut out_cmd_rx: UnboundedReceiver<NciPacket>,
+    mut out_data_rx: UnboundedReceiver<DataPacket>,
+) {
+    loop {
+        select! {
+            Some(cmd) = out_cmd_rx.recv() => ffi::send_command(&cmd.to_bytes()),
+            Some(data) = out_data_rx.recv() => ffi::send_command(&data.to_bytes()),
+            else => break,
+        }
+    }
+}
diff --git a/src/rust/hal/rootcanal_hal.rs b/src/rust/hal/rootcanal_hal.rs
new file mode 100644
index 0000000..ec4abbf
--- /dev/null
+++ b/src/rust/hal/rootcanal_hal.rs
@@ -0,0 +1,93 @@
+//! Rootcanal HAL
+//! This connects to "rootcanal" which provides a simulated
+//! Nfc chip as well as a simulated environment.
+
+use crate::internal::InnerHal;
+use crate::{is_control_packet, Hal, Result};
+use bytes::{BufMut, BytesMut};
+use log::{debug, error};
+use nfc_packets::nci::{DataPacket, NciPacket, Packet};
+use std::convert::TryInto;
+use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
+use tokio::net::TcpStream;
+use tokio::select;
+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
+
+/// Initialize the module
+pub async fn init() -> Hal {
+    let (raw_hal, inner_hal) = InnerHal::new();
+    let (reader, writer) = TcpStream::connect("127.0.0.1:54323")
+        .await
+        .expect("unable to create stream to rootcanal")
+        .into_split();
+
+    let reader = BufReader::new(reader);
+    tokio::spawn(dispatch_incoming(inner_hal.in_cmd_tx, inner_hal.in_data_tx, reader));
+    tokio::spawn(dispatch_outgoing(inner_hal.out_cmd_rx, inner_hal.out_data_rx, writer));
+
+    raw_hal
+}
+
+/// Send NCI events received from the HAL to the NCI layer
+async fn dispatch_incoming<R>(
+    in_cmd_tx: UnboundedSender<NciPacket>,
+    in_data_tx: UnboundedSender<DataPacket>,
+    mut reader: R,
+) -> Result<()>
+where
+    R: AsyncReadExt + Unpin,
+{
+    loop {
+        let mut buffer = BytesMut::with_capacity(1024);
+        let len: usize = reader.read_u16().await?.into();
+        buffer.resize(len, 0);
+        reader.read_exact(&mut buffer).await?;
+        let frozen = buffer.freeze();
+        debug!("{:?}", &frozen);
+        if is_control_packet(&frozen[..]) {
+            match NciPacket::parse(&frozen) {
+                Ok(p) => in_cmd_tx.send(p)?,
+                Err(e) => error!("dropping invalid cmd event packet: {}: {:02x}", e, frozen),
+            }
+        } else {
+            match DataPacket::parse(&frozen) {
+                Ok(p) => in_data_tx.send(p)?,
+                Err(e) => error!("dropping invalid data event packet: {}: {:02x}", e, frozen),
+            }
+        }
+    }
+}
+
+/// Send commands received from the NCI later to rootcanal
+async fn dispatch_outgoing<W>(
+    mut out_cmd_rx: UnboundedReceiver<NciPacket>,
+    mut out_data_rx: UnboundedReceiver<DataPacket>,
+    mut writer: W,
+) -> Result<()>
+where
+    W: AsyncWriteExt + Unpin,
+{
+    loop {
+        select! {
+            Some(cmd) = out_cmd_rx.recv() => write_nci(&mut writer, cmd).await?,
+            Some(data) = out_data_rx.recv() => write_nci(&mut writer, data).await?,
+            else => break,
+        }
+    }
+
+    Ok(())
+}
+
+async fn write_nci<W, P>(writer: &mut W, cmd: P) -> Result<()>
+where
+    W: AsyncWriteExt + Unpin,
+    P: Packet,
+{
+    let b = cmd.to_bytes();
+    let mut data = BytesMut::with_capacity(b.len() + 2);
+    data.put_u16(b.len().try_into().unwrap());
+    data.extend(b);
+    writer.write_all(&data[..]).await?;
+    debug!("Sent {:?}", data);
+    Ok(())
+}
diff --git a/src/rust/nci/nci.rs b/src/rust/nci/nci.rs
new file mode 100644
index 0000000..47685e4
--- /dev/null
+++ b/src/rust/nci/nci.rs
@@ -0,0 +1,197 @@
+//! NCI Protocol Abstraction Layer
+//! Supports sending NCI commands to the HAL and receiving
+//! NCI messages back
+
+use log::error;
+use nfc_hal::Hal;
+use nfc_packets::nci::NciChild::{Notification, Response};
+use nfc_packets::nci::{CommandPacket, DataPacket, NotificationPacket, Opcode, ResponsePacket};
+use std::collections::HashMap;
+use std::sync::Arc;
+use tokio::select;
+use tokio::sync::mpsc::{channel, Receiver, Sender};
+use tokio::sync::{oneshot, Mutex};
+use tokio::time::{sleep, Duration, Instant};
+
+/// Result type
+type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+
+/// Initialize the module and connect the channels
+pub async fn init() -> Nci {
+    let hc = nfc_hal::init().await;
+    // Channel to handle data downstream messages
+    let (out_data_ext, out_data_int) = channel::<DataPacket>(10);
+    // Channel to handle data upstream messages
+    let (in_data_int, in_data_ext) = channel::<DataPacket>(10);
+    // Internal data channels
+    let ic = InternalChannels { out_data_int, in_data_int };
+
+    let (cmd_tx, cmd_rx) = channel::<QueuedCommand>(10);
+    let commands = CommandSender { cmd_tx };
+
+    let notifications = EventRegistry { handlers: Arc::new(Mutex::new(HashMap::new())) };
+
+    tokio::spawn(dispatch(notifications, hc, ic, cmd_rx));
+    Nci { commands, out_data_ext, in_data_ext }
+}
+
+/// NCI module external interface
+pub struct Nci {
+    /// NCI command communication interface
+    pub commands: CommandSender,
+    /// NCI outbound channel for Data messages
+    pub out_data_ext: Sender<DataPacket>,
+    /// NCI inbound channel for Data messages
+    pub in_data_ext: Receiver<DataPacket>,
+}
+
+struct InternalChannels {
+    out_data_int: Receiver<DataPacket>,
+    in_data_int: Sender<DataPacket>,
+}
+
+#[derive(Debug)]
+struct PendingCommand {
+    cmd: CommandPacket,
+    response: oneshot::Sender<ResponsePacket>,
+}
+
+#[derive(Debug)]
+struct QueuedCommand {
+    pending: PendingCommand,
+    notification: Option<oneshot::Sender<NotificationPacket>>,
+}
+
+/// Sends raw commands. Only useful for facades & shims, or wrapped as a CommandSender.
+#[derive(Clone)]
+pub struct CommandSender {
+    cmd_tx: Sender<QueuedCommand>,
+}
+
+/// The data returned by send_notify() method.
+pub struct ResponsePendingNotification {
+    /// Command response
+    pub response: ResponsePacket,
+    /// Pending notification receiver
+    pub notification: oneshot::Receiver<NotificationPacket>,
+}
+
+impl CommandSender {
+    /// Send a command, but do not expect notification to be returned
+    pub async fn send(&mut self, cmd: CommandPacket) -> Result<ResponsePacket> {
+        let (tx, rx) = oneshot::channel::<ResponsePacket>();
+        self.cmd_tx
+            .send(QueuedCommand {
+                pending: PendingCommand { cmd, response: tx },
+                notification: None,
+            })
+            .await?;
+        let event = rx.await?;
+        Ok(event)
+    }
+    /// Send a command which expects notification as a result
+    pub async fn send_and_notify(
+        &mut self,
+        cmd: CommandPacket,
+    ) -> Result<ResponsePendingNotification> {
+        let (tx, rx) = oneshot::channel::<ResponsePacket>();
+        let (ntx, nrx) = oneshot::channel::<NotificationPacket>();
+        self.cmd_tx
+            .send(QueuedCommand {
+                pending: PendingCommand { cmd, response: tx },
+                notification: Some(ntx),
+            })
+            .await?;
+        let event = rx.await?;
+        Ok(ResponsePendingNotification { response: event, notification: nrx })
+    }
+}
+
+/// Provides ability to register and unregister for NCI notifications
+#[derive(Clone)]
+pub struct EventRegistry {
+    handlers: Arc<Mutex<HashMap<Opcode, oneshot::Sender<NotificationPacket>>>>,
+}
+
+impl EventRegistry {
+    /// Indicate interest in specific NCI notification
+    pub async fn register(&mut self, code: Opcode, sender: oneshot::Sender<NotificationPacket>) {
+        assert!(
+            self.handlers.lock().await.insert(code, sender).is_none(),
+            "A handler for {:?} is already registered",
+            code
+        );
+    }
+
+    /// Remove interest in specific NCI notification
+    pub async fn unregister(
+        &mut self,
+        code: Opcode,
+    ) -> Option<oneshot::Sender<NotificationPacket>> {
+        self.handlers.lock().await.remove(&code)
+    }
+}
+
+async fn dispatch(
+    mut ntfs: EventRegistry,
+    mut hc: Hal,
+    mut ic: InternalChannels,
+    mut cmd_rx: Receiver<QueuedCommand>,
+) -> Result<()> {
+    let mut pending: Option<PendingCommand> = None;
+    let timeout = sleep(Duration::MAX);
+    let max_deadline = timeout.deadline();
+    tokio::pin!(timeout);
+    loop {
+        select! {
+            Some(cmd) = hc.in_cmd_rx.recv() => {
+                match cmd.specialize() {
+                    Response(rsp) => {
+                        timeout.as_mut().reset(max_deadline);
+                        let this_opcode = rsp.get_cmd_op();
+                        match pending.take() {
+                            Some(PendingCommand{cmd, response}) if cmd.get_op() == this_opcode => {
+                                if let Err(e) = response.send(rsp) {
+                                    error!("failure dispatching command status {:?}", e);
+                                }
+                            },
+                            Some(PendingCommand{cmd, ..}) => panic!("Waiting for {}, got {}", cmd.get_op(), this_opcode),
+                            None => panic!("Unexpected status event with opcode {}", this_opcode),
+                        }
+                    }
+                    Notification(ntfy) => {
+                        let code = ntfy.get_cmd_op();
+                        match ntfs.unregister(code).await {
+                            Some(sender) => {
+                                if let Err(e) = sender.send(ntfy) {
+                                    error!("notification channel closed {:?}", e);
+                                }
+                            },
+                            None => panic!("Unhandled notification {:?}", code),
+                        }
+                    }
+                    _ => error!("Unexpected NCI data received {:?}", cmd),
+                }
+            }
+            Some(queued) = cmd_rx.recv(), if pending.is_none() => {
+                if let Some(nsender) = queued.notification {
+                    ntfs.register(queued.pending.cmd.get_op(), nsender).await;
+                }
+                if let Err(e) = hc.out_cmd_tx.send(queued.pending.cmd.clone().into()) {
+                    error!("command queue closed: {:?}", e);
+                }
+                timeout.as_mut().reset(Instant::now() + Duration::from_millis(20));
+                pending = Some(queued.pending);
+            },
+            () = &mut timeout => {
+                error!("Command processing timeout");
+                timeout.as_mut().reset(max_deadline);
+                pending = None;
+            }
+            Some(data) = hc.in_data_rx.recv() => ic.in_data_int.send(data).await?,
+            Some(data) = ic.out_data_int.recv() => hc.out_data_tx.send(data)?,
+            else => break,
+        }
+    }
+    Ok(())
+}
diff --git a/src/rust/packets/lib.rs b/src/rust/packets/lib.rs
new file mode 100644
index 0000000..c459c75
--- /dev/null
+++ b/src/rust/packets/lib.rs
@@ -0,0 +1,9 @@
+//! reimport of generated packets (to go away once rust_genrule exists)
+
+#![allow(clippy::all)]
+#![allow(unused)]
+#![allow(missing_docs)]
+
+pub mod nci {
+    include!(concat!(env!("OUT_DIR"), "/nci_packets.rs"));
+}
diff --git a/src/rust/rootcanal/Android.bp b/src/rust/rootcanal/Android.bp
new file mode 100644
index 0000000..8673273
--- /dev/null
+++ b/src/rust/rootcanal/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_nfc_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_nfc_license"],
+}
+
+rust_binary {
+    name: "nfc_rootcanal",
+    defaults: ["nfc_rust_defaults"],
+    srcs: ["main.rs"],
+    rustlibs: [
+        "libnfc_packets",
+        "libbytes",
+        "libthiserror",
+        "liblogger",
+        "liblog_rust",
+        "libtokio",
+    ],
+    proc_macros: ["libnum_derive"],
+}
diff --git a/src/rust/rootcanal/main.rs b/src/rust/rootcanal/main.rs
new file mode 100644
index 0000000..c023a11
--- /dev/null
+++ b/src/rust/rootcanal/main.rs
@@ -0,0 +1,164 @@
+//! This connects to "rootcanal" and provides a simulated
+//! Nfc chip as well as a simulated environment.
+
+use bytes::{BufMut, BytesMut};
+use log::{debug, Level};
+use logger::{self, Config};
+use nfc_packets::nci;
+use nfc_packets::nci::{CommandChild, NciChild};
+use nfc_packets::nci::{
+    ConfigStatus, NciVersion, ResetNotificationBuilder, ResetResponseBuilder, ResetTrigger,
+    ResetType,
+};
+use nfc_packets::nci::{InitResponseBuilder, NfccFeatures, RfInterface};
+use nfc_packets::nci::{NciMsgType, NciPacket, Packet, PacketBoundaryFlag};
+use std::convert::TryInto;
+use thiserror::Error;
+use tokio::io;
+use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
+use tokio::net::TcpListener;
+
+/// Result type
+type Result<T> = std::result::Result<T, RootcanalError>;
+
+#[derive(Debug, Error)]
+enum RootcanalError {
+    #[error("Termination request")]
+    TerminateTask,
+    #[error("Socket error")]
+    IoError(#[from] io::Error),
+    #[error("Unsupported command packet")]
+    UnsupportedCommand,
+    #[error("Packet did not parse correctly")]
+    InvalidPacket,
+    #[error("Packet type not supported")]
+    UnsupportedPacket,
+}
+
+const TERMINATION: u8 = 4u8;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+    logger::init(Config::default().with_tag_on_device("nfc-rc").with_min_level(Level::Trace));
+
+    let listener = TcpListener::bind("127.0.0.1:54323").await?;
+
+    let (mut sock, _) = listener.accept().await?;
+
+    tokio::spawn(async move {
+        let (rd, mut wr) = sock.split();
+        let mut rd = BufReader::new(rd);
+        loop {
+            if let Err(e) = process(&mut rd, &mut wr).await {
+                match e {
+                    RootcanalError::TerminateTask => break,
+                    _ => panic!("Communication error: {:?}", e),
+                }
+            }
+        }
+    })
+    .await?;
+    Ok(())
+}
+
+async fn process<R, W>(reader: &mut R, writer: &mut W) -> Result<()>
+where
+    R: AsyncReadExt + Unpin,
+    W: AsyncWriteExt + Unpin,
+{
+    let mut buffer = BytesMut::with_capacity(1024);
+    let len: usize = reader.read_u16().await?.into();
+    buffer.resize(len, 0);
+    reader.read_exact(&mut buffer).await?;
+    let frozen = buffer.freeze();
+    debug!("{:?}", &frozen);
+    let pkt_type = (frozen[0] >> 5) & 0x7;
+    debug!("packet {} received len={}", &pkt_type, &len);
+    if pkt_type == NciMsgType::Command as u8 {
+        match NciPacket::parse(&frozen) {
+            Ok(p) => command_response(writer, p).await,
+            Err(_) => Err(RootcanalError::InvalidPacket),
+        }
+    } else if pkt_type == TERMINATION {
+        Err(RootcanalError::TerminateTask)
+    } else {
+        Err(RootcanalError::UnsupportedPacket)
+    }
+}
+
+async fn command_response<W>(out: &mut W, cmd: NciPacket) -> Result<()>
+where
+    W: AsyncWriteExt + Unpin,
+{
+    let pbf = PacketBoundaryFlag::CompleteOrFinal;
+    let gid = 0u8;
+    match cmd.specialize() {
+        NciChild::Command(cmd) => match cmd.specialize() {
+            CommandChild::ResetCommand(rst) => {
+                write_nci(
+                    out,
+                    (ResetResponseBuilder { gid, pbf, status: nci::Status::Ok }).build(),
+                )
+                .await?;
+                write_nci(
+                    out,
+                    (ResetNotificationBuilder {
+                        gid,
+                        pbf,
+                        trigger: ResetTrigger::ResetCommand,
+                        config_status: if rst.get_reset_type() == ResetType::KeepConfig {
+                            ConfigStatus::ConfigKept
+                        } else {
+                            ConfigStatus::ConfigReset
+                        },
+                        nci_version: NciVersion::Version20,
+                        manufacturer_id: 0,
+                        mfsi: Vec::new(),
+                    })
+                    .build(),
+                )
+                .await
+            }
+            CommandChild::InitCommand(_) => {
+                let nfcc_feat = [0u8; 5];
+                let rf_int = [0u8; 2];
+                write_nci(
+                    out,
+                    (InitResponseBuilder {
+                        gid,
+                        pbf,
+                        status: nci::Status::Ok,
+                        nfcc_features: NfccFeatures::parse(&nfcc_feat).unwrap(),
+                        max_log_conns: 0,
+                        max_rout_tbls_size: 0x0000,
+                        max_ctrl_payload: 255,
+                        max_data_payload: 255,
+                        num_of_credits: 0,
+                        max_nfcv_rf_frame_sz: 64,
+                        rf_interface: vec![RfInterface::parse(&rf_int).unwrap(); 1],
+                    })
+                    .build(),
+                )
+                .await
+            }
+            _ => Err(RootcanalError::UnsupportedCommand),
+        },
+        _ => Err(RootcanalError::InvalidPacket),
+    }
+}
+
+async fn write_nci<W, T>(writer: &mut W, rsp: T) -> Result<()>
+where
+    W: AsyncWriteExt + Unpin,
+    T: Into<NciPacket>,
+{
+    let pkt = rsp.into();
+    let b = pkt.to_bytes();
+    let mut data = BytesMut::with_capacity(b.len() + 2);
+    data.put_u16(b.len().try_into().unwrap());
+    data.extend(b);
+    let frozen = data.freeze();
+    writer.write_all(frozen.as_ref()).await?;
+    debug!("command written");
+    Ok(())
+}
diff --git a/src/rust/test/Android.bp b/src/rust/test/Android.bp
new file mode 100644
index 0000000..138d551
--- /dev/null
+++ b/src/rust/test/Android.bp
@@ -0,0 +1,24 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_nfc_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_nfc_license"],
+}
+
+rust_binary {
+    name: "nfc_rootcanal_test",
+    defaults: ["nfc_rust_defaults"],
+    srcs: ["main.rs"],
+    rustlibs: [
+        "libnfc_packets",
+        "libnfc_rnci",
+        "libbytes",
+        "libthiserror",
+	"liblogger",
+        "liblog_rust",
+        "libtokio",
+    ],
+    proc_macros: ["libnum_derive"],
+}
diff --git a/src/rust/test/main.rs b/src/rust/test/main.rs
new file mode 100644
index 0000000..aa66dc4
--- /dev/null
+++ b/src/rust/test/main.rs
@@ -0,0 +1,44 @@
+//! Rootcanal HAL
+//! This connects to "rootcanal" which provides a simulated
+//! Nfc chip as well as a simulated environment.
+
+use log::{debug, error, Level};
+use logger::{self, Config};
+use nfc_packets::nci::CommandPacket;
+use nfc_packets::nci::Opcode::{self, CoreInit, CoreReset};
+use nfc_packets::nci::{FeatureEnable, PacketBoundaryFlag, ResetType};
+use nfc_packets::nci::{InitCommandBuilder, ResetCommandBuilder};
+
+/// Result type
+type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    logger::init(Config::default().with_tag_on_device("lnfc").with_min_level(Level::Trace));
+    let mut nci = nfc_rnci::init().await;
+    let reset = nci.commands.send_and_notify(build_cmd(CoreReset).unwrap()).await?;
+    let init = nci.commands.send(build_cmd(CoreInit).unwrap()).await?;
+    let reset_response_packet = reset.response.specialize();
+    debug!("Received {:?}", reset_response_packet);
+    let init_response_packet = init.specialize();
+    debug!("Received {:?}", init_response_packet);
+    let notification_packet = reset.notification.await?;
+    debug!("Received {:?}", notification_packet.specialize());
+    Ok(())
+}
+
+fn build_cmd(cmd_op_code: Opcode) -> Option<CommandPacket> {
+    let pbf = PacketBoundaryFlag::CompleteOrFinal;
+    match cmd_op_code {
+        CoreReset => Some(
+            ResetCommandBuilder { gid: 0, pbf, reset_type: ResetType::ResetConfig }.build().into(),
+        ),
+        CoreInit => Some(
+            InitCommandBuilder { gid: 0, pbf, feature_eneble: FeatureEnable::Rfu }.build().into(),
+        ),
+        _ => {
+            error!("Unsupported command: {}", cmd_op_code);
+            None
+        }
+    }
+}