Skip to main content

starnix_features/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use strum_macros::EnumString;
6use thiserror::Error;
7
8use std::fmt::Display;
9use std::str::FromStr;
10
11/// Features are a way to enable or disable specific starnix behaviors.
12///
13/// These are specified in the component manifest of the starnix container,
14/// or explicitly provided to the kernel constructor in tests.
15#[derive(Debug, Clone, Copy, PartialEq, EnumString, strum_macros::Display)]
16#[strum(serialize_all = "snake_case")]
17pub enum Feature {
18    // Legacy module feature flags retained for backwards compatibility with `ExtraFeatures`,
19    // Product Assembly, and prebuilt container manifests. Prefer configuring modules via
20    // `program.modules` in container manifests.
21    AndroidUsb,
22    AspectRatio,
23    Ashmem,
24    BootNotifier,
25    BootNotifierCpuBoost,
26    Cooling,
27    DataCollectionConsentSync,
28    Fastrpc,
29    Framebuffer,
30    Gfxstream,
31    // Note: GoogleOdpm statically registers `iio:device0`.
32    // Conflicts with `HvdcpOpti`, which also statically registers `iio:device0`.
33    // Until dynamic IIO numbering is supported, they must not be used at the same time.
34    GoogleOdpm,
35    Gralloc,
36    // Note: HvdcpOpti statically registers IIO devices (`iio:device0`, `iio:device3`).
37    // Conflicts with `GoogleOdpm`, which also statically registers `iio:device0`.
38    // Until dynamic IIO numbering is supported, they must not be used at the same time.
39    HvdcpOpti,
40    Kgsl,
41    Magma,
42    MagmaSupportedVendors,
43    MmcblkStub,
44    Nanohub,
45    NetworkManager,
46    Perfetto,
47    Thermal,
48    Usbfs,
49    WakeupTest,
50
51    // Container and kernel behavior features.
52    AdditionalMounts,
53    AndroidBootreason,
54    AndroidSerialno,
55    Bpf,
56    Container,
57    CustomArtifacts,
58    Devtmpfs,
59    EnableSuid,
60    ErrorOnFailedReboot,
61    // TODO(https://fxbug.dev/485370648) remove when unnecessary
62    FakeIon,
63    IoUring,
64    LogSysfsLookupMisses,
65    PingGroupRange,
66    RemotevolInProcess,
67    RootfsRw,
68    Selinux,
69    SelinuxTestSuite,
70    TestData,
71    UnifiedTracing,
72    Wifi,
73}
74
75/// Error returned when a feature is not recognized.
76#[derive(Debug, Error)]
77#[error("unsupported feature: {0}")]
78pub struct UnsupportedFeatureError(String);
79
80impl Feature {
81    /// Parses the name of a feature from a string.
82    pub fn try_parse(s: &str) -> Result<Feature, UnsupportedFeatureError> {
83        Feature::from_str(s).map_err(|_| UnsupportedFeatureError(s.to_string()))
84    }
85}
86
87/// Feature together with any arguments that go along with it, if specified.
88#[derive(Debug, Clone, PartialEq)]
89pub struct FeatureAndArgs {
90    /// The feature.
91    pub feature: Feature,
92    /// If specified, the (unparsed) arguments for the feature.
93    pub raw_args: Option<String>,
94}
95
96impl FeatureAndArgs {
97    /// Parses a feature and args from a string that separates them with `:`, e.g. "bpf:v2".
98    ///
99    /// If there is no `:` then the whole string is interpreted as the feature name.
100    pub fn try_parse(s: &str) -> Result<FeatureAndArgs, UnsupportedFeatureError> {
101        let (raw_flag, raw_args) =
102            s.split_once(':').map(|(f, a)| (f, Some(a.to_string()))).unwrap_or((s, None));
103        let feature = Feature::try_parse(raw_flag)?;
104        Ok(FeatureAndArgs { feature, raw_args })
105    }
106}
107
108impl Display for FeatureAndArgs {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
110        let FeatureAndArgs { feature, raw_args } = self;
111        match raw_args {
112            None => feature.fmt(f),
113            Some(raw_args) => format_args!("{feature}:{raw_args}").fmt(f),
114        }
115    }
116}
117
118#[cfg(test)]
119mod test {
120    use super::*;
121
122    #[test]
123    fn feature_serde() {
124        for (feature, expected_str) in [
125            (Feature::AndroidSerialno, "android_serialno"),
126            (Feature::AndroidBootreason, "android_bootreason"),
127            (Feature::AspectRatio, "aspect_ratio"),
128            (Feature::Container, "container"),
129            (Feature::CustomArtifacts, "custom_artifacts"),
130            (Feature::Devtmpfs, "devtmpfs"),
131            (Feature::Ashmem, "ashmem"),
132            (Feature::BootNotifier, "boot_notifier"),
133            (Feature::BootNotifierCpuBoost, "boot_notifier_cpu_boost"),
134            (Feature::Framebuffer, "framebuffer"),
135            (Feature::Gralloc, "gralloc"),
136            (Feature::Kgsl, "kgsl"),
137            (Feature::Magma, "magma"),
138            (Feature::MagmaSupportedVendors, "magma_supported_vendors"),
139            (Feature::Nanohub, "nanohub"),
140            (Feature::NetworkManager, "network_manager"),
141            (Feature::Gfxstream, "gfxstream"),
142            (Feature::Bpf, "bpf"),
143            (Feature::EnableSuid, "enable_suid"),
144            (Feature::IoUring, "io_uring"),
145            (Feature::ErrorOnFailedReboot, "error_on_failed_reboot"),
146            (Feature::Perfetto, "perfetto"),
147            (Feature::PingGroupRange, "ping_group_range"),
148            (Feature::RootfsRw, "rootfs_rw"),
149            (Feature::Selinux, "selinux"),
150            (Feature::SelinuxTestSuite, "selinux_test_suite"),
151            (Feature::TestData, "test_data"),
152            (Feature::Thermal, "thermal"),
153            (Feature::Cooling, "cooling"),
154            (Feature::DataCollectionConsentSync, "data_collection_consent_sync"),
155            (Feature::HvdcpOpti, "hvdcp_opti"),
156            (Feature::Wifi, "wifi"),
157            (Feature::AdditionalMounts, "additional_mounts"),
158            (Feature::WakeupTest, "wakeup_test"),
159            (Feature::MmcblkStub, "mmcblk_stub"),
160            (Feature::AndroidUsb, "android_usb"),
161            (Feature::GoogleOdpm, "google_odpm"),
162            (Feature::Usbfs, "usbfs"),
163            // TODO(https://fxbug.dev/485370648) remove when unnecessary
164            (Feature::FakeIon, "fake_ion"),
165            (Feature::UnifiedTracing, "unified_tracing"),
166            (Feature::RemotevolInProcess, "remotevol_in_process"),
167            (Feature::LogSysfsLookupMisses, "log_sysfs_lookup_misses"),
168        ] {
169            let string = feature.to_string();
170            assert_eq!(string.as_str(), expected_str);
171            assert_eq!(Feature::try_parse(&string).expect("should parse"), feature);
172        }
173    }
174
175    #[test]
176    fn deserialize_feature_and_args() {
177        let FeatureAndArgs { feature, raw_args } =
178            FeatureAndArgs::try_parse("bpf:v2").expect("should parse successfully");
179        assert_eq!(feature, Feature::Bpf);
180        assert_eq!(raw_args.as_ref().expect("should be populated"), "v2");
181    }
182}