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