1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This module defines system properties used for mmd.
16 //!
17 //! System properties "mmd.<flag_name>" are defined per OEM.
18 //!
19 //! Server configrations "persist.device_config.mmd_native.<flag_name>" overrides the corresponding
20 //! system properties for in-field experiment on a small population.
21 
22 use std::str::FromStr;
23 use std::time::Duration;
24 
25 use flags_rust::GetServerConfigurableFlag;
26 use log::error;
27 use rustutils::system_properties;
28 
29 const SERVER_CONFIG_NAMESPACE: &str = "mmd_native";
30 
generate_property_name(flag_name: &str) -> String31 fn generate_property_name(flag_name: &str) -> String {
32     format!("mmd.{flag_name}")
33 }
34 
35 /// bool system properties for mmd.
36 ///
37 /// clippy::enum_variant_names is allowed because we may add more properties.
38 #[allow(clippy::enum_variant_names)]
39 pub enum BoolProp {
40     ZramEnabled,
41     ZramWritebackEnabled,
42     ZramWritebackHugeIdleEnabled,
43     ZramWritebackIdleEnabled,
44     ZramWritebackHugeEnabled,
45     ZramRecompressionEnabled,
46     ZramRecompressionHugeIdleEnabled,
47     ZramRecompressionIdleEnabled,
48     ZramRecompressionHugeEnabled,
49 }
50 
51 impl BoolProp {
flag_name(&self) -> &'static str52     fn flag_name(&self) -> &'static str {
53         match self {
54             Self::ZramEnabled => "zram.enabled",
55             Self::ZramWritebackEnabled => "zram.writeback.enabled",
56             Self::ZramWritebackHugeIdleEnabled => "zram.writeback.huge_idle.enabled",
57             Self::ZramWritebackIdleEnabled => "zram.writeback.idle.enabled",
58             Self::ZramWritebackHugeEnabled => "zram.writeback.huge.enabled",
59             Self::ZramRecompressionEnabled => "zram.recompression.enabled",
60             Self::ZramRecompressionHugeIdleEnabled => "zram.recompression.huge_idle.enabled",
61             Self::ZramRecompressionIdleEnabled => "zram.recompression.idle.enabled",
62             Self::ZramRecompressionHugeEnabled => "zram.recompression.huge.enabled",
63         }
64     }
65 
get(&self, default: bool) -> bool66     pub fn get(&self, default: bool) -> bool {
67         if let Some(v) = read(self.flag_name()) {
68             v
69         } else {
70             default
71         }
72     }
73 }
74 
75 /// u64 system properties for mmd.
76 ///
77 /// clippy::enum_variant_names is allowed because we may add more properties.
78 #[allow(clippy::enum_variant_names)]
79 pub enum U64Prop {
80     ZramWritebackMinBytes,
81     ZramWritebackMaxBytes,
82     ZramWritebackMaxBytesPerDay,
83     ZramRecompressionThresholdMib,
84 }
85 
86 impl U64Prop {
flag_name(&self) -> &'static str87     fn flag_name(&self) -> &'static str {
88         match self {
89             Self::ZramWritebackMinBytes => "zram.writeback.min_bytes",
90             Self::ZramWritebackMaxBytes => "zram.writeback.max_bytes",
91             Self::ZramWritebackMaxBytesPerDay => "zram.writeback.max_bytes_per_day",
92             Self::ZramRecompressionThresholdMib => "zram.recompression.threshold_mib",
93         }
94     }
95 
get(&self, default: u64) -> u6496     pub fn get(&self, default: u64) -> u64 {
97         if let Some(v) = read(self.flag_name()) {
98             v
99         } else {
100             default
101         }
102     }
103 }
104 
105 /// Duration system properties for mmd in seconds.
106 ///
107 /// clippy::enum_variant_names is allowed because we may add more properties.
108 #[allow(clippy::enum_variant_names)]
109 pub enum SecondsProp {
110     ZramWritebackBackoff,
111     ZramWritebackMinIdle,
112     ZramWritebackMaxIdle,
113     ZramRecompressionBackoff,
114     ZramRecompressionMinIdle,
115     ZramRecompressionMaxIdle,
116 }
117 
118 impl SecondsProp {
flag_name(&self) -> &'static str119     fn flag_name(&self) -> &'static str {
120         match self {
121             Self::ZramWritebackBackoff => "zram.writeback.backoff_seconds",
122             Self::ZramWritebackMinIdle => "zram.writeback.min_idle_seconds",
123             Self::ZramWritebackMaxIdle => "zram.writeback.max_idle_seconds",
124             Self::ZramRecompressionBackoff => "zram.recompression.backoff_seconds",
125             Self::ZramRecompressionMinIdle => "zram.recompression.min_idle_seconds",
126             Self::ZramRecompressionMaxIdle => "zram.recompression.max_idle_seconds",
127         }
128     }
129 
get(&self, default: Duration) -> Duration130     pub fn get(&self, default: Duration) -> Duration {
131         if let Some(v) = read::<u64>(self.flag_name()) {
132             Duration::from_secs(v)
133         } else {
134             default
135         }
136     }
137 }
138 
139 /// String system properties for mmd.
140 ///
141 /// clippy::enum_variant_names is allowed because we may add more properties.
142 #[allow(clippy::enum_variant_names)]
143 pub enum StringProp {
144     ZramSize,
145 }
146 
147 impl StringProp {
flag_name(&self) -> &'static str148     fn flag_name(&self) -> &'static str {
149         match self {
150             Self::ZramSize => "zram.size",
151         }
152     }
153 
get(&self, default: &str) -> String154     pub fn get(&self, default: &str) -> String {
155         read(self.flag_name()).unwrap_or_else(|| default.to_string())
156     }
157 }
158 
read<T: FromStr>(flag_name: &str) -> Option<T>159 fn read<T: FromStr>(flag_name: &str) -> Option<T> {
160     let value = GetServerConfigurableFlag(SERVER_CONFIG_NAMESPACE, flag_name, "");
161     if !value.is_empty() {
162         if let Ok(v) = value.parse() {
163             return Some(v);
164         }
165         error!("failed to parse server config flag: {flag_name}={value}");
166     }
167 
168     // fallback if server flag is not set or broken.
169     let property_name = generate_property_name(flag_name);
170     match system_properties::read(&property_name) {
171         Ok(Some(v)) => {
172             if let Ok(v) = v.parse() {
173                 return Some(v);
174             } else {
175                 error!("failed to parse system property: {property_name}={v}");
176             }
177         }
178         Ok(None) => {}
179         Err(e) => {
180             error!("failed to read system property: {property_name} {e:?}");
181         }
182     }
183 
184     None
185 }
186 
187 #[cfg(test)]
188 mod tests {
189     use super::*;
190 
191     #[test]
bool_prop_from_default()192     fn bool_prop_from_default() {
193         // We can't test system properties directly. Just a unit test for
194         // default value.
195         assert!(BoolProp::ZramWritebackEnabled.get(true));
196         assert!(!BoolProp::ZramWritebackEnabled.get(false));
197     }
198 
199     #[test]
u64_prop_from_default()200     fn u64_prop_from_default() {
201         // We can't test system properties directly. Just a unit test for
202         // default value.
203         assert_eq!(U64Prop::ZramWritebackMinBytes.get(12345), 12345);
204     }
205 
206     #[test]
seconds_prop_from_default()207     fn seconds_prop_from_default() {
208         // We can't test system properties directly. Just a unit test for
209         // default value.
210         assert_eq!(
211             SecondsProp::ZramWritebackBackoff.get(Duration::from_secs(12345)),
212             Duration::from_secs(12345)
213         );
214     }
215 }
216