Merge "API for libnfc_nci is added"
diff --git a/src/nci_packets.pdl b/src/nci_packets.pdl
index f94a35d..6fea6e0 100644
--- a/src/nci_packets.pdl
+++ b/src/nci_packets.pdl
@@ -230,7 +230,7 @@
 }
 
 packet InitCommand : Command (op = CORE_INIT) {
-  feature_eneble : FeatureEnable,
+  feature_enable : FeatureEnable,
 }
 
 test InitCommand {
diff --git a/src/rust/hal/hal.rs b/src/rust/hal/hal.rs
index a1689b3..fd4ae32 100644
--- a/src/rust/hal/hal.rs
+++ b/src/rust/hal/hal.rs
@@ -1,10 +1,27 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //! 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 std::collections::HashMap;
+use std::sync::Arc;
 use thiserror::Error;
 use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
+use tokio::sync::{oneshot, Mutex};
 
 #[cfg(target_os = "android")]
 #[path = "hidl_hal.rs"]
@@ -16,6 +33,8 @@
 
 /// HAL module interface
 pub struct Hal {
+    /// HAL events
+    pub hal_events: HalEventRegistry,
     /// HAL outbound channel for Command messages
     pub out_cmd_tx: UnboundedSender<NciPacket>,
     /// HAL inbound channel for Response and Notification messages
@@ -31,10 +50,57 @@
     ihal::init().await
 }
 
+/// NFC HAL specific events
+#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
+pub enum HalEvent {
+    /// HAL CLOSE_CPLT event
+    CloseComplete,
+}
+
+/// Status of a NFC HAL event
+#[derive(Debug)]
+pub enum HalEventStatus {
+    /// HAL OK status
+    Success,
+    /// HAL FAILED status
+    Failed,
+    /// HAL ERR_TRANSPORT status
+    TransportError,
+    /// HAL ERR_CMD_TIMEOUT status
+    Timeout,
+    /// HAL REFUSED status
+    Refused,
+}
+
+/// Provides ability to register and unregister for HAL event notifications
+#[derive(Clone)]
+pub struct HalEventRegistry {
+    handlers: Arc<Mutex<HashMap<HalEvent, oneshot::Sender<HalEventStatus>>>>,
+}
+
+impl HalEventRegistry {
+    /// Indicate interest in specific HAL event
+    pub async fn register(&mut self, event: HalEvent, sender: oneshot::Sender<HalEventStatus>) {
+        assert!(
+            self.handlers.lock().await.insert(event, sender).is_none(),
+            "A handler for {:?} is already registered",
+            event
+        );
+    }
+
+    /// Remove interest in specific HAL event
+    pub async fn unregister(&mut self, event: HalEvent) -> Option<oneshot::Sender<HalEventStatus>> {
+        self.handlers.lock().await.remove(&event)
+    }
+}
+
 mod internal {
-    use crate::Hal;
+    use crate::{Hal, HalEventRegistry};
     use nfc_packets::nci::{DataPacket, NciPacket};
+    use std::collections::HashMap;
+    use std::sync::Arc;
     use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
+    use tokio::sync::Mutex;
 
     pub struct InnerHal {
         pub out_cmd_rx: UnboundedReceiver<NciPacket>,
@@ -49,8 +115,10 @@
             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();
+            let handlers = Arc::new(Mutex::new(HashMap::new()));
+            let hal_events = HalEventRegistry { handlers };
             (
-                Hal { out_cmd_tx, in_cmd_rx, out_data_tx, in_data_rx },
+                Hal { hal_events, 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 },
             )
         }
diff --git a/src/rust/hal/hidl_hal.rs b/src/rust/hal/hidl_hal.rs
index feb3913..91e67ef 100644
--- a/src/rust/hal/hidl_hal.rs
+++ b/src/rust/hal/hidl_hal.rs
@@ -1,27 +1,49 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //! 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 crate::{is_control_packet, Hal, HalEvent, HalEventRegistry, HalEventStatus, Result};
 use lazy_static::lazy_static;
-use log::error;
+use log::{debug, error};
 use nfc_packets::nci::{DataPacket, NciPacket, Packet};
 use std::sync::Mutex;
 use tokio::select;
-use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
+use tokio::sync::oneshot;
 
 /// 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();
+    let (hal_open_evt_tx, hal_open_evt_rx) = oneshot::channel::<ffi::NfcStatus>();
+    let (hal_close_evt_tx, hal_close_evt_rx) = oneshot::channel::<ffi::NfcStatus>();
     *CALLBACKS.lock().unwrap() = Some(Callbacks {
-        hal_open_evt_tx,
+        hal_open_evt_tx: Some(hal_open_evt_tx),
+        hal_close_evt_tx: Some(hal_close_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();
+    hal_open_evt_rx.await.unwrap();
 
-    tokio::spawn(dispatch_outgoing(inner_hal.out_cmd_rx, inner_hal.out_data_rx));
+    tokio::spawn(dispatch_outgoing(
+        raw_hal.hal_events.clone(),
+        inner_hal.out_cmd_rx,
+        inner_hal.out_data_rx,
+        hal_close_evt_rx,
+    ));
 
     raw_hal
 }
@@ -73,8 +95,22 @@
     }
 }
 
+impl From<ffi::NfcStatus> for HalEventStatus {
+    fn from(ffi_nfc_status: ffi::NfcStatus) -> Self {
+        match ffi_nfc_status {
+            ffi::NfcStatus::OK => HalEventStatus::Success,
+            ffi::NfcStatus::FAILED => HalEventStatus::Failed,
+            ffi::NfcStatus::ERR_TRANSPORT => HalEventStatus::TransportError,
+            ffi::NfcStatus::ERR_CMD_TIMEOUT => HalEventStatus::Timeout,
+            ffi::NfcStatus::REFUSED => HalEventStatus::Refused,
+            _ => HalEventStatus::Failed,
+        }
+    }
+}
+
 struct Callbacks {
-    hal_open_evt_tx: UnboundedSender<()>,
+    hal_open_evt_tx: Option<oneshot::Sender<ffi::NfcStatus>>,
+    hal_close_evt_tx: Option<oneshot::Sender<ffi::NfcStatus>>,
     in_cmd_tx: UnboundedSender<NciPacket>,
     in_data_tx: UnboundedSender<DataPacket>,
 }
@@ -84,15 +120,25 @@
 }
 
 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();
+    debug!("got event: {:?} with status {:?}", evt, status);
+    let mut callbacks = CALLBACKS.lock().unwrap();
+    match evt {
+        ffi::NfcEvent::OPEN_CPLT => {
+            if let Some(evt_tx) = callbacks.as_mut().unwrap().hal_open_evt_tx.take() {
+                evt_tx.send(status).unwrap();
+            }
+        }
+        ffi::NfcEvent::CLOSE_CPLT => {
+            if let Some(evt_tx) = callbacks.as_mut().unwrap().hal_close_evt_tx.take() {
+                evt_tx.send(status).unwrap();
+            }
+        }
+        _ => error!("Unhandled HAL event {:?}", evt),
     }
 }
 
 fn on_data(data: &[u8]) {
-    error!("got packet: {:02x?}", data);
+    debug!("got packet: {:02x?}", data);
     let callbacks = CALLBACKS.lock().unwrap();
     if is_control_packet(data) {
         match NciPacket::parse(data) {
@@ -108,8 +154,10 @@
 }
 
 async fn dispatch_outgoing(
+    mut hal_events: HalEventRegistry,
     mut out_cmd_rx: UnboundedReceiver<NciPacket>,
     mut out_data_rx: UnboundedReceiver<DataPacket>,
+    hal_close_evt_rx: oneshot::Receiver<ffi::NfcStatus>,
 ) {
     loop {
         select! {
@@ -118,4 +166,9 @@
             else => break,
         }
     }
+    ffi::stop_hal();
+    let status = hal_close_evt_rx.await.unwrap();
+    if let Some(evt) = hal_events.unregister(HalEvent::CloseComplete).await {
+        evt.send(HalEventStatus::from(status)).unwrap();
+    }
 }
diff --git a/src/rust/hal/rootcanal_hal.rs b/src/rust/hal/rootcanal_hal.rs
index ec4abbf..b52154d 100644
--- a/src/rust/hal/rootcanal_hal.rs
+++ b/src/rust/hal/rootcanal_hal.rs
@@ -1,9 +1,23 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //! 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 crate::{is_control_packet, Hal, HalEvent, HalEventRegistry, HalEventStatus, Result};
 use bytes::{BufMut, BytesMut};
 use log::{debug, error};
 use nfc_packets::nci::{DataPacket, NciPacket, Packet};
@@ -23,7 +37,12 @@
 
     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));
+    tokio::spawn(dispatch_outgoing(
+        raw_hal.hal_events.clone(),
+        inner_hal.out_cmd_rx,
+        inner_hal.out_data_rx,
+        writer,
+    ));
 
     raw_hal
 }
@@ -46,20 +65,31 @@
         debug!("{:?}", &frozen);
         if is_control_packet(&frozen[..]) {
             match NciPacket::parse(&frozen) {
-                Ok(p) => in_cmd_tx.send(p)?,
+                Ok(p) => {
+                    if in_cmd_tx.send(p).is_err() {
+                        break;
+                    }
+                }
                 Err(e) => error!("dropping invalid cmd event packet: {}: {:02x}", e, frozen),
             }
         } else {
             match DataPacket::parse(&frozen) {
-                Ok(p) => in_data_tx.send(p)?,
+                Ok(p) => {
+                    if in_data_tx.send(p).is_err() {
+                        break;
+                    }
+                }
                 Err(e) => error!("dropping invalid data event packet: {}: {:02x}", e, frozen),
             }
         }
     }
+    debug!("Dispatch incoming finished.");
+    Ok(())
 }
 
 /// Send commands received from the NCI later to rootcanal
 async fn dispatch_outgoing<W>(
+    mut hal_events: HalEventRegistry,
     mut out_cmd_rx: UnboundedReceiver<NciPacket>,
     mut out_data_rx: UnboundedReceiver<DataPacket>,
     mut writer: W,
@@ -75,6 +105,11 @@
         }
     }
 
+    writer.shutdown().await?;
+    if let Some(evt) = hal_events.unregister(HalEvent::CloseComplete).await {
+        evt.send(HalEventStatus::Success).unwrap();
+    }
+    debug!("Dispatch outgoing finished.");
     Ok(())
 }
 
diff --git a/src/rust/nci/api.rs b/src/rust/nci/api.rs
new file mode 100644
index 0000000..2bd4ae6
--- /dev/null
+++ b/src/rust/nci/api.rs
@@ -0,0 +1,137 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! NCI API module
+
+use crate::{CommandSender, Result};
+use log::debug;
+use nfc_hal::{HalEvent, HalEventRegistry, HalEventStatus};
+use nfc_packets::nci::{FeatureEnable, PacketBoundaryFlag, ResetType};
+use nfc_packets::nci::{InitCommandBuilder, ResetCommandBuilder};
+use tokio::sync::oneshot;
+
+/// NCI API object to manage static API data
+pub struct NciApi {
+    /// Command Sender external interface
+    commands: Option<CommandSender>,
+    /// The NFC response callback
+    callback: Option<fn(u16, &[u8])>,
+    /// HalEventRegistry is used to register for HAL events
+    hal_events: Option<HalEventRegistry>,
+}
+
+impl NciApi {
+    /// NciApi constructor
+    pub fn new() -> NciApi {
+        NciApi { commands: None, callback: None, hal_events: None }
+    }
+
+    /** ****************************************************************************
+     **
+     ** Function         nfc_enable
+     **
+     ** Description      This function enables NFC. Prior to calling NFC_Enable:
+     **                  - the NFCC must be powered up, and ready to receive
+     **                    commands.
+     **
+     **                  This function opens the NCI transport (if applicable),
+     **                  resets the NFC controller, and initializes the NFC
+     **                  subsystems.
+     **
+     **                  When the NFC startup procedure is completed, an
+     **                  NFC_ENABLE_REVT is returned to the application using the
+     **                  tNFC_RESPONSE_CBACK.
+     **
+     ** Returns          tNFC_STATUS
+     **
+     *******************************************************************************/
+    /// extern tNFC_STATUS NFC_Enable(tNFC_RESPONSE_CBACK* p_cback);
+    pub async fn nfc_enable(&mut self, callback: fn(u16, &[u8])) {
+        let nci = crate::init().await;
+
+        self.commands = Some(nci.commands);
+        self.callback = Some(callback);
+        self.hal_events = Some(nci.hal_events);
+    }
+    /** ****************************************************************************
+     **
+     ** Function         NFC_Disable
+     **
+     ** Description      This function performs clean up routines for shutting down
+     **                  NFC and closes the NCI transport (if using dedicated NCI
+     **                  transport).
+     **
+     **                  When the NFC shutdown procedure is completed, an
+     **                  NFC_DISABLED_REVT is returned to the application using the
+     **                  tNFC_RESPONSE_CBACK.
+     **
+     ** Returns          nothing
+     **
+     *******************************************************************************/
+    // extern void NFC_Disable(void);
+    pub async fn nfc_disable(&mut self) {
+        let (tx, rx) = oneshot::channel::<HalEventStatus>();
+        if let Some(mut hr) = self.hal_events.take() {
+            hr.register(HalEvent::CloseComplete, tx).await;
+
+            if let Some(cmd) = self.commands.take() {
+                drop(cmd);
+            }
+            let status = rx.await.unwrap();
+            debug!("Shutdown complete {:?}.", status);
+
+            if let Some(cb) = self.callback.take() {
+                cb(1, &[]);
+            }
+        }
+    }
+
+    /** ****************************************************************************
+     **
+     ** Function         NFC_Init
+     **
+     ** Description      This function initializes control blocks for NFC
+     **
+     ** Returns          nothing
+     **
+     *******************************************************************************/
+    /// extern void NFC_Init(tHAL_NFC_ENTRY* p_hal_entry_tbl);
+    pub async fn nfc_init(&mut self) -> Result<()> {
+        let pbf = PacketBoundaryFlag::CompleteOrFinal;
+        if let Some(cmd) = self.commands.as_mut() {
+            let reset = cmd
+                .send_and_notify(
+                    ResetCommandBuilder { gid: 0, pbf, reset_type: ResetType::ResetConfig }
+                        .build()
+                        .into(),
+                )
+                .await?;
+            let _notification_packet = reset.notification.await?;
+            let _init = cmd
+                .send(
+                    InitCommandBuilder { gid: 0, pbf, feature_enable: FeatureEnable::Rfu }
+                        .build()
+                        .into(),
+                )
+                .await?;
+        }
+        Ok(())
+    }
+}
+
+impl Default for NciApi {
+    fn default() -> Self {
+        Self::new()
+    }
+}
diff --git a/src/rust/nci/nci.rs b/src/rust/nci/nci.rs
index 47685e4..d0743f1 100644
--- a/src/rust/nci/nci.rs
+++ b/src/rust/nci/nci.rs
@@ -1,9 +1,23 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //! NCI Protocol Abstraction Layer
 //! Supports sending NCI commands to the HAL and receiving
 //! NCI messages back
 
-use log::error;
-use nfc_hal::Hal;
+use log::{debug, error};
+use nfc_hal::{Hal, HalEventRegistry};
 use nfc_packets::nci::NciChild::{Notification, Response};
 use nfc_packets::nci::{CommandPacket, DataPacket, NotificationPacket, Opcode, ResponsePacket};
 use std::collections::HashMap;
@@ -13,6 +27,8 @@
 use tokio::sync::{oneshot, Mutex};
 use tokio::time::{sleep, Duration, Instant};
 
+pub mod api;
+
 /// Result type
 type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
 
@@ -28,15 +44,18 @@
 
     let (cmd_tx, cmd_rx) = channel::<QueuedCommand>(10);
     let commands = CommandSender { cmd_tx };
+    let hal_events = hc.hal_events.clone();
 
     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 { hal_events, commands, out_data_ext, in_data_ext }
 }
 
 /// NCI module external interface
 pub struct Nci {
+    /// HAL events
+    pub hal_events: HalEventRegistry,
     /// NCI command communication interface
     pub commands: CommandSender,
     /// NCI outbound channel for Data messages
@@ -63,7 +82,7 @@
 }
 
 /// Sends raw commands. Only useful for facades & shims, or wrapped as a CommandSender.
-#[derive(Clone)]
+// #[derive(Clone)]
 pub struct CommandSender {
     cmd_tx: Sender<QueuedCommand>,
 }
@@ -107,6 +126,12 @@
     }
 }
 
+impl Drop for CommandSender {
+    fn drop(&mut self) {
+        debug!("CommandSender is dropped");
+    }
+}
+
 /// Provides ability to register and unregister for NCI notifications
 #[derive(Clone)]
 pub struct EventRegistry {
@@ -140,6 +165,8 @@
 ) -> Result<()> {
     let mut pending: Option<PendingCommand> = None;
     let timeout = sleep(Duration::MAX);
+    // The max_deadline is used to set  the sleep() deadline to a very distant moment in
+    // the future, when the notification from the timer is not required.
     let max_deadline = timeout.deadline();
     tokio::pin!(timeout);
     loop {
@@ -172,8 +199,9 @@
                     }
                     _ => error!("Unexpected NCI data received {:?}", cmd),
                 }
-            }
-            Some(queued) = cmd_rx.recv(), if pending.is_none() => {
+            },
+            qc = cmd_rx.recv(), if pending.is_none() => if let Some(queued) = qc {
+                debug!("cmd_rx got a q");
                 if let Some(nsender) = queued.notification {
                     ntfs.register(queued.pending.cmd.get_op(), nsender).await;
                 }
@@ -182,16 +210,22 @@
                 }
                 timeout.as_mut().reset(Instant::now() + Duration::from_millis(20));
                 pending = Some(queued.pending);
+            } else {
+                break;
             },
             () = &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,
+            },
+            Some(data) = hc.in_data_rx.recv() => ic.in_data_int.send(data).await.unwrap(),
+            Some(data) = ic.out_data_int.recv() => hc.out_data_tx.send(data).unwrap(),
+            else => {
+                debug!("Select is done");
+                break;
+            },
         }
     }
+    debug!("NCI dispatch is terminated.");
     Ok(())
 }
diff --git a/src/rust/rootcanal/main.rs b/src/rust/rootcanal/main.rs
index c023a11..e7d20a4 100644
--- a/src/rust/rootcanal/main.rs
+++ b/src/rust/rootcanal/main.rs
@@ -1,3 +1,17 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //! This connects to "rootcanal" and provides a simulated
 //! Nfc chip as well as a simulated environment.
 
@@ -15,7 +29,7 @@
 use std::convert::TryInto;
 use thiserror::Error;
 use tokio::io;
-use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
+use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader, ErrorKind};
 use tokio::net::TcpListener;
 
 /// Result type
@@ -43,21 +57,28 @@
 
     let listener = TcpListener::bind("127.0.0.1:54323").await?;
 
-    let (mut sock, _) = listener.accept().await?;
+    for _ in 0..2 {
+        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),
+        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,
+                        RootcanalError::IoError(e) => {
+                            if e.kind() == ErrorKind::UnexpectedEof {
+                                break;
+                            }
+                        }
+                        _ => panic!("Communication error: {:?}", e),
+                    }
                 }
             }
-        }
-    })
-    .await?;
+        })
+        .await?;
+    }
     Ok(())
 }
 
diff --git a/src/rust/test/main.rs b/src/rust/test/main.rs
index aa66dc4..aa01629 100644
--- a/src/rust/test/main.rs
+++ b/src/rust/test/main.rs
@@ -1,44 +1,43 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 //! Rootcanal HAL
 //! This connects to "rootcanal" which provides a simulated
 //! Nfc chip as well as a simulated environment.
 
-use log::{debug, error, Level};
+use log::{debug, 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};
+use nfc_rnci::api::NciApi;
 
 /// Result type
 type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
 
+/// The NFC response callback
+pub fn nfc_callback(kind: u16, _val: &[u8]) {
+    debug!("Callback {} received", kind);
+}
+
 #[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
-        }
-    }
+    let mut nci = NciApi::new();
+    nci.nfc_enable(nfc_callback).await;
+    nci.nfc_init().await?;
+    nci.nfc_disable().await;
+    nci.nfc_enable(nfc_callback).await;
+    nci.nfc_init().await?;
+    nci.nfc_disable().await;
+    Ok(())
 }