1 //! Bindings for `ALooper`
2 //!
3 //! In Android, `ALooper`s are inherently thread-local.  Due to this, there are two different
4 //! `ALooper` interfaces exposed in this module:
5 //!
6 //!  * [`ThreadLooper`], which has methods for the operations performable with a looper in one's own
7 //!    thread; and
8 //!  * [`ForeignLooper`], which has methods for the operations performable with any thread's looper.
9 
10 use bitflags::bitflags;
11 use std::convert::TryInto;
12 use std::os::raw::c_void;
13 use std::os::unix::io::RawFd;
14 use std::ptr;
15 use std::ptr::NonNull;
16 use std::time::Duration;
17 use thiserror::Error;
18 
19 /// A thread-local `ALooper`.  This contains a native `ALooper *` and promises that there is a
20 /// looper associated with the current thread.
21 #[derive(Debug)]
22 pub struct ThreadLooper {
23     _marker: std::marker::PhantomData<*mut ()>, // Not send or sync
24     foreign: ForeignLooper,
25 }
26 
27 bitflags! {
28     /// Flags for file descriptor events that a looper can monitor.
29     pub struct FdEvent: u32 {
30         const INPUT = ffi::ALOOPER_EVENT_INPUT;
31         const OUTPUT = ffi::ALOOPER_EVENT_OUTPUT;
32         const ERROR = ffi::ALOOPER_EVENT_ERROR;
33         const HANGUP = ffi::ALOOPER_EVENT_HANGUP;
34         const INVALID = ffi::ALOOPER_EVENT_INVALID;
35     }
36 }
37 
38 /// The poll result from a [`ThreadLooper`].
39 #[derive(Debug)]
40 pub enum Poll {
41     /// This looper was woken using [`ForeignLooper::wake()`]
42     Wake,
43     /// For [`ThreadLooper::poll_once*()`][ThreadLooper::poll_once()], an event was received and processed using a callback.
44     Callback,
45     /// For [`ThreadLooper::poll_*_timeout()`][ThreadLooper::poll_once_timeout()], the requested timeout was reached before any events.
46     Timeout,
47     /// An event was received
48     Event {
49         ident: i32,
50         fd: RawFd,
51         events: FdEvent,
52         data: *mut c_void,
53     },
54 }
55 
56 #[derive(Debug, Copy, Clone, Error)]
57 #[error("Android Looper error")]
58 pub struct LooperError;
59 
60 impl ThreadLooper {
61     /// Prepares a looper for the current thread and returns it
prepare() -> Self62     pub fn prepare() -> Self {
63         unsafe {
64             let ptr = ffi::ALooper_prepare(ffi::ALOOPER_PREPARE_ALLOW_NON_CALLBACKS as _);
65             let foreign = ForeignLooper::from_ptr(NonNull::new(ptr).expect("looper non null"));
66             Self {
67                 _marker: std::marker::PhantomData,
68                 foreign,
69             }
70         }
71     }
72 
73     /// Returns the looper associated with the current thread, if any.
for_thread() -> Option<Self>74     pub fn for_thread() -> Option<Self> {
75         Some(Self {
76             _marker: std::marker::PhantomData,
77             foreign: ForeignLooper::for_thread()?,
78         })
79     }
80 
poll_once_ms(&self, ms: i32) -> Result<Poll, LooperError>81     fn poll_once_ms(&self, ms: i32) -> Result<Poll, LooperError> {
82         let mut fd: RawFd = -1;
83         let mut events: i32 = -1;
84         let mut data: *mut c_void = ptr::null_mut();
85         match unsafe { ffi::ALooper_pollOnce(ms, &mut fd, &mut events, &mut data) } {
86             ffi::ALOOPER_POLL_WAKE => Ok(Poll::Wake),
87             ffi::ALOOPER_POLL_CALLBACK => Ok(Poll::Callback),
88             ffi::ALOOPER_POLL_TIMEOUT => Ok(Poll::Timeout),
89             ffi::ALOOPER_POLL_ERROR => Err(LooperError),
90             ident if ident >= 0 => Ok(Poll::Event {
91                 ident,
92                 fd,
93                 events: FdEvent::from_bits(events as u32)
94                     .expect("poll event contains unknown bits"),
95                 data,
96             }),
97             _ => unreachable!(),
98         }
99     }
100 
101     /// Polls the looper, blocking on processing an event.
102     #[inline]
poll_once(&self) -> Result<Poll, LooperError>103     pub fn poll_once(&self) -> Result<Poll, LooperError> {
104         self.poll_once_ms(-1)
105     }
106 
107     /// Polls the looper, blocking on processing an event, but with a timeout.  Give a timeout of 0
108     /// to make this non-blocking.
109     ///
110     /// It panics if the timeout is larger than expressible as an [`i32`] of milliseconds (roughly 25
111     /// days).
112     #[inline]
poll_once_timeout(&self, timeout: Duration) -> Result<Poll, LooperError>113     pub fn poll_once_timeout(&self, timeout: Duration) -> Result<Poll, LooperError> {
114         self.poll_once_ms(
115             timeout
116                 .as_millis()
117                 .try_into()
118                 .expect("Supplied timeout is too large"),
119         )
120     }
121 
poll_all_ms(&self, ms: i32) -> Result<Poll, LooperError>122     fn poll_all_ms(&self, ms: i32) -> Result<Poll, LooperError> {
123         let mut fd: RawFd = -1;
124         let mut events: i32 = -1;
125         let mut data: *mut c_void = ptr::null_mut();
126         match unsafe { ffi::ALooper_pollAll(ms, &mut fd, &mut events, &mut data) } {
127             ffi::ALOOPER_POLL_WAKE => Ok(Poll::Wake),
128             ffi::ALOOPER_POLL_TIMEOUT => Ok(Poll::Timeout),
129             ffi::ALOOPER_POLL_ERROR => Err(LooperError),
130             ident if ident >= 0 => Ok(Poll::Event {
131                 ident,
132                 fd,
133                 events: FdEvent::from_bits(events as u32)
134                     .expect("poll event contains unknown bits"),
135                 data,
136             }),
137             _ => unreachable!(),
138         }
139     }
140 
141     /// Repeatedly polls the looper, blocking on processing an event.
142     ///
143     /// This function will never return [`Poll::Callback`].
144     #[inline]
poll_all(&self) -> Result<Poll, LooperError>145     pub fn poll_all(&self) -> Result<Poll, LooperError> {
146         self.poll_all_ms(-1)
147     }
148 
149     /// Repeatedly polls the looper, blocking on processing an event, but with a timeout. Give a
150     /// timeout of 0 to make this non-blocking.
151     ///
152     /// This function will never return [`Poll::Callback`].
153     ///
154     /// It panics if the timeout is larger than expressible as an [`i32`] of milliseconds (roughly 25
155     /// days).
156     #[inline]
poll_all_timeout(&self, timeout: Duration) -> Result<Poll, LooperError>157     pub fn poll_all_timeout(&self, timeout: Duration) -> Result<Poll, LooperError> {
158         self.poll_all_ms(
159             timeout
160                 .as_millis()
161                 .try_into()
162                 .expect("Supplied timeout is too large"),
163         )
164     }
165 
166     /// Returns a reference to the [`ForeignLooper`] that is associated with the current thread.
as_foreign(&self) -> &ForeignLooper167     pub fn as_foreign(&self) -> &ForeignLooper {
168         &self.foreign
169     }
170 
into_foreign(self) -> ForeignLooper171     pub fn into_foreign(self) -> ForeignLooper {
172         self.foreign
173     }
174 }
175 
176 /// An `ALooper`, not necessarily allocated with the current thread.
177 #[derive(Debug)]
178 pub struct ForeignLooper {
179     ptr: NonNull<ffi::ALooper>,
180 }
181 
182 unsafe impl Send for ForeignLooper {}
183 unsafe impl Sync for ForeignLooper {}
184 
185 impl Drop for ForeignLooper {
drop(&mut self)186     fn drop(&mut self) {
187         unsafe { ffi::ALooper_release(self.ptr.as_ptr()) }
188     }
189 }
190 
191 impl Clone for ForeignLooper {
clone(&self) -> Self192     fn clone(&self) -> Self {
193         unsafe {
194             ffi::ALooper_acquire(self.ptr.as_ptr());
195             Self { ptr: self.ptr }
196         }
197     }
198 }
199 
200 impl ForeignLooper {
201     /// Returns the looper associated with the current thread, if any.
202     #[inline]
for_thread() -> Option<Self>203     pub fn for_thread() -> Option<Self> {
204         NonNull::new(unsafe { ffi::ALooper_forThread() }).map(|ptr| unsafe { Self::from_ptr(ptr) })
205     }
206 
207     /// Construct a [`ForeignLooper`] object from the given pointer.
208     ///
209     /// By calling this function, you guarantee that the pointer is a valid, non-null pointer to an
210     /// NDK `ALooper`.
211     #[inline]
from_ptr(ptr: NonNull<ffi::ALooper>) -> Self212     pub unsafe fn from_ptr(ptr: NonNull<ffi::ALooper>) -> Self {
213         ffi::ALooper_acquire(ptr.as_ptr());
214         Self { ptr }
215     }
216 
217     /// Returns a pointer to the NDK `ALooper` object.
218     #[inline]
ptr(&self) -> NonNull<ffi::ALooper>219     pub fn ptr(&self) -> NonNull<ffi::ALooper> {
220         self.ptr
221     }
222 
223     /// Wakes the looper.  An event of [`Poll::Wake`] will be sent.
wake(&self)224     pub fn wake(&self) {
225         unsafe { ffi::ALooper_wake(self.ptr.as_ptr()) }
226     }
227 
228     /// Adds a file descriptor to be polled, without a callback.
229     ///
230     /// See also [the NDK
231     /// docs](https://developer.android.com/ndk/reference/group/looper.html#alooper_addfd).
232     // TODO why is this unsafe?
add_fd( &self, fd: RawFd, ident: i32, events: FdEvent, data: *mut c_void, ) -> Result<(), LooperError>233     pub unsafe fn add_fd(
234         &self,
235         fd: RawFd,
236         ident: i32,
237         events: FdEvent,
238         data: *mut c_void,
239     ) -> Result<(), LooperError> {
240         match ffi::ALooper_addFd(
241             self.ptr.as_ptr(),
242             fd,
243             ident,
244             events.bits() as i32,
245             None,
246             data,
247         ) {
248             1 => Ok(()),
249             -1 => Err(LooperError),
250             _ => unreachable!(),
251         }
252     }
253 
254     /// Adds a file descriptor to be polled, with a callback.
255     ///
256     /// The callback takes as an argument the file descriptor, and should return true to continue
257     /// receiving callbacks, or false to have the callback unregistered.
258     ///
259     /// See also [the NDK
260     /// docs](https://developer.android.com/ndk/reference/group/looper.html#alooper_addfd).
261     // TODO why is this unsafe?
add_fd_with_callback( &self, fd: RawFd, ident: i32, events: FdEvent, callback: Box<dyn FnMut(RawFd) -> bool>, ) -> Result<(), LooperError>262     pub unsafe fn add_fd_with_callback(
263         &self,
264         fd: RawFd,
265         ident: i32,
266         events: FdEvent,
267         callback: Box<dyn FnMut(RawFd) -> bool>,
268     ) -> Result<(), LooperError> {
269         extern "C" fn cb_handler(fd: RawFd, _events: i32, data: *mut c_void) -> i32 {
270             unsafe {
271                 let cb: &mut Box<dyn FnMut(RawFd) -> bool> = &mut *(data as *mut _);
272                 cb(fd) as i32
273             }
274         }
275         let data: *mut c_void = Box::into_raw(Box::new(callback)) as *mut _;
276         match ffi::ALooper_addFd(
277             self.ptr.as_ptr(),
278             fd,
279             ident,
280             events.bits() as i32,
281             Some(cb_handler),
282             data,
283         ) {
284             1 => Ok(()),
285             -1 => Err(LooperError),
286             _ => unreachable!(),
287         }
288     }
289 }
290