Add methods to lock and unlock HardwareBuffer.

Bug: 371874777
Test: atest libnativewindow_rs-internal_test
Change-Id: I1494f17720761db6dbc7ba63ba51c5ec91536f74
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 014c912..9876362 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -31,11 +31,13 @@
 };
 use ffi::{
     AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_readFromParcel,
-    AHardwareBuffer_writeToParcel,
+    AHardwareBuffer_writeToParcel, ARect,
 };
+use std::ffi::c_void;
 use std::fmt::{self, Debug, Formatter};
-use std::mem::ManuallyDrop;
-use std::ptr::{self, null_mut, NonNull};
+use std::mem::{forget, ManuallyDrop};
+use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
+use std::ptr::{self, null, null_mut, NonNull};
 
 /// Wrapper around a C `AHardwareBuffer_Desc`.
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -267,10 +269,141 @@
             rfu0: 0,
             rfu1: 0,
         };
-        // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null.
+        // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the
+        // AHardwareBuffer_Desc pointer is valid because it comes from a reference.
         unsafe { ffi::AHardwareBuffer_describe(self.0.as_ref(), &mut buffer_desc) };
         HardwareBufferDescription(buffer_desc)
     }
+
+    /// Locks the hardware buffer for direct CPU access.
+    ///
+    /// # Safety
+    ///
+    /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed
+    ///   before calling this function.
+    /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the
+    ///   buffer simultaneously, but the caller must ensure that they don't access it simultaneously
+    ///   and break Rust's aliasing rules, like any other shared memory.
+    /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or
+    ///   `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or
+    ///   processes lock the buffer simultaneously for any usage.
+    /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing
+    ///   simultaneously.
+    /// - If `rect` is not `None`, the caller must not modify the buffer outside of that rectangle.
+    pub unsafe fn lock<'a>(
+        &'a self,
+        usage: AHardwareBuffer_UsageFlags,
+        fence: Option<BorrowedFd>,
+        rect: Option<&ARect>,
+    ) -> Result<HardwareBufferGuard<'a>, StatusCode> {
+        let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 };
+        let rect = rect.map(ptr::from_ref).unwrap_or(null());
+        let mut address = null_mut();
+        // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the buffer address out
+        // pointer is valid because it comes from a reference. Our caller promises that writes have
+        // completed and there will be no simultaneous read/write locks.
+        let status = unsafe {
+            ffi::AHardwareBuffer_lock(self.0.as_ptr(), usage.0, fence, rect, &mut address)
+        };
+        status_result(status)?;
+        Ok(HardwareBufferGuard {
+            buffer: self,
+            address: NonNull::new(address)
+                .expect("AHardwareBuffer_lock set a null outVirtualAddress"),
+        })
+    }
+
+    /// Locks the hardware buffer for direct CPU access, returning information about the bytes per
+    /// pixel and stride as well.
+    ///
+    /// # Safety
+    ///
+    /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed
+    ///   before calling this function.
+    /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the
+    ///   buffer simultaneously, but the caller must ensure that they don't access it simultaneously
+    ///   and break Rust's aliasing rules, like any other shared memory.
+    /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or
+    ///   `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or
+    ///   processes lock the buffer simultaneously for any usage.
+    /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing
+    ///   simultaneously.
+    pub unsafe fn lock_and_get_info<'a>(
+        &'a self,
+        usage: AHardwareBuffer_UsageFlags,
+        fence: Option<BorrowedFd>,
+        rect: Option<&ARect>,
+    ) -> Result<LockedBufferInfo<'a>, StatusCode> {
+        let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 };
+        let rect = rect.map(ptr::from_ref).unwrap_or(null());
+        let mut address = null_mut();
+        let mut bytes_per_pixel = 0;
+        let mut stride = 0;
+        // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the various out
+        // pointers are valid because they come from references. Our caller promises that writes have
+        // completed and there will be no simultaneous read/write locks.
+        let status = unsafe {
+            ffi::AHardwareBuffer_lockAndGetInfo(
+                self.0.as_ptr(),
+                usage.0,
+                fence,
+                rect,
+                &mut address,
+                &mut bytes_per_pixel,
+                &mut stride,
+            )
+        };
+        status_result(status)?;
+        Ok(LockedBufferInfo {
+            guard: HardwareBufferGuard {
+                buffer: self,
+                address: NonNull::new(address)
+                    .expect("AHardwareBuffer_lockAndGetInfo set a null outVirtualAddress"),
+            },
+            bytes_per_pixel: bytes_per_pixel as u32,
+            stride: stride as u32,
+        })
+    }
+
+    /// Unlocks the hardware buffer from direct CPU access.
+    ///
+    /// Must be called after all changes to the buffer are completed by the caller. This will block
+    /// until the unlocking is complete and the buffer contents are updated.
+    fn unlock(&self) -> Result<(), StatusCode> {
+        // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid.
+        let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), null_mut()) };
+        status_result(status)?;
+        Ok(())
+    }
+
+    /// Unlocks the hardware buffer from direct CPU access.
+    ///
+    /// Must be called after all changes to the buffer are completed by the caller.
+    ///
+    /// This may not block until all work is completed, but rather will return a file descriptor
+    /// which will be signalled once the unlocking is complete and the buffer contents is updated.
+    /// If `Ok(None)` is returned then unlocking has already completed and no further waiting is
+    /// necessary. The file descriptor may be passed to a subsequent call to [`Self::lock`].
+    pub fn unlock_with_fence(
+        &self,
+        guard: HardwareBufferGuard,
+    ) -> Result<Option<OwnedFd>, StatusCode> {
+        // Forget the guard so that its `Drop` implementation doesn't try to unlock the
+        // HardwareBuffer again.
+        forget(guard);
+
+        let mut fence = -2;
+        // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid.
+        let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), &mut fence) };
+        let fence = if fence < 0 {
+            None
+        } else {
+            // SAFETY: `AHardwareBuffer_unlock` gives us ownership of the fence file descriptor.
+            Some(unsafe { OwnedFd::from_raw_fd(fence) })
+        };
+        status_result(status)?;
+        Ok(fence)
+    }
 }
 
 impl Drop for HardwareBuffer {
@@ -346,6 +479,37 @@
 //     according to the docs on the underlying gralloc calls)
 unsafe impl Sync for HardwareBuffer {}
 
+/// A guard for when a `HardwareBuffer` is locked.
+///
+/// The `HardwareBuffer` will be unlocked when this is dropped, or may be unlocked via
+/// [`HardwareBuffer::unlock_with_fence`].
+#[derive(Debug)]
+pub struct HardwareBufferGuard<'a> {
+    buffer: &'a HardwareBuffer,
+    /// The address of the buffer in memory.
+    pub address: NonNull<c_void>,
+}
+
+impl<'a> Drop for HardwareBufferGuard<'a> {
+    fn drop(&mut self) {
+        self.buffer
+            .unlock()
+            .expect("Failed to unlock HardwareBuffer when dropping HardwareBufferGuard");
+    }
+}
+
+/// A guard for when a `HardwareBuffer` is locked, with additional information about the number of
+/// bytes per pixel and stride.
+#[derive(Debug)]
+pub struct LockedBufferInfo<'a> {
+    /// The locked buffer guard.
+    pub guard: HardwareBufferGuard<'a>,
+    /// The number of bytes used for each pixel in the buffer.
+    pub bytes_per_pixel: u32,
+    /// The stride in bytes between rows in the buffer.
+    pub stride: u32,
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
@@ -499,4 +663,108 @@
         assert_eq!(buffer.description(), buffer_description);
         assert_eq!(buffer2.description(), buffer_description);
     }
+
+    #[test]
+    fn lock() {
+        let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+            1024,
+            512,
+            1,
+            AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+            AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+            0,
+        ))
+        .expect("Failed to create buffer");
+
+        // SAFETY: No other threads or processes have access to the buffer.
+        let guard = unsafe {
+            buffer.lock(
+                AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+                None,
+                None,
+            )
+        }
+        .unwrap();
+
+        drop(guard);
+    }
+
+    #[test]
+    fn lock_with_rect() {
+        let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+            1024,
+            512,
+            1,
+            AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+            AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+            0,
+        ))
+        .expect("Failed to create buffer");
+        let rect = ARect { left: 10, right: 20, top: 35, bottom: 45 };
+
+        // SAFETY: No other threads or processes have access to the buffer.
+        let guard = unsafe {
+            buffer.lock(
+                AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+                None,
+                Some(&rect),
+            )
+        }
+        .unwrap();
+
+        drop(guard);
+    }
+
+    #[test]
+    fn unlock_with_fence() {
+        let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+            1024,
+            512,
+            1,
+            AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+            AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+            0,
+        ))
+        .expect("Failed to create buffer");
+
+        // SAFETY: No other threads or processes have access to the buffer.
+        let guard = unsafe {
+            buffer.lock(
+                AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+                None,
+                None,
+            )
+        }
+        .unwrap();
+
+        buffer.unlock_with_fence(guard).unwrap();
+    }
+
+    #[test]
+    fn lock_with_info() {
+        const WIDTH: u32 = 1024;
+        let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+            WIDTH,
+            512,
+            1,
+            AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+            AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+            0,
+        ))
+        .expect("Failed to create buffer");
+
+        // SAFETY: No other threads or processes have access to the buffer.
+        let info = unsafe {
+            buffer.lock_and_get_info(
+                AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+                None,
+                None,
+            )
+        }
+        .unwrap();
+
+        assert_eq!(info.bytes_per_pixel, 4);
+        assert_eq!(info.stride, WIDTH * 4);
+        drop(info);
+    }
 }