1use crate::ContainerStartInfo;
6use anyhow::{Context, Error, anyhow};
7use fidl_fuchsia_ui_composition as fuicomposition;
8use fidl_fuchsia_ui_input3 as fuiinput;
9use fidl_fuchsia_ui_policy as fuipolicy;
10use fidl_fuchsia_ui_views as fuiviews;
11use starnix_consent_sync::init as consent_sync_init;
12use starnix_container_structured_config::Config as ContainerStructuredConfig;
13use starnix_core::device::block::add_mmc_block_device;
14use starnix_core::mm::MlockPinFlavor;
15use starnix_core::task::{CurrentTask, Kernel, KernelFeatures, SystemLimits};
16use starnix_core::vfs::FsString;
17use starnix_features::Feature;
18use starnix_logging::log_error;
19use starnix_modules_ashmem::ashmem_device_init;
20use starnix_modules_boot::booted_device_init;
21use starnix_modules_fastrpc::fastrpc_device_init;
22use starnix_modules_framebuffer::{AspectRatio, Framebuffer};
23use starnix_modules_gpu::gpu_device_init;
24use starnix_modules_gralloc::gralloc_device_init;
25use starnix_modules_hvdcp_opti::hvdcp_opti_init;
26use starnix_modules_input::uinput::register_uinput_device;
27use starnix_modules_input::{
28 DEFAULT_KEYBOARD_DEVICE_ID, DEFAULT_MOUSE_DEVICE_ID, DEFAULT_TOUCH_DEVICE_ID, EventProxyMode,
29 InputDevice, new_input_relay,
30};
31use starnix_modules_kgsl::kgsl_device_init;
32use starnix_modules_magma::magma_device_init;
33use starnix_modules_nanohub::nanohub_device_init;
34use starnix_modules_nmfs::nmfs_init;
35use starnix_modules_perfetto_consumer::start_perfetto_consumer_thread;
36use starnix_modules_thermal::{cooling_device_init, thermal_device_init};
37use starnix_modules_touch_power_policy::TouchPowerPolicyDevice;
38use starnix_modules_wakeup_test::register_wakeup_test_device;
39use starnix_sync::{Locked, Unlocked};
40use starnix_uapi::error;
41use starnix_uapi::errors::Errno;
42use std::sync::mpsc::channel;
43
44#[derive(Default, Debug)]
46pub struct Features {
47 pub kernel: KernelFeatures,
49
50 pub selinux: SELinuxFeature,
52
53 pub system_limits: SystemLimits,
55
56 pub ashmem: bool,
58
59 pub boot_notifier: bool,
61
62 pub data_collection_consent_sync: bool,
64
65 pub boot_notifier_cpu_boost: Option<zx::MonotonicDuration>,
67
68 pub framebuffer: bool,
70
71 pub aspect_ratio: Option<AspectRatio>,
73
74 pub enable_visual_debugging: bool,
77
78 pub gralloc: bool,
80
81 pub kgsl: bool,
83
84 pub magma_supported_vendors: Option<Vec<u16>>,
86
87 pub gfxstream: bool,
89
90 pub container: bool,
92
93 pub test_data: bool,
95
96 pub custom_artifacts: bool,
98
99 pub android_serialno: bool,
101
102 pub perfetto: Option<FsString>,
104
105 pub rootfs_rw: bool,
107
108 pub network_manager: bool,
110
111 pub nanohub: bool,
113
114 pub fastrpc: bool,
116
117 pub enable_utc_time_adjustment: bool,
118
119 pub thermal: bool,
120
121 pub cooling: Option<Vec<String>>,
124
125 pub android_bootreason: bool,
127
128 pub hvdcp_opti: bool,
129
130 pub additional_mounts: Option<Vec<String>>,
131
132 pub wakeup_test: bool,
134
135 pub mmcblk_stub: bool,
138}
139
140#[derive(Default, Debug, PartialEq)]
141pub struct SELinuxFeature {
142 pub enabled: bool,
144
145 pub options: String,
147
148 pub exceptions: Vec<String>,
150}
151
152impl Features {
153 pub fn record_inspect(&self, parent_node: &fuchsia_inspect::Node) {
154 parent_node.record_child("features", |inspect_node| match self {
155 Features {
156 kernel:
157 KernelFeatures {
158 bpf_v2,
159 enable_suid,
160 io_uring,
161 error_on_failed_reboot,
162 default_uid,
163 default_seclabel,
164 selinux_test_suite,
165 default_ns_mount_options,
166 mlock_always_onfault,
167 mlock_pin_flavor,
168 crash_report_throttling,
169 wifi,
170 cached_zx_map_info_bytes,
171 dirent_cache_size,
172 fake_ion,
173 },
174 system_limits,
175 selinux,
176 ashmem,
177 boot_notifier,
178 boot_notifier_cpu_boost,
179 data_collection_consent_sync,
180 framebuffer,
181 aspect_ratio,
182 enable_visual_debugging,
183 gralloc,
184 kgsl,
185 magma_supported_vendors,
186 gfxstream,
187 container,
188 test_data,
189 custom_artifacts,
190 android_serialno,
191 perfetto,
192 rootfs_rw,
193 network_manager,
194 nanohub,
195 fastrpc,
196 enable_utc_time_adjustment,
197 thermal,
198 cooling,
199 android_bootreason,
200 hvdcp_opti,
201 additional_mounts,
202 wakeup_test,
203 mmcblk_stub,
204 } => {
205 inspect_node.record_bool("selinux", selinux.enabled);
206 inspect_node.record_bool("ashmem", *ashmem);
207 inspect_node.record_bool("boot_notifier", *boot_notifier);
208 inspect_node
209 .record_bool("data_collection_consent_sync", *data_collection_consent_sync);
210 inspect_node.record_string(
211 "boot_notifier_cpu_boost",
212 boot_notifier_cpu_boost
213 .map(|d| format!("{}s", d.into_seconds()))
214 .unwrap_or_else(|| "none".to_string()),
215 );
216 inspect_node.record_bool("framebuffer", *framebuffer);
217 inspect_node.record_bool("gralloc", *gralloc);
218 inspect_node.record_bool("kgsl", *kgsl);
219 inspect_node.record_string(
220 "magma_supported_vendors",
221 match magma_supported_vendors {
222 Some(vendors) => vendors
223 .iter()
224 .map(|vendor| format!("0x{:x}", vendor))
225 .collect::<Vec<String>>()
226 .join(","),
227 None => "".to_string(),
228 },
229 );
230 inspect_node.record_bool("gfxstream", *gfxstream);
231 inspect_node.record_bool("container", *container);
232 inspect_node.record_bool("test_data", *test_data);
233 inspect_node.record_bool("custom_artifacts", *custom_artifacts);
234 inspect_node.record_bool("android_serialno", *android_serialno);
235 inspect_node.record_string(
236 "aspect_ratio",
237 aspect_ratio
238 .as_ref()
239 .map(|aspect_ratio| {
240 format!("width: {} height: {}", aspect_ratio.width, aspect_ratio.height)
241 })
242 .unwrap_or_default(),
243 );
244 inspect_node.record_string(
245 "perfetto",
246 perfetto.as_ref().map(|p| p.to_string()).unwrap_or_default(),
247 );
248 inspect_node.record_bool("rootfs_rw", *rootfs_rw);
249 inspect_node.record_bool("network_manager", *network_manager);
250 inspect_node.record_bool("nanohub", *nanohub);
251 inspect_node.record_bool("fastrpc", *fastrpc);
252 inspect_node.record_bool("thermal", *thermal);
253 inspect_node.record_string(
254 "cooling",
255 match cooling {
256 Some(devices) => devices.join(","),
257 None => "".to_string(),
258 },
259 );
260 inspect_node.record_bool("android_bootreason", *android_bootreason);
261 inspect_node.record_bool("hvdcp_opti", *hvdcp_opti);
262 inspect_node.record_string("ping_group_range", {
263 let range = system_limits.socket.icmp_ping_gids.lock();
264 std::format!("{},{}", range.start, range.end - 1)
265 });
266
267 inspect_node.record_child("kernel", |kernel_node| {
268 kernel_node.record_bool("bpf_v2", *bpf_v2);
269 kernel_node.record_bool("enable_suid", *enable_suid);
270 kernel_node.record_bool("io_uring", *io_uring);
271 kernel_node.record_bool("error_on_failed_reboot", *error_on_failed_reboot);
272 kernel_node.record_bool("enable_visual_debugging", *enable_visual_debugging);
273 kernel_node.record_int("default_uid", (*default_uid).into());
274 kernel_node.record_string(
275 "default_seclabel",
276 default_seclabel.as_deref().unwrap_or_default(),
277 );
278 kernel_node.record_bool("selinux_test_suite", *selinux_test_suite);
279 kernel_node.record_bool("crash_report_throttling", *crash_report_throttling);
280 kernel_node
281 .record_uint("cached_zx_map_info_bytes", *cached_zx_map_info_bytes as u64);
282 inspect_node.record_string(
283 "default_ns_mount_options",
284 format!("{:?}", default_ns_mount_options),
285 );
286 inspect_node
287 .record_bool("enable_utc_time_adjustment", *enable_utc_time_adjustment);
288 inspect_node.record_bool("mlock_always_onfault", *mlock_always_onfault);
289 inspect_node
290 .record_string("mlock_pin_flavor", format!("{:?}", mlock_pin_flavor));
291 inspect_node.record_bool("wifi", *wifi);
292 inspect_node
293 .record_string("additional_mounts", format!("{:?}", additional_mounts));
294 inspect_node.record_uint("dirent_cache_size", *dirent_cache_size as u64);
295 inspect_node.record_bool("wakeup_test", *wakeup_test);
296 inspect_node.record_bool("mmcblk_stub", *mmcblk_stub);
297 inspect_node.record_bool("fake_ion", *fake_ion);
298 });
299 }
300 });
301 }
302}
303
304pub fn parse_features(
308 start_info: &ContainerStartInfo,
309 kernel_extra_features: &[String],
310) -> Result<Features, Error> {
311 let ContainerStructuredConfig {
312 crash_report_throttling,
313 enable_utc_time_adjustment,
314 extra_features,
315 cached_zx_map_info_bytes,
316 mlock_always_onfault,
317 mlock_pin_flavor,
318 selinux_exceptions,
319 ui_visual_debugging_level,
320 additional_mounts,
321 dirent_cache_size,
322 } = &start_info.config;
323
324 let mut features = Features::default();
325 for entry in start_info
326 .program
327 .features
328 .iter()
329 .chain(kernel_extra_features.iter())
330 .chain(extra_features.iter())
331 {
332 let (feature, raw_args) = Feature::try_parse_feature_and_args(entry)?;
333 match (feature, raw_args) {
334 (Feature::AndroidSerialno, _) => features.android_serialno = true,
335 (Feature::AndroidBootreason, _) => features.android_bootreason = true,
336 (Feature::AspectRatio, Some(args)) => {
337 let e = anyhow!("Invalid aspect_ratio: {:?}", args);
338 let components: Vec<_> = args.split(':').collect();
339 if components.len() != 2 {
340 return Err(e);
341 }
342 let width: u32 =
343 components[0].parse().map_err(|_| anyhow!("Invalid aspect ratio width"))?;
344 let height: u32 =
345 components[1].parse().map_err(|_| anyhow!("Invalid aspect ratio height"))?;
346 features.aspect_ratio = Some(AspectRatio { width, height });
347 }
348 (Feature::AspectRatio, None) => {
349 return Err(anyhow!(
350 "Aspect ratio feature must contain the aspect ratio in the format: aspect_ratio:w:h"
351 ));
352 }
353 (Feature::Container, _) => features.container = true,
354 (Feature::CustomArtifacts, _) => features.custom_artifacts = true,
355 (Feature::Ashmem, _) => features.ashmem = true,
356 (Feature::BootNotifier, _) => features.boot_notifier = true,
357 (Feature::DataCollectionConsentSync, _) => features.data_collection_consent_sync = true,
358 (Feature::BootNotifierCpuBoost, Some(arg)) => {
359 let duration = zx::MonotonicDuration::from_seconds(arg.parse::<i64>()?);
360 features.boot_notifier_cpu_boost = Some(duration);
361 }
362 (Feature::BootNotifierCpuBoost, None) => {
363 return Err(anyhow!(
364 "boot_notifier_cpu_boost feature must have an argument (e.g. \"boot_notifier_cpu_boost:60\")"
365 ));
366 }
367 (Feature::Framebuffer, _) => features.framebuffer = true,
368 (Feature::Gralloc, _) => features.gralloc = true,
369 (Feature::Kgsl, _) => features.kgsl = true,
370 (Feature::Magma, _) => {
371 if features.magma_supported_vendors.is_none() {
372 const VENDOR_ARM: u16 = 0x13B5;
373 const VENDOR_INTEL: u16 = 0x8086;
374 features.magma_supported_vendors = Some(vec![VENDOR_ARM, VENDOR_INTEL])
375 }
376 }
377 (Feature::MagmaSupportedVendors, Some(arg)) => {
378 features.magma_supported_vendors = Some(
379 arg.split(',')
380 .map(|s| {
381 let err = anyhow!(
382 "Feature format must be: magma_supported_vendors:0x1234[,0xabcd]"
383 );
384 let trimmed = s.trim_start_matches("0x");
385 u16::from_str_radix(trimmed, 16).map_err(|_| err)
386 })
387 .collect::<Result<Vec<u16>, Error>>()?,
388 );
389 }
390 (Feature::MagmaSupportedVendors, None) => {
391 return Err(anyhow!(
392 "Feature format must be: magma_supported_vendors:0x1234[,0xabcd]"
393 ));
394 }
395 (Feature::Nanohub, _) => features.nanohub = true,
396 (Feature::Fastrpc, _) => features.fastrpc = true,
397 (Feature::NetworkManager, _) => features.network_manager = true,
398 (Feature::Gfxstream, _) => features.gfxstream = true,
399 (Feature::Bpf, Some(version)) => features.kernel.bpf_v2 = version == "v2",
400 (Feature::Bpf, None) => {
401 return Err(anyhow!("bpf feature must have an argument (e.g. \"bpf:v2\")"));
402 }
403 (Feature::EnableSuid, _) => features.kernel.enable_suid = true,
404 (Feature::IoUring, _) => features.kernel.io_uring = true,
405 (Feature::ErrorOnFailedReboot, _) => features.kernel.error_on_failed_reboot = true,
406 (Feature::Perfetto, Some(socket_path)) => {
407 features.perfetto = Some(socket_path.into());
408 }
409 (Feature::Perfetto, None) => {
410 return Err(anyhow!("Perfetto feature must contain a socket path"));
411 }
412 (Feature::PingGroupRange, Some(arg)) => {
413 let mut args = arg.split(',');
414 let (min, max) = (|| {
415 let min = args.next()?.trim_ascii().parse::<u32>().ok()?;
416 let max = args.next()?.trim_ascii().parse::<u32>().ok()?.checked_add(1)?;
417 if args.next().is_some() {
418 return None;
419 }
420 Some((min, max))
421 })()
422 .ok_or_else(|| anyhow!("Feature format must be: ping_group_range:0,100"))?;
423 *features.system_limits.socket.icmp_ping_gids.lock() = min..max;
424 }
425 (Feature::PingGroupRange, None) => {
426 return Err(anyhow!("Feature format must be: ping_group_range:0,100"));
427 }
428 (Feature::RootfsRw, _) => features.rootfs_rw = true,
429 (Feature::Selinux, arg) => {
430 features.selinux = SELinuxFeature {
431 enabled: true,
432 options: arg.unwrap_or_default(),
433 exceptions: selinux_exceptions.clone(),
434 };
435 }
436 (Feature::SelinuxTestSuite, _) => features.kernel.selinux_test_suite = true,
437 (Feature::TestData, _) => features.test_data = true,
438 (Feature::Thermal, _) => features.thermal = true,
439 (Feature::Cooling, Some(arg)) => {
440 features.cooling = Some(arg.split(',').map(String::from).collect::<Vec<String>>())
441 }
442 (Feature::Cooling, None) => {
443 return Err(anyhow!("cooling feature must have an argument"));
444 }
445 (Feature::HvdcpOpti, _) => features.hvdcp_opti = true,
446 (Feature::Wifi, _) => features.kernel.wifi = true,
447 (Feature::AdditionalMounts, _) => {
448 features.additional_mounts = Some(additional_mounts.clone())
449 }
450 (Feature::WakeupTest, _) => features.wakeup_test = true,
451 (Feature::MmcblkStub, _) => features.mmcblk_stub = true,
452 (Feature::FakeIon, _) => features.kernel.fake_ion = true,
453 };
454 }
455
456 if features.boot_notifier_cpu_boost.is_some() && !features.boot_notifier {
457 return Err(anyhow!("boot_notifier_cpu_boost feature requires boot_notifier"));
458 }
459
460 if *ui_visual_debugging_level > 0 {
461 features.enable_visual_debugging = true;
462 }
463 features.enable_utc_time_adjustment = *enable_utc_time_adjustment;
464
465 features.kernel.default_uid = start_info.program.default_uid.0;
466 features.kernel.default_seclabel = start_info.program.default_seclabel.clone();
467 features.kernel.default_ns_mount_options =
468 if let Some(mount_options) = &start_info.program.default_ns_mount_options {
469 let options = mount_options
470 .iter()
471 .map(|item| {
472 let mut splitter = item.splitn(2, ":");
473 let key = splitter.next().expect("Failed to parse mount options");
474 let value = splitter.next().expect("Failed to parse mount options");
475 (key.to_string(), value.to_string())
476 })
477 .collect();
478 Some(options)
479 } else {
480 None
481 };
482
483 features.kernel.mlock_always_onfault = *mlock_always_onfault;
484 features.kernel.mlock_pin_flavor = MlockPinFlavor::parse(mlock_pin_flavor.as_str())?;
485 features.kernel.crash_report_throttling = *crash_report_throttling;
486 features.kernel.cached_zx_map_info_bytes = *cached_zx_map_info_bytes;
487 features.kernel.dirent_cache_size = *dirent_cache_size;
488
489 Ok(features)
490}
491
492pub fn run_container_features(
495 locked: &mut Locked<Unlocked>,
496 system_task: &CurrentTask,
497 features: &Features,
498) -> Result<(), Error> {
499 let kernel = system_task.kernel();
500
501 if features.framebuffer {
502 let framebuffer = Framebuffer::device_init(
503 locked,
504 system_task,
505 features.aspect_ratio,
506 features.enable_visual_debugging,
507 )
508 .context("initializing framebuffer")?;
509
510 let (touch_source_client, touch_source_server) = fidl::endpoints::create_endpoints();
511 let (mouse_source_client, mouse_source_server) = fidl::endpoints::create_endpoints();
512 let view_bound_protocols = fuicomposition::ViewBoundProtocols {
513 touch_source: Some(touch_source_server),
514 mouse_source: Some(mouse_source_server),
515 ..Default::default()
516 };
517 let view_identity = fuiviews::ViewIdentityOnCreation::from(
518 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair"),
519 );
520 let view_ref = fuchsia_scenic::duplicate_view_ref(&view_identity.view_ref)
521 .expect("Failed to dup view ref.");
522 let keyboard =
523 fuchsia_component::client::connect_to_protocol_sync::<fuiinput::KeyboardMarker>()
524 .expect("Failed to connect to keyboard");
525 let registry_proxy = fuchsia_component::client::connect_to_protocol_sync::<
526 fuipolicy::DeviceListenerRegistryMarker,
527 >()
528 .expect("Failed to connect to device listener registry");
529
530 *framebuffer.view_identity.lock() = Some(view_identity);
539 *framebuffer.view_bound_protocols.lock() = Some(view_bound_protocols);
540
541 let framebuffer_info = framebuffer.info.read();
542
543 let display_width = framebuffer_info.xres as i32;
544 let display_height = framebuffer_info.yres as i32;
545
546 let touch_device =
547 InputDevice::new_touch(display_width, display_height, &kernel.inspect_node);
548 let keyboard_device = InputDevice::new_keyboard(&kernel.inspect_node);
549 let mouse_device = InputDevice::new_mouse(&kernel.inspect_node);
550
551 touch_device.clone().register(
552 locked,
553 &kernel.kthreads.system_task(),
554 DEFAULT_TOUCH_DEVICE_ID,
555 )?;
556 keyboard_device.clone().register(
557 locked,
558 &kernel.kthreads.system_task(),
559 DEFAULT_KEYBOARD_DEVICE_ID,
560 )?;
561 mouse_device.clone().register(
562 locked,
563 &kernel.kthreads.system_task(),
564 DEFAULT_MOUSE_DEVICE_ID,
565 )?;
566
567 let (input_events_relay, input_events_relay_handle) = new_input_relay();
568 input_events_relay.start_relays(
569 &kernel,
570 EventProxyMode::WakeContainer,
571 touch_source_client,
572 keyboard,
573 mouse_source_client,
574 view_ref,
575 registry_proxy,
576 touch_device.open_files.clone(),
577 keyboard_device.open_files.clone(),
578 mouse_device.open_files.clone(),
579 Some(touch_device.inspect_status),
580 Some(keyboard_device.inspect_status),
581 Some(mouse_device.inspect_status),
582 );
583
584 register_uinput_device(locked, &kernel.kthreads.system_task(), input_events_relay_handle)?;
585
586 let (touch_standby_sender, touch_standby_receiver) = channel::<bool>();
588 let touch_policy_device = TouchPowerPolicyDevice::new(touch_standby_sender);
589 touch_policy_device.clone().register(locked, &kernel.kthreads.system_task());
590 touch_policy_device.start_relay(&kernel, touch_standby_receiver);
591
592 framebuffer.start_server(kernel, None);
593 }
594 if features.gralloc {
595 gralloc_device_init(locked, system_task);
605 }
606 if features.kgsl {
607 kgsl_device_init(locked, system_task);
608 }
609 if let Some(supported_vendors) = &features.magma_supported_vendors {
610 magma_device_init(locked, system_task, supported_vendors.clone());
611 }
612 if features.gfxstream {
613 gpu_device_init(locked, system_task);
614 }
615 if let Some(socket_path) = features.perfetto.clone() {
616 start_perfetto_consumer_thread(kernel, socket_path)
617 .context("Failed to start perfetto consumer thread")?;
618 }
619 if features.ashmem {
620 ashmem_device_init(locked, system_task);
621 }
622 if features.boot_notifier {
623 booted_device_init(locked, system_task, features.boot_notifier_cpu_boost);
624 }
625 if features.data_collection_consent_sync {
626 consent_sync_init(locked, system_task);
627 }
628 if features.network_manager {
629 if let Err(e) = nmfs_init(system_task) {
630 log_error!("Network manager initialization failed: ({e:?})");
631 }
632 }
633 if features.nanohub {
634 nanohub_device_init(locked, system_task);
635 }
636 if features.thermal {
637 thermal_device_init(locked, kernel)?;
638 }
639 if let Some(devices) = &features.cooling {
640 cooling_device_init(locked, kernel, devices.clone())?;
641 }
642 if features.hvdcp_opti {
643 hvdcp_opti_init(locked, system_task)?;
644 }
645 if features.fastrpc {
646 fastrpc_device_init(locked, system_task);
647 }
648 if features.wakeup_test {
649 register_wakeup_test_device(locked, system_task)?;
650 }
651 if features.mmcblk_stub {
652 let _device = add_mmc_block_device(locked, system_task)
653 .context("Failed to add stub mmcblk0 device")?;
654 }
655 Ok(())
656}
657
658pub fn run_component_features(
660 kernel: &Kernel,
661 entries: &Vec<String>,
662 mut incoming_dir: Option<fidl_fuchsia_io::DirectoryProxy>,
663) -> Result<(), Errno> {
664 for entry in entries {
665 match entry.as_str() {
666 "framebuffer" => {
667 Framebuffer::get(kernel)?.start_server(kernel, incoming_dir.take());
668 }
669 feature => {
670 return error!(ENOSYS, format!("Unsupported feature: {}", feature));
671 }
672 }
673 }
674 Ok(())
675}