android_ndk/
looper.rs

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