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