hardware_buffer/
lib.rs

1//! [HB] is an exposed singleton that dynamically loads libandroid.so on first access, reads the API version
2//! (accessible as [HBHolder::api_level]) and provides safe wrappers for the functions.  
3//!   
4//! [HBRef] is a reference-counted AHardwareBuffer pointer.  
5//!   
6//! All other types are generated from the NDK's hardware_buffer.h and dependent header files.
7
8
9use std::{ffi::CStr, mem::MaybeUninit, os::{fd::{AsRawFd, BorrowedFd}, unix::net::UnixStream}, ptr::{null, null_mut, NonNull}};
10
11use cstr::cstr;
12use lazy_static::lazy_static;
13use libc::{c_char, c_int, c_void, dlopen, dlsym, RTLD_LAZY, RTLD_LOCAL};
14
15
16mod hb_raw {
17    #![allow(non_upper_case_globals)]
18    #![allow(non_camel_case_types)]
19    #![allow(non_snake_case)]
20    #![allow(unused)]
21    
22    include!(concat!(env!("OUT_DIR"), "/hardware_buffer.rs"));
23}
24
25pub use hb_raw::*;
26
27
28/// Type of the singleton that holds the loaded libandroid functions
29#[allow(non_snake_case, dead_code)]
30pub struct HBHolder {
31    /// The device API level. Some functions are only introduced in specific API levels.
32    /// The wrappers will always check before calling and generate an error if the function isn't available.
33    pub api_level: c_int,
34    AHardwareBuffer_acquire: *const fn(buffer: NonNull<AHardwareBuffer>) -> c_void,
35    AHardwareBuffer_allocate: *const fn(desc: NonNull<AHardwareBufferDesc>, outBuffer: NonNull<*mut AHardwareBuffer>) -> c_int,
36    AHardwareBuffer_describe: *const fn(buffer: NonNull<AHardwareBuffer>, outDesc: NonNull<AHardwareBufferDesc>) -> c_void,
37    AHardwareBuffer_getId: *const fn(buffer: NonNull<AHardwareBuffer>, outId: NonNull<u64>) -> c_int,
38    AHardwareBuffer_isSupported: *const fn(desc: NonNull<AHardwareBufferDesc>) -> c_int,
39    AHardwareBuffer_recvHandleFromUnixSocket: *const fn(fd: c_int, outBuffer: NonNull<*mut AHardwareBuffer>) -> c_int,
40    AHardwareBuffer_lock: *const fn(buffer: NonNull<AHardwareBuffer>, usage: u64, fence: i32, rect: *const ARect, outAddr: NonNull<*mut c_void>) -> c_int,
41    AHardwareBuffer_lockAndGetInfo: *const fn(buffer: NonNull<AHardwareBuffer>, usage: u64, fence: i32, rect: *const ARect, outAddr: NonNull<*mut c_void>, bpp: NonNull<i32>, bps: NonNull<i32>) -> c_int,
42    AHardwareBuffer_lockPlanes: *const fn(buffer: NonNull<AHardwareBuffer>, usage: u64, fence: i32, rect: *const ARect, planes: NonNull<AHardwareBufferPlanes>) -> c_int,
43    AHardwareBuffer_release: *const fn(buffer: NonNull<AHardwareBuffer>) -> c_void,
44    AHardwareBuffer_sendHandleToUnixSocket: *const fn(buffer: NonNull<AHardwareBuffer>, fd: c_int) -> c_int,
45    AHardwareBuffer_unlock: *const fn(buffer: NonNull<AHardwareBuffer>, fence: *mut i32) -> c_int,
46}
47
48
49/// Only written on initialization, so should still be OK.
50/// Also the pointers are valid for all threads in the process.
51unsafe impl Sync for HBHolder {}
52
53lazy_static! {
54    /// Singleton that holds the loaded libandroid functions and provides wrappers.
55    pub static ref HB: HBHolder = HBHolder::new();
56}
57
58
59impl HBHolder {
60    fn new() -> Self {
61        let lib = unsafe { dlopen(cstr!("libandroid.so").as_ptr(), RTLD_LAZY | RTLD_LOCAL) };
62        if lib == null_mut() {
63            return Self {
64                api_level: -1,
65                AHardwareBuffer_release: null(),
66                AHardwareBuffer_recvHandleFromUnixSocket: null(),
67                AHardwareBuffer_acquire: null(),
68                AHardwareBuffer_allocate: null(),
69                AHardwareBuffer_describe: null(),
70                AHardwareBuffer_getId: null(),
71                AHardwareBuffer_isSupported: null(),
72                AHardwareBuffer_lock: null(),
73                AHardwareBuffer_lockAndGetInfo: null(),
74                AHardwareBuffer_lockPlanes: null(),
75                AHardwareBuffer_sendHandleToUnixSocket: null(),
76                AHardwareBuffer_unlock: null(),
77            };
78        }
79        
80        let mut api_level = -1;
81        {
82            // copy the functionality of the inline version of android_get_device_api_level, because somehow it only became a function after a specific API level,
83            // which means you'd have to check for the API level to check for the API level.
84            let __system_property_get = unsafe { dlsym(lib, cstr!("__system_property_get").as_ptr()) } as *const fn(name: *const c_char, value: *mut c_char) -> c_int;
85            if __system_property_get != null() {
86                // buffer size taken from the NDK source code
87                let mut buffer: [c_char; 92] = [0; 92];
88                if (unsafe { *__system_property_get })(cstr!("ro.build.version.sdk").as_ptr(), buffer.as_mut_ptr()) >= 1 {
89                    if let Ok(s) = CStr::from_bytes_until_nul(bytemuck::cast_slice(&buffer[..])) {
90                        if let Ok(l) = s.to_str() {
91                            if let Ok(l) = l.parse() {
92                                api_level = l;
93                            }
94                        }
95                    }
96                }
97            }
98        }
99        
100        #[allow(non_snake_case)]
101        {
102            let AHardwareBuffer_acquire = unsafe { dlsym(lib, cstr!("AHardwareBuffer_acquire").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>) -> c_void;
103            let AHardwareBuffer_allocate = unsafe { dlsym(lib, cstr!("AHardwareBuffer_allocate").as_ptr()) } as *const fn(desc: NonNull<AHardwareBufferDesc>, outBuffer: NonNull<*mut AHardwareBuffer>) -> c_int;
104            let AHardwareBuffer_describe = unsafe { dlsym(lib, cstr!("AHardwareBuffer_describe").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>, outDesc: NonNull<AHardwareBufferDesc>) -> c_void;
105            let AHardwareBuffer_getId = unsafe { dlsym(lib, cstr!("AHardwareBuffer_getId").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>, outId: NonNull<u64>) -> c_int;
106            let AHardwareBuffer_isSupported = unsafe { dlsym(lib, cstr!("AHardwareBuffer_isSupported").as_ptr()) } as *const fn(desc: NonNull<AHardwareBufferDesc>) -> c_int;
107            let AHardwareBuffer_recvHandleFromUnixSocket = unsafe { dlsym(lib, cstr!("AHardwareBuffer_recvHandleFromUnixSocket").as_ptr()) } as *const fn(fd: c_int, outBuffer: NonNull<*mut AHardwareBuffer>) -> c_int;
108            let AHardwareBuffer_lock = unsafe { dlsym(lib, cstr!("AHardwareBuffer_lock").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>, usage: u64, fence: i32, rect: *const ARect, outAddr: NonNull<*mut c_void>) -> c_int;
109            let AHardwareBuffer_lockAndGetInfo = unsafe { dlsym(lib, cstr!("AHardwareBuffer_lockAndGetInfo").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>, usage: u64, fence: i32, rect: *const ARect, outAddr: NonNull<*mut c_void>, bpp: NonNull<i32>, bps: NonNull<i32>) -> c_int;
110            let AHardwareBuffer_lockPlanes = unsafe { dlsym(lib, cstr!("AHardwareBuffer_lockPlanes").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>, usage: u64, fence: i32, rect: *const ARect, planes: NonNull<AHardwareBufferPlanes>) -> c_int;
111            let AHardwareBuffer_release = unsafe { dlsym(lib, cstr!("AHardwareBuffer_release").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>) -> c_void;
112            let AHardwareBuffer_sendHandleToUnixSocket = unsafe { dlsym(lib, cstr!("AHardwareBuffer_sendHandleToUnixSocket").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>, fd: c_int) -> c_int;
113            let AHardwareBuffer_unlock = unsafe { dlsym(lib, cstr!("AHardwareBuffer_unlock").as_ptr()) } as *const fn(buffer: NonNull<AHardwareBuffer>, fence: *mut i32) -> c_int;
114            Self {
115                api_level,
116                AHardwareBuffer_acquire,
117                AHardwareBuffer_release,
118                AHardwareBuffer_allocate,
119                AHardwareBuffer_describe,
120                AHardwareBuffer_getId,
121                AHardwareBuffer_isSupported,
122                AHardwareBuffer_recvHandleFromUnixSocket,
123                AHardwareBuffer_sendHandleToUnixSocket,
124                AHardwareBuffer_lock,
125                AHardwareBuffer_lockAndGetInfo,
126                AHardwareBuffer_lockPlanes,
127                AHardwareBuffer_unlock,
128            }
129        }
130    }
131    
132    
133
134    /// Acquire a reference on the given AHardwareBuffer object.  
135    /// This prevents the object from being deleted until the last reference is removed.  
136    /// Available since API level 26.
137    pub unsafe fn acquire(&self, buffer: NonNull<AHardwareBuffer>) {
138        if self.api_level < 26 {
139            return;
140        }
141        (*self.AHardwareBuffer_acquire)(buffer);
142    }
143    
144    
145
146    /// Remove a reference that was previously acquired with AHardwareBuffer_acquire() or AHardwareBuffer_allocate().  
147    /// Available since API level 26.
148    pub unsafe fn release(&self, buffer: NonNull<AHardwareBuffer>) {
149        if self.api_level < 26 {
150            return;
151        }
152        (*self.AHardwareBuffer_release)(buffer);
153    }
154    
155    /// Allocates a buffer that matches the passed AHardwareBuffer_Desc.  
156    /// If allocation succeeds, the buffer can be used according to the usage flags specified in its description.
157    /// If a buffer is used in ways not compatible with its usage flags, the results are undefined and may include program termination.  
158    /// Available since API level 26.
159    pub fn allocate(&self, desc: AHardwareBufferDesc) -> Option<HBRef> {
160        if self.api_level < 26 {
161            return None;
162        }
163        let mut desc = desc;
164        let mut buffer: *mut AHardwareBuffer = null_mut();
165        unsafe {
166            if (*self.AHardwareBuffer_allocate)(NonNull::new_unchecked(&mut desc), NonNull::new_unchecked(&mut buffer as *mut *mut AHardwareBuffer)) != 0 {
167                buffer = null_mut();
168            }
169        }
170        if buffer == null_mut() {
171            return None;
172        }
173        return Some(HBRef { data: unsafe { NonNull::new_unchecked(buffer) } });
174    }
175    
176    /// Return a description of the AHardwareBuffer.  
177    /// Available since API level 26.
178    pub fn describe(&self, buffer: &HBRef) -> Option<AHardwareBufferDesc> {
179        if self.api_level < 26 {
180            return None;
181        }
182        let mut desc: MaybeUninit<AHardwareBufferDesc> = MaybeUninit::uninit();
183        unsafe {
184            (*self.AHardwareBuffer_describe)(buffer.get(), NonNull::new_unchecked(desc.as_mut_ptr()));
185            return Some(desc.assume_init());
186        }
187    }
188    
189    /// Get the system wide unique id for an AHardwareBuffer.  
190    /// Available since API level 31.
191    pub fn get_id(&self, buffer: &HBRef) -> Option<u64> {
192        if self.api_level < 31 {
193            return None;
194        }
195        let mut id: u64 = 0;
196        unsafe {
197            if (*self.AHardwareBuffer_getId)(buffer.get(), NonNull::new_unchecked(&mut id)) != 0 {
198                None
199            } else {
200                Some(id)
201            }
202        }
203    }
204    
205    /// Test whether the given format and usage flag combination is allocatable.  
206    /// If this function returns true, it means that a buffer with the given description can be allocated on this implementation,
207    /// unless resource exhaustion occurs. If this function returns false, it means that the allocation of the given description will never succeed.  
208    /// The return value of this function may depend on all fields in the description, except stride, which is always ignored.
209    /// For example, some implementations have implementation-defined limits on texture size and layer count.  
210    /// Available since API level 29.
211    pub fn is_supported(&self, desc: &AHardwareBufferDesc) -> bool {
212        if self.api_level < 29 {
213            return false;
214        }
215        if (unsafe { *self.AHardwareBuffer_isSupported })(desc.into()) == 1 {
216            true
217        } else {
218            false
219        }
220    }
221    
222    /// Receive an AHardwareBuffer from an AF_UNIX socket. 
223    /// Available since API level 26.
224    pub fn recv(&self, socket: &UnixStream) -> Option<HBRef> {
225        if self.api_level < 26 {
226            return None;
227        }
228        let mut buffer: *mut AHardwareBuffer = null_mut();
229        unsafe {
230            if (*self.AHardwareBuffer_recvHandleFromUnixSocket)(socket.as_raw_fd(), NonNull::new_unchecked(&mut buffer as *mut *mut AHardwareBuffer)) != 0 {
231                buffer = null_mut();
232            }
233        }
234        if buffer == null_mut() {
235            return None;
236        }
237        return Some(HBRef { data: unsafe { NonNull::new_unchecked(buffer) } });
238    }
239    
240    /// Send the AHardwareBuffer to an AF_UNIX socket. 
241    /// Available since API level 26.
242    pub fn send(&self, socket: &UnixStream, buffer: &HBRef) -> bool {
243        if self.api_level < 26 {
244            return false;
245        }
246        if (unsafe { *self.AHardwareBuffer_sendHandleToUnixSocket })(buffer.get(), socket.as_raw_fd()) == 0 {
247            true
248        } else {
249            false
250        }
251    }
252    
253    /// Lock the AHardwareBuffer for direct CPU access.
254    /// Available since API level 26.
255    pub fn lock(&self, buffer: &HBRef, usage: u64, fence: Option<BorrowedFd>, rect: Option<&ARect>) -> *mut c_void {
256        if self.api_level < 26 {
257            return null_mut();
258        }
259        let mut out: *mut c_void = null_mut();
260        if (unsafe { *self.AHardwareBuffer_lock })(buffer.get(), usage, fence.and_then(|f| Some(f.as_raw_fd())).unwrap_or(-1), rect.and_then(|r| Some(r as *const ARect)).unwrap_or(null()), unsafe { NonNull::new_unchecked(&mut out) }) != 0 {
261            out = null_mut();
262        }
263        return out;
264    }
265    
266    /// Lock an AHardwareBuffer for direct CPU access. 
267    /// Available since API level 29. 
268    pub fn lock_info(&self, buffer: &HBRef, usage: u64, fence: Option<BorrowedFd>, rect: Option<&ARect>) -> Option<LockInfo> {
269        if self.api_level < 29 {
270            return None;
271        }
272        let mut bpp: i32 = 0;
273        let mut bps: i32 = 0;
274        let mut out: *mut c_void = null_mut();
275        if (unsafe { *self.AHardwareBuffer_lockAndGetInfo })(buffer.get(), usage, fence.and_then(|f| Some(f.as_raw_fd())).unwrap_or(-1), rect.and_then(|r| Some(r as *const ARect)).unwrap_or(null()), unsafe { NonNull::new_unchecked(&mut out) }, (&mut bpp).into(), (&mut bps).into()) != 0 {
276            return None;
277        }
278        return Some(LockInfo { address: out, bytes_per_pixel: bpp, bytes_per_stride: bps });
279    }
280    
281    /// Lock an AHardwareBuffer for direct CPU access. 
282    /// Available since API level 29. 
283    pub fn lock_planes(&self, buffer: &HBRef, usage: u64, fence: Option<BorrowedFd>, rect: Option<&ARect>) -> Option<AHardwareBufferPlanes> {
284        if self.api_level < 29 {
285            return None;
286        }
287        let mut out: MaybeUninit<AHardwareBufferPlanes> = MaybeUninit::uninit();
288        if (unsafe { *self.AHardwareBuffer_lockPlanes })(buffer.get(), usage, fence.and_then(|f| Some(f.as_raw_fd())).unwrap_or(-1), rect.and_then(|r| Some(r as *const ARect)).unwrap_or(null()), unsafe { NonNull::new_unchecked(out.as_mut_ptr()) }) != 0 {
289            return None;
290        }
291        return Some(unsafe { out.assume_init() });
292    }
293    
294    /// Unlock the AHardwareBuffer from direct CPU access. 
295    /// Available since API level 26.
296    pub fn unlock(&self, buffer: &HBRef) -> bool {
297        if self.api_level < 26 {
298            return false;
299        }
300        if (unsafe { *self.AHardwareBuffer_unlock })(buffer.get(), null_mut()) == 0 {
301            true
302        } else {
303            false
304        }
305    }
306}
307
308/// Container for a mapped AHardwareBuffer and additional information.
309pub struct LockInfo {
310    /// The address of the mapped buffer.
311    pub address: *mut c_void,
312    /// The bytes per pixel.
313    pub bytes_per_pixel: i32,
314    /// The bytes per stride.
315    pub bytes_per_stride: i32,
316}
317
318/// A reference-counting pointer to an AHardwareBuffer.
319#[repr(transparent)]
320pub struct HBRef {
321    data: NonNull<AHardwareBuffer>
322}
323
324impl HBRef {
325    /// Gets the underlying AHardwareBuffer pointer, without modifying the reference count.
326    pub fn get(&self) -> NonNull<AHardwareBuffer> {
327        self.data
328    }
329}
330
331impl Clone for HBRef {
332    fn clone(&self) -> Self {
333        unsafe { (*HB.AHardwareBuffer_acquire)(self.data) };
334        Self { data: self.data.clone() }
335    }
336}
337
338
339impl Drop for HBRef {
340    fn drop(&mut self) {
341        unsafe { (*HB.AHardwareBuffer_release)(self.data) };
342    }
343}