gpu_display/
gpu_display_android.rs

1// Copyright 2024 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::collections::HashMap;
6use std::ffi::c_char;
7use std::ffi::CStr;
8use std::ffi::CString;
9use std::os::fd::AsFd;
10use std::os::fd::AsRawFd;
11use std::panic::catch_unwind;
12use std::process::abort;
13use std::ptr::NonNull;
14use std::rc::Rc;
15use std::slice;
16use std::sync::Arc;
17use std::sync::RwLock;
18
19use anyhow::bail;
20use base::error;
21use base::AsRawDescriptor;
22use base::Event;
23use base::RawDescriptor;
24use base::VolatileSlice;
25use rutabaga_gfx::AhbInfo;
26use sync::Waitable;
27use vm_control::gpu::DisplayParameters;
28
29use crate::DisplayExternalResourceImport;
30use crate::DisplayT;
31use crate::FlipToExtraInfo;
32use crate::GpuDisplayError;
33use crate::GpuDisplayFramebuffer;
34use crate::GpuDisplayResult;
35use crate::GpuDisplaySurface;
36use crate::SemaphoreTimepoint;
37use crate::SurfaceType;
38use crate::SysDisplayT;
39
40// Opaque blob
41#[repr(C)]
42pub(crate) struct AndroidDisplayContext {
43    _data: [u8; 0],
44    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
45}
46
47// Opaque blob
48#[repr(C)]
49pub(crate) struct AndroidDisplaySurface {
50    _data: [u8; 0],
51    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
52}
53
54// Should be the same as ANativeWindow_Buffer in android/native_window.h
55// Note that this struct is part of NDK; guaranteed to be stable, so we use it directly across the
56// FFI.
57#[repr(C)]
58pub(crate) struct ANativeWindow_Buffer {
59    width: i32,
60    height: i32,
61    stride: i32, // in number of pixels, NOT bytes
62    format: i32,
63    bits: *mut u8,
64    reserved: [u32; 6],
65}
66
67pub(crate) type ErrorCallback = unsafe extern "C" fn(message: *const c_char);
68
69extern "C" {
70    /// Constructs an AndroidDisplayContext for this backend. This awlays returns a valid (ex:
71    /// non-null) handle to the context. The `name` parameter is from crosvm commandline and the
72    /// client of crosvm will use it to locate and communicate to the AndroidDisplayContext. For
73    /// example, this can be a path to UNIX domain socket where a RPC binder server listens on.
74    /// `error_callback` is a function pointer to an error reporting function, and will be used by
75    /// this and other functions below when something goes wrong. The returned context should be
76    /// destroyed by calling `destroy_android_display_context` if this backend is no longer in use.
77    fn create_android_display_context(
78        name: *const c_char,
79        error_callback: ErrorCallback,
80    ) -> *mut AndroidDisplayContext;
81
82    /// Destroys the AndroidDisplayContext created from `create_android_display_context`.
83    fn destroy_android_display_context(self_: *mut AndroidDisplayContext);
84
85    /// Creates an Android Surface (which is also called as Window) of given size. If the surface
86    /// can't be created for whatever reason, null pointer is returned, in which case we shouldn't
87    /// proceed further.
88    fn create_android_surface(
89        ctx: *mut AndroidDisplayContext,
90        width: u32,
91        height: u32,
92        for_cursor: bool,
93    ) -> *mut AndroidDisplaySurface;
94
95    /// Destroys the Android surface created from `create_android_surface`.
96    #[allow(dead_code)]
97    fn destroy_android_surface(
98        ctx: *mut AndroidDisplayContext,
99        surface: *mut AndroidDisplaySurface,
100    );
101
102    /// Obtains one buffer from the given Android Surface. The information about the buffer (buffer
103    /// address, size, stride, etc) is reported via the `ANativeWindow_Buffer` struct. It shouldn't
104    /// be null. The size of the buffer is guaranteed to be bigger than (width * stride * 4) bytes.
105    /// This function locks the buffer for the client, which means the caller has the exclusive
106    /// access to the buffer until it is returned back to Android display stack (surfaceflinger) by
107    /// calling `post_android_surface_buffer`. This function may fail (in which case false is
108    /// returned), then the caller shouldn't try to read `out_buffer` or use the buffer in any way.
109    fn get_android_surface_buffer(
110        ctx: *mut AndroidDisplayContext,
111        surface: *mut AndroidDisplaySurface,
112        out_buffer: *mut ANativeWindow_Buffer,
113    ) -> bool;
114
115    fn set_android_surface_position(ctx: *mut AndroidDisplayContext, x: u32, y: u32);
116
117    /// Posts the buffer obtained from `get_android_surface_buffer` to the Android display system
118    /// so that it can be displayed on the screen. Once this is called, the caller shouldn't use
119    /// the buffer any more.
120    fn post_android_surface_buffer(
121        ctx: *mut AndroidDisplayContext,
122        surface: *mut AndroidDisplaySurface,
123    );
124
125    fn android_display_flip_to(
126        ctx: *mut AndroidDisplayContext,
127        _surface: *mut AndroidDisplaySurface,
128        ahb_info: *const AHardwareBufferInfo,
129    );
130}
131
132unsafe extern "C" fn error_callback(message: *const c_char) {
133    catch_unwind(|| {
134        error!(
135            "{}",
136            // SAFETY: message is null terminated
137            unsafe { CStr::from_ptr(message) }.to_string_lossy()
138        )
139    })
140    .unwrap_or_else(|_| abort())
141}
142
143struct AndroidDisplayContextWrapper(NonNull<AndroidDisplayContext>);
144
145impl Drop for AndroidDisplayContextWrapper {
146    fn drop(&mut self) {
147        // SAFETY: this object is constructed from create_android_display_context
148        unsafe { destroy_android_display_context(self.0.as_ptr()) };
149    }
150}
151
152impl Default for ANativeWindow_Buffer {
153    fn default() -> Self {
154        Self {
155            width: 0,
156            height: 0,
157            stride: 0,
158            format: 0,
159            bits: std::ptr::null_mut(),
160            reserved: [0u32; 6],
161        }
162    }
163}
164
165impl From<ANativeWindow_Buffer> for GpuDisplayFramebuffer<'_> {
166    fn from(anb: ANativeWindow_Buffer) -> Self {
167        // TODO: check anb.format to see if it's ARGB8888?
168        // TODO: infer bpp from anb.format?
169        const BYTES_PER_PIXEL: u32 = 4;
170        let stride_bytes = BYTES_PER_PIXEL * u32::try_from(anb.stride).unwrap();
171        let buffer_size = stride_bytes * u32::try_from(anb.height).unwrap();
172        let buffer =
173            // SAFETY: get_android_surface_buffer guarantees that bits points to a valid buffer and
174            // the buffer remains available until post_android_surface_buffer is called.
175            unsafe { slice::from_raw_parts_mut(anb.bits, buffer_size.try_into().unwrap()) };
176        Self::new(VolatileSlice::new(buffer), stride_bytes, BYTES_PER_PIXEL)
177    }
178}
179
180#[repr(C)]
181pub struct AHardwareBufferInfo {
182    pub num_fds: usize,
183    pub fds_ptr: *const i32,
184    pub metadata_len: usize,
185    pub metadata_ptr: *const u8,
186}
187
188// The key is an import id.
189type AHardwareBufferImportMap = HashMap<u32, AhbInfo>;
190
191struct AndroidSurface {
192    context: Rc<AndroidDisplayContextWrapper>,
193    surface: NonNull<AndroidDisplaySurface>,
194    ahb_import_map: Arc<RwLock<AHardwareBufferImportMap>>,
195}
196
197impl GpuDisplaySurface for AndroidSurface {
198    fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
199        let mut anb = ANativeWindow_Buffer::default();
200        // SAFETY: context and surface are opaque handles and buf is used as the out parameter to
201        // hold the return values.
202        let success = unsafe {
203            get_android_surface_buffer(
204                self.context.0.as_ptr(),
205                self.surface.as_ptr(),
206                &mut anb as *mut ANativeWindow_Buffer,
207            )
208        };
209        if success {
210            Some(anb.into())
211        } else {
212            None
213        }
214    }
215
216    fn flip(&mut self) {
217        // SAFETY: context and surface are opaque handles.
218        unsafe { post_android_surface_buffer(self.context.0.as_ptr(), self.surface.as_ptr()) }
219    }
220
221    fn set_position(&mut self, x: u32, y: u32) {
222        // SAFETY: context is an opaque handle.
223        unsafe { set_android_surface_position(self.context.0.as_ptr(), x, y) };
224    }
225
226    fn flip_to(
227        &mut self,
228        import_id: u32,
229        _acquire_timepoint: Option<SemaphoreTimepoint>,
230        _release_timepoint: Option<SemaphoreTimepoint>,
231        _extra_info: Option<FlipToExtraInfo>,
232    ) -> anyhow::Result<Waitable> {
233        {
234            let ahb_import_map = self.ahb_import_map.read().expect("failed to get a lock");
235            let ahb = ahb_import_map
236                .get(&import_id)
237                .ok_or(GpuDisplayError::InvalidImportId)?;
238            let fds: Vec<i32> = ahb.fds.iter().map(|fd| fd.as_fd().as_raw_fd()).collect();
239
240            let info = AHardwareBufferInfo {
241                num_fds: fds.len(),
242                fds_ptr: fds.as_ptr(),
243                metadata_len: ahb.metadata.len(),
244                metadata_ptr: ahb.metadata.as_ptr(),
245            };
246            // SAFETY:
247            // Safe because the `AHardwareBufferInfo` outlives the call, and
248            // `android_display_flip_to` does not retain the pointer.
249            unsafe {
250                android_display_flip_to(
251                    self.context.0.as_ptr(),
252                    self.surface.as_ptr(),
253                    &info as *const AHardwareBufferInfo,
254                )
255            };
256        }
257        Ok(Waitable::signaled())
258    }
259}
260
261pub struct DisplayAndroid {
262    context: Rc<AndroidDisplayContextWrapper>,
263    /// This event is never triggered and is used solely to fulfill AsRawDescriptor.
264    event: Event,
265    // The key is a surface id.
266    surface_ahbs_map: HashMap<u32, Arc<RwLock<AHardwareBufferImportMap>>>,
267}
268
269impl DisplayAndroid {
270    pub fn new(name: &str) -> GpuDisplayResult<DisplayAndroid> {
271        let name = CString::new(name).unwrap();
272        let context = NonNull::new(
273            // SAFETY: service_name is not leaked outside of this function
274            unsafe { create_android_display_context(name.as_ptr(), error_callback) },
275        )
276        .ok_or(GpuDisplayError::Unsupported)?;
277        let context = AndroidDisplayContextWrapper(context);
278        let event = Event::new().map_err(|_| GpuDisplayError::CreateEvent)?;
279        Ok(DisplayAndroid {
280            context: context.into(),
281            event,
282            surface_ahbs_map: HashMap::new(),
283        })
284    }
285}
286
287impl DisplayT for DisplayAndroid {
288    fn create_surface(
289        &mut self,
290        parent_surface_id: Option<u32>,
291        surface_id: u32,
292        _scanout_id: Option<u32>,
293        display_params: &DisplayParameters,
294        _surf_type: SurfaceType,
295    ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
296        let (requested_width, requested_height) = display_params.get_virtual_display_size();
297        // SAFETY: context is an opaque handle.
298        let surface = NonNull::new(unsafe {
299            create_android_surface(
300                self.context.0.as_ptr(),
301                requested_width,
302                requested_height,
303                parent_surface_id.is_some(),
304            )
305        })
306        .ok_or(GpuDisplayError::CreateSurface)?;
307        let ahb_import_map = self.surface_ahbs_map.entry(surface_id).or_default();
308
309        Ok(Box::new(AndroidSurface {
310            context: self.context.clone(),
311            surface,
312            ahb_import_map: Arc::clone(ahb_import_map),
313        }))
314    }
315
316    fn release_surface(&mut self, surface_id: u32) {
317        self.surface_ahbs_map.remove(&surface_id);
318    }
319
320    fn import_resource(
321        &mut self,
322        import_id: u32,
323        surface_id: u32,
324        external_display_resource: DisplayExternalResourceImport,
325    ) -> anyhow::Result<()> {
326        let DisplayExternalResourceImport::AHardwareBuffer { info } = external_display_resource
327        else {
328            bail!("gpu_display_android only supports AHardwareBufferInfo imports");
329        };
330
331        {
332            let mut ahbs = self
333                .surface_ahbs_map
334                .entry(surface_id)
335                .or_default()
336                .write()
337                .expect("failed to get a lock");
338            ahbs.insert(import_id, info);
339        }
340        Ok(())
341    }
342
343    fn release_import(&mut self, surface_id: u32, import_id: u32) {
344        let mut ahbs = self
345            .surface_ahbs_map
346            .entry(surface_id)
347            .or_default()
348            .write()
349            .expect("failed to get a lock");
350        ahbs.remove(&import_id);
351    }
352}
353
354impl SysDisplayT for DisplayAndroid {}
355
356impl AsRawDescriptor for DisplayAndroid {
357    fn as_raw_descriptor(&self) -> RawDescriptor {
358        self.event.as_raw_descriptor()
359    }
360}