paranoid_android/
writer.rs1use core::slice;
2use std::{
3 ffi::{CStr, CString},
4 io::{self, Write},
5 os::raw::c_char,
6};
7
8use lazy_static::lazy_static;
9use sharded_slab::{pool::RefMut, Pool};
10use smallvec::SmallVec;
11use tracing_core::Metadata;
12use tracing_subscriber::fmt::MakeWriter;
13
14use crate::logging::{Buffer, Priority};
15
16#[derive(Debug)]
18pub struct AndroidLogWriter<'a> {
19 tag: &'a CStr,
20 message: PooledCString,
21
22 priority: Priority,
23 buffer: Buffer,
24 location: Option<Location>,
25}
26
27#[derive(Debug)]
29pub struct AndroidLogMakeWriter {
30 tag: CString,
31 buffer: Buffer,
32}
33
34#[derive(Debug)]
35struct Location {
36 file: PooledCString,
37 line: u32,
38}
39
40const MAX_LOG_LEN: usize = 4000;
42
43impl Write for AndroidLogWriter<'_> {
44 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
45 self.message.write(buf);
46 Ok(buf.len())
47 }
48
49 fn flush(&mut self) -> io::Result<()> {
50 let mut sv = SmallVec::<[PooledCString; 4]>::new();
51 let messages = if self.message.as_bytes().len() < MAX_LOG_LEN {
52 MessageIter::Single(Some(&mut self.message))
53 } else {
54 sv.extend(
55 self.message
56 .as_bytes()
57 .chunks(MAX_LOG_LEN)
58 .map(PooledCString::new),
59 );
60 MessageIter::Multi(sv.as_mut().iter_mut())
61 }
62 .filter_map(PooledCString::as_ptr);
63
64 let buffer = self.buffer.as_raw().0 as i32;
65 let priority = self.priority.as_raw().0 as i32;
66 let tag = self.tag.as_ptr();
67
68 #[cfg(feature = "api-30")]
69 {
70 use std::{mem::size_of, ptr::null};
71
72 use ndk_sys::{
73 __android_log_is_loggable, __android_log_message, __android_log_write_log_message,
74 };
75
76 if unsafe { __android_log_is_loggable(priority, tag, priority) } == 0 {
77 return Ok(());
78 }
79
80 let (file, line) = match &mut self.location {
81 Some(Location { file, line }) => match file.as_ptr() {
82 Some(ptr) => (ptr, *line),
83 None => (null(), 0),
84 },
85 None => (null(), 0),
86 };
87
88 for message in messages {
89 let mut message = __android_log_message {
90 struct_size: size_of::<__android_log_message>(),
91 buffer_id: buffer,
92 priority,
93 tag,
94 file,
95 line,
96 message,
97 };
98
99 unsafe { __android_log_write_log_message(&mut message) };
100 }
101 }
102
103 #[cfg(not(feature = "api-30"))]
104 {
105 use ndk_sys::__android_log_buf_write;
106
107 for message in messages {
108 unsafe { __android_log_buf_write(buffer, priority, tag, message) };
109 }
110 }
111
112 Ok(())
113 }
114}
115
116impl Drop for AndroidLogWriter<'_> {
117 fn drop(&mut self) {
118 self.flush().unwrap();
119 }
120}
121
122impl<'a> MakeWriter<'a> for AndroidLogMakeWriter {
123 type Writer = AndroidLogWriter<'a>;
124
125 fn make_writer(&'a self) -> Self::Writer {
126 AndroidLogWriter {
127 tag: self.tag.as_c_str(),
128 message: PooledCString::empty(),
129
130 buffer: self.buffer,
131 priority: Priority::Info,
132 location: None,
133 }
134 }
135
136 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
137 let priority = (*meta.level()).into();
138
139 let location = match (meta.file(), meta.line()) {
140 (Some(file), Some(line)) => {
141 let file = PooledCString::new(file.as_bytes());
142 Some(Location { file, line })
143 }
144 _ => None,
145 };
146
147 AndroidLogWriter {
148 tag: self.tag.as_c_str(),
149 message: PooledCString::empty(),
150
151 buffer: self.buffer,
152 priority,
153 location,
154 }
155 }
156}
157
158impl AndroidLogMakeWriter {
159 pub fn new(tag: String) -> Self {
161 Self::with_buffer(tag, Default::default())
162 }
163
164 pub fn with_buffer(tag: String, buffer: Buffer) -> Self {
167 Self {
168 tag: CString::new(tag).unwrap(),
169 buffer,
170 }
171 }
172}
173
174#[derive(Debug)]
175struct PooledCString {
176 buf: RefMut<'static, Vec<u8>>,
177}
178
179enum MessageIter<'a> {
180 Single(Option<&'a mut PooledCString>),
181 Multi(slice::IterMut<'a, PooledCString>),
182}
183
184lazy_static! {
185 static ref BUFFER_POOL: Pool<Vec<u8>> = Pool::new();
186}
187
188impl PooledCString {
189 fn empty() -> Self {
190 Self {
191 buf: BUFFER_POOL.create().unwrap(),
192 }
193 }
194
195 fn new(data: &[u8]) -> Self {
196 let mut this = PooledCString::empty();
197 this.write(data);
198 this
199 }
200
201 fn write(&mut self, data: &[u8]) {
202 self.buf.extend_from_slice(data);
203 }
204
205 fn as_ptr(&mut self) -> Option<*const c_char> {
206 if self.buf.last().copied() != Some(0) {
207 self.buf.push(0);
208 }
209
210 CStr::from_bytes_with_nul(self.buf.as_ref())
211 .ok()
212 .map(CStr::as_ptr)
213 }
214
215 fn as_bytes(&self) -> &[u8] {
216 self.buf.as_ref()
217 }
218}
219
220impl Drop for PooledCString {
221 fn drop(&mut self) {
222 BUFFER_POOL.clear(self.buf.key());
223 }
224}
225
226impl<'a> Iterator for MessageIter<'a> {
227 type Item = &'a mut PooledCString;
228
229 fn next(&mut self) -> Option<Self::Item> {
230 match self {
231 MessageIter::Single(message) => message.take(),
232 MessageIter::Multi(iter) => iter.next(),
233 }
234 }
235
236 fn size_hint(&self) -> (usize, Option<usize>) {
237 match self {
238 MessageIter::Single(Some(_)) => (1, Some(1)),
239 MessageIter::Single(None) => (0, Some(0)),
240 MessageIter::Multi(iter) => iter.size_hint(),
241 }
242 }
243}