blob: 8b149149061314a978951e5a5191f3502ba651b3 [file]
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use anyhow::{bail, Result};
use atrace::AtraceTag;
use native_activity_thread_bindgen::ashmem_get_size_region;
use nix::sys::mman;
use std::{
convert::TryFrom,
os::fd::{AsRawFd, OwnedFd},
ptr::NonNull,
sync::OnceLock,
};
static DEFAULT_FONT_FAMILY: &str = "sans-serif";
struct Ashmem {
ptr: NonNull<libc::c_void>,
size: usize,
}
// SAFETY: `self.ptr` is mmap'ed and is valid across the entire process address space.
unsafe impl Send for Ashmem {}
impl TryFrom<OwnedFd> for Ashmem {
type Error = anyhow::Error;
fn try_from(fd: OwnedFd) -> Result<Self> {
// SAFETY: The passed fd is valid.
let size = unsafe { ashmem_get_size_region(fd.as_raw_fd()) };
if size < 0 {
bail!("Failed to get the size of system font map ashmem region")
}
let size = size as usize;
// SAFETY: The passed fd and length are valid.
let ptr = unsafe {
mman::mmap(
None,
size.try_into().unwrap(),
mman::ProtFlags::PROT_READ,
mman::MapFlags::MAP_SHARED,
fd,
0,
)?
};
Ok(Self { ptr, size })
}
}
impl Drop for Ashmem {
fn drop(&mut self) {
// SAFETY: `self.ptr` points to memory returned from `mmap` and `self.size` is the length of
// the mapped memory.
let _ = unsafe { mman::munmap(self.ptr, self.size) };
}
}
impl AsRef<[u8]> for Ashmem {
fn as_ref(&self) -> &[u8] {
// SAFETY: `self.ptr` is returned from mmap(2) thus addr points to `self.size` consecutive
// bytes of valid memory.
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr() as *const u8, self.size) }
}
}
struct SystemFontMap(Ashmem);
// SAFETY: System font map is only set once and is immutable after that.
unsafe impl Sync for SystemFontMap {}
// The mmap'ed regions of the system font map needs to be valid for the entire process lifetime as
// some data structures in `libminikin` directly point to it.
static SYSTEM_FONT_MAP: OnceLock<SystemFontMap> = OnceLock::new();
fn set_system_font_map(fd: OwnedFd) -> Result<&'static SystemFontMap> {
let ashmem = Ashmem::try_from(fd)?;
SYSTEM_FONT_MAP
.set(SystemFontMap(ashmem))
.map_err(|_| anyhow::anyhow!("Cannot update system font map once set"))?;
Ok(SYSTEM_FONT_MAP.get().expect("system font map not set"))
}
struct SystemFontMapReader {
data: &'static [u8],
position: usize,
}
impl SystemFontMapReader {
fn new(system_font_map: &'static SystemFontMap) -> Self {
Self { data: system_font_map.0.as_ref(), position: 0 }
}
fn remaining_slice(&self) -> &[u8] {
&self.data[self.position..]
}
fn consume(&mut self, size: usize) {
self.position += size;
}
// Consumes minikin::FontStyle
fn consume_font_style(&mut self) {
// mWeight
self.consume(std::mem::size_of::<u16>());
// mSlant
self.consume(std::mem::size_of::<u8>());
}
// Consumes bytes for android::Typeface
fn consume_type_face(&mut self) {
self.consume_font_style();
// android::Typeface::Style
self.consume(std::mem::size_of::<u8>());
// Typeface base weight
self.consume(std::mem::size_of::<i32>());
}
fn read_four_bytes(&mut self) -> [u8; 4] {
let size = std::mem::size_of::<u32>();
let bytes = self.remaining_slice()[..size].try_into().unwrap();
self.consume(size);
bytes
}
fn read_be_i32(&mut self) -> i32 {
i32::from_be_bytes(self.read_four_bytes())
}
fn read_le_u32(&mut self) -> u32 {
u32::from_le_bytes(self.read_four_bytes())
}
fn read_be_u32(&mut self) -> u32 {
u32::from_be_bytes(self.read_four_bytes())
}
fn read_string(&mut self) -> Result<String> {
let len = self.read_be_u32() as usize;
let bytes = self.remaining_slice()[..len].to_vec();
self.consume(len);
Ok(String::from_utf8(bytes)?)
}
}
pub fn load_system_font_map(fd: OwnedFd) -> Result<()> {
atrace::trace_method!(AtraceTag::Graphics);
let system_font_map = set_system_font_map(fd)?;
let mut reader = SystemFontMapReader::new(system_font_map);
let typeface_bytes_count = reader.read_be_i32();
ffi::minikin_font_skia_factory_init();
let mut bytes_read = 0usize;
let collections =
ffi::font_collection_read_vector_slice(reader.remaining_slice(), &mut bytes_read);
reader.consume(bytes_read);
let typeface_count = reader.read_le_u32();
let mut font_collections = Vec::with_capacity(typeface_count as usize);
for _ in 0..typeface_count {
let idx = reader.read_le_u32();
reader.consume_type_face();
font_collections.push(ffi::vector_get(&collections, idx as usize));
}
if typeface_bytes_count as usize != reader.position {
bail!(
"Typeface bytes count does not match: expected {} but got {}",
typeface_bytes_count,
reader.position
);
}
for font_collection in font_collections {
let name = reader.read_string()?;
if name == DEFAULT_FONT_FAMILY {
ffi::system_fonts_register_default(font_collection.clone());
}
if matches!(
name.as_str(),
"serif" | "sans-serif" | "cursive" | "fantasy" | "monospace" | "system-ui"
) {
cxx::let_cxx_string!(cname = name);
ffi::system_fonts_register_fallback(&cname, font_collection.clone());
}
ffi::system_fonts_add_font_map(font_collection);
}
Ok(())
}
#[cxx::bridge]
pub mod ffi {
#[namespace = "minikin"]
unsafe extern "C++" {
include!("SystemFontsBridge/SystemFontsBridge.h");
type FontCollection;
type FontCollectionPtrs;
fn font_collection_read_vector_slice(
data: &[u8],
bytes_read: &mut usize,
) -> UniquePtr<FontCollectionPtrs>;
fn vector_get(collections: &FontCollectionPtrs, index: usize) -> SharedPtr<FontCollection>;
fn system_fonts_add_font_map(collection: SharedPtr<FontCollection>);
fn system_fonts_register_default(collection: SharedPtr<FontCollection>);
fn system_fonts_register_fallback(
family_name: &CxxString,
collection: SharedPtr<FontCollection>,
);
}
#[namespace = "android"]
unsafe extern "C++" {
include!("SystemFontsBridge/SystemFontsBridge.h");
fn minikin_font_skia_factory_init();
}
}