1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! NFC Service implementation with NFC AIDL HAL (`INfc.aidl`)
16 //!
17 //! `INfc.aidl` only has blocking calls, but calls are called by multiple thread
18 //! so implementation should prepare calls from multiple thread.
19 
20 use crate::nci;
21 
22 use android_hardware_nfc::aidl::android::hardware::nfc::{
23     INfc::INfcAsyncServer, INfcClientCallback::INfcClientCallback, NfcCloseType::NfcCloseType,
24     NfcConfig::NfcConfig, NfcEvent::NfcEvent, NfcStatus::NfcStatus,
25 };
26 use async_trait::async_trait;
27 use binder::{DeathRecipient, IBinder, Interface, Strong};
28 use log::{debug, error, info};
29 use nix::sys::termios;
30 use std::path::Path;
31 use std::sync::Arc;
32 use tokio::fs::{File, OpenOptions};
33 use tokio::io::{AsyncReadExt, AsyncWriteExt};
34 use tokio::sync::Mutex;
35 use tokio::task::JoinSet;
36 const BUF_SIZE: usize = 1024;
37 const NCI_HEADER_SIZE: usize = 3;
38 const DBG: bool = true;
39 
40 struct NfcClient {
41     callback: Strong<dyn INfcClientCallback>,
42     death_recipient: DeathRecipient,
43 }
44 
45 impl NfcClient {
send_data_callback(&self, data: &[u8]) -> binder::Result<()>46     fn send_data_callback(&self, data: &[u8]) -> binder::Result<()> {
47         match self.callback.sendData(data) {
48             Err(err) => {
49                 info!("Failed to send data: {err}");
50                 Err(err)
51             }
52             _ => Ok(()),
53         }
54     }
55 
send_event_callback(&self, event: NfcEvent, status: NfcStatus) -> binder::Result<()>56     fn send_event_callback(&self, event: NfcEvent, status: NfcStatus) -> binder::Result<()> {
57         match self.callback.sendEvent(event, status) {
58             Err(err) => {
59                 info!("Failed to send event: {err}");
60                 Err(err)
61             }
62             _ => Ok(()),
63         }
64     }
65 }
66 
67 #[derive(Default)]
68 enum NfcSession {
69     #[default]
70     Closed,
71     Opened(NfcClient),
72 }
73 
74 #[derive(Default)]
75 struct NfcHalConfig {
76     dbg_logging: bool,
77 }
78 
79 struct NfcServiceStatus {
80     writer: File,
81     session: NfcSession,
82 }
83 
84 impl NfcServiceStatus {
unwrap_mut_opened(&mut self) -> &mut NfcClient85     fn unwrap_mut_opened(&mut self) -> &mut NfcClient {
86         match self.session {
87             NfcSession::Opened(ref mut client) => client,
88             _ => unreachable!(),
89         }
90     }
91 
ensure_opened(&self) -> binder::Result<&NfcClient>92     fn ensure_opened(&self) -> binder::Result<&NfcClient> {
93         match self.session {
94             NfcSession::Opened(ref client) => Ok(client),
95             _ => {
96                 error!("NFC isn't opened");
97                 Err(binder::Status::new_service_specific_error(NfcStatus::FAILED.0, None))
98             }
99         }
100     }
101 
ensure_opened_mut(&mut self) -> binder::Result<(&mut File, &mut NfcClient)>102     fn ensure_opened_mut(&mut self) -> binder::Result<(&mut File, &mut NfcClient)> {
103         match self.session {
104             NfcSession::Opened(ref mut client) => Ok((&mut self.writer, client)),
105             _ => {
106                 error!("NFC isn't opened");
107                 Err(binder::Status::new_service_specific_error(NfcStatus::FAILED.0, None))
108             }
109         }
110     }
111 
close(&mut self) -> binder::Result<()>112     async fn close(&mut self) -> binder::Result<()> {
113         match self.session {
114             NfcSession::Opened(ref mut client) => {
115                 client.callback.as_binder().unlink_to_death(&mut client.death_recipient)?;
116                 client.send_event_callback(NfcEvent::CLOSE_CPLT, NfcStatus::OK)?;
117                 self.session = NfcSession::Closed;
118                 Ok(())
119             }
120             _ => Err(binder::Status::new_service_specific_error(NfcStatus::FAILED.0, None)),
121         }
122     }
123 }
124 
125 pub struct NfcService {
126     _tasks: JoinSet<()>,
127     status: Arc<Mutex<NfcServiceStatus>>,
128     config: Arc<Mutex<NfcHalConfig>>,
129 }
130 
set_console_fd_raw(file: &File)131 fn set_console_fd_raw(file: &File) {
132     let mut attrs = termios::tcgetattr(file).expect("Failed to setup virtio-console to raw mode");
133     termios::cfmakeraw(&mut attrs);
134     termios::tcsetattr(file, termios::SetArg::TCSANOW, &attrs)
135         .expect("Failed to set virtio-console to raw mode");
136 
137     let raw_attrs = termios::tcgetattr(file).expect("Failed to validate virtio-console mode");
138     if attrs != raw_attrs {
139         panic!("Failed to set virtio-console to raw mode. Only partially applied");
140     }
141 }
142 
143 impl NfcService {
new(dev_path: &Path) -> NfcService144     pub async fn new(dev_path: &Path) -> NfcService {
145         // Important notes:
146         // - Must clone FD for writing and reading. Otherwise can't read data from host side.
147         // - Must not be closed while HAL is running. Otherwise packet loss may happen.
148         let writer = OpenOptions::new()
149             .read(true)
150             .write(true)
151             .open(dev_path)
152             .await
153             .expect("Failed to open virtio-console device");
154 
155         // Make FD raw mode for sending raw bytes via console driver (virtio-console),
156         set_console_fd_raw(&writer);
157 
158         // Must clone FD -- otherwise read may not get incoming data from host side.
159         let mut reader =
160             writer.try_clone().await.expect("Failed to prepare virtio-console device for reading");
161         let mut tasks = JoinSet::new();
162         let status = Arc::new(Mutex::new(NfcServiceStatus { writer, session: Default::default() }));
163         let status_clone = status.clone();
164 
165         // Keep this task running forever to prevent packet loss. read_exact() may partially read
166         // packets and dropped if cancelled.
167         tasks.spawn(async move {
168             let mut buf = [0_u8; BUF_SIZE];
169             loop {
170                 reader
171                     .read_exact(&mut buf[0..NCI_HEADER_SIZE])
172                     .await
173                     .expect("Failed to read from virtio-console device");
174                 let total_packet_length = (buf[2] as usize) + NCI_HEADER_SIZE;
175                 reader
176                     .read_exact(&mut buf[NCI_HEADER_SIZE..total_packet_length])
177                     .await
178                     .expect("Failed to read from virtio-console device");
179 
180                 info!("read sz={}", total_packet_length);
181                 if let Err(e) = log_packet(&buf[0..total_packet_length]) {
182                     debug!(
183                         "+ Unidentified packet ({e:?}): bytes={:?}",
184                         &buf[0..total_packet_length]
185                     );
186                 }
187 
188                 let status = status_clone.lock().await;
189                 if let NfcSession::Opened(ref client) = status.session {
190                     if let Err(e) = client.send_data_callback(&buf[0..total_packet_length]) {
191                         info!("Failed to send data callback. Maybe closed?: err={e:?}");
192                         // If client is disconnected, DeathRecipient will handle clean up.
193                     }
194                 } else {
195                     info!("Nfc service is closed. Dropping incoming packets");
196                 }
197             }
198         });
199 
200         NfcService { _tasks: tasks, status, config: Default::default() }
201     }
202 }
203 
log_packet(packet: &[u8]) -> Result<(), anyhow::Error>204 fn log_packet(packet: &[u8]) -> Result<(), anyhow::Error> {
205     if !DBG {
206         return Ok(());
207     }
208     let header = nci::PacketHeader::parse(&packet[0..3])?;
209     match header.get_mt() {
210         nci::MessageType::Data => {
211             let packet = nci::DataPacket::parse(packet)?;
212             debug!("+ Packet: {packet:?}");
213         }
214         _ => {
215             let packet = nci::ControlPacket::parse(packet)?;
216             debug!("+ Packet: {packet:?}");
217         }
218     }
219     Ok(())
220 }
221 
222 impl Interface for NfcService {}
223 
224 #[async_trait]
225 impl INfcAsyncServer for NfcService {
open(&self, callback: &Strong<dyn INfcClientCallback>) -> binder::Result<()>226     async fn open(&self, callback: &Strong<dyn INfcClientCallback>) -> binder::Result<()> {
227         info!("open");
228 
229         let mut status = self.status.lock().await;
230         if let NfcSession::Opened(ref _client) = status.session {
231             info!("already opened. closing first.");
232             status.close().await?;
233         }
234 
235         let status_death_recipient = self.status.clone();
236         let mut death_recipient = DeathRecipient::new(move || {
237             let mut status = status_death_recipient.blocking_lock();
238             if let NfcSession::Opened(ref _client) = status.session {
239                 info!("Nfc service has died");
240                 // Just set status to closed, because no need to unlink DeathRecipient nor send event callback.
241                 status.session = NfcSession::Closed;
242             }
243         });
244         callback.as_binder().link_to_death(&mut death_recipient)?;
245 
246         status.session =
247             NfcSession::Opened(NfcClient { callback: callback.clone(), death_recipient });
248 
249         let session = status.unwrap_mut_opened();
250         session.send_event_callback(NfcEvent::OPEN_CPLT, NfcStatus::OK)
251     }
252 
close(&self, _close_type: NfcCloseType) -> binder::Result<()>253     async fn close(&self, _close_type: NfcCloseType) -> binder::Result<()> {
254         info!("close");
255 
256         let mut status = self.status.lock().await;
257         match status.session {
258             NfcSession::Opened(_) => status.close().await,
259             _ => {
260                 error!("NFC isn't opened");
261                 Err(binder::Status::new_service_specific_error(NfcStatus::FAILED.0, None))
262             }
263         }
264     }
265 
coreInitialized(&self) -> binder::Result<()>266     async fn coreInitialized(&self) -> binder::Result<()> {
267         info!("coreInitialized");
268         let status = self.status.lock().await;
269         let client = status.ensure_opened()?;
270         client.send_event_callback(NfcEvent::POST_INIT_CPLT, NfcStatus::OK)
271     }
272 
factoryReset(&self) -> binder::Result<()>273     async fn factoryReset(&self) -> binder::Result<()> {
274         info!("factoryReset");
275         let _status = self.status.lock().await;
276         // No-op
277         Ok(())
278     }
279 
getConfig(&self) -> binder::Result<NfcConfig>280     async fn getConfig(&self) -> binder::Result<NfcConfig> {
281         info!("getConfig");
282         let _status = self.status.lock().await;
283         // TODO: read config from libnfc-hal-cf.conf (/apex/<name>/etc)
284         Ok(NfcConfig {
285             nfaPollBailOutMode: true,
286             maxIsoDepTransceiveLength: 0xFEFF,
287             defaultOffHostRoute: 0x81_u8 as i8,
288             defaultOffHostRouteFelica: 0x81_u8 as i8,
289             defaultSystemCodeRoute: 0x00,
290             defaultSystemCodePowerState: 0x3B,
291             defaultRoute: 0x00,
292             offHostRouteUicc: vec![0x81],
293             offHostRouteEse: vec![0x81],
294             defaultIsoDepRoute: 0x81_u8 as i8,
295             ..Default::default()
296         })
297     }
298 
powerCycle(&self) -> binder::Result<()>299     async fn powerCycle(&self) -> binder::Result<()> {
300         info!("powerCycle");
301         let status = self.status.lock().await;
302         let client = status.ensure_opened()?;
303         client.send_event_callback(NfcEvent::OPEN_CPLT, NfcStatus::OK)
304     }
305 
preDiscover(&self) -> binder::Result<()>306     async fn preDiscover(&self) -> binder::Result<()> {
307         info!("preDiscover");
308         let status = self.status.lock().await;
309         let client = status.ensure_opened()?;
310         client.send_event_callback(NfcEvent::PRE_DISCOVER_CPLT, NfcStatus::OK)
311     }
312 
write(&self, data: &[u8]) -> binder::Result<i32>313     async fn write(&self, data: &[u8]) -> binder::Result<i32> {
314         info!("write, sz={}", data.len());
315         let mut status = self.status.lock().await;
316         let (writer, _) = status.ensure_opened_mut()?;
317 
318         writer.write_all(data).await.expect("Failed to write virtio-console device");
319 
320         if let Err(e) = log_packet(data) {
321             debug!("+ Unidentified packet ({e:?}): bytes={data:?}");
322         }
323 
324         Ok(data.len().try_into().unwrap())
325     }
326 
setEnableVerboseLogging(&self, enable: bool) -> binder::Result<()>327     async fn setEnableVerboseLogging(&self, enable: bool) -> binder::Result<()> {
328         info!("setEnableVerboseLogging");
329         let mut config = self.config.lock().await;
330         config.dbg_logging = enable;
331         Ok(())
332     }
333 
isVerboseLoggingEnabled(&self) -> binder::Result<bool>334     async fn isVerboseLoggingEnabled(&self) -> binder::Result<bool> {
335         info!("isVerboseLoggingEnabled");
336         let config = self.config.lock().await;
337         Ok(config.dbg_logging)
338     }
339 }
340