• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! `aflags` is a device binary to read and write aconfig flags.
18 
19 use std::env;
20 use std::process::{Command as OsCommand, Stdio};
21 
22 use anyhow::{anyhow, ensure, Result};
23 use clap::Parser;
24 
25 mod device_config_source;
26 use device_config_source::DeviceConfigSource;
27 
28 mod aconfig_storage_source;
29 use aconfig_storage_source::AconfigStorageSource;
30 
31 mod load_protos;
32 
33 #[derive(Clone, PartialEq, Debug)]
34 enum FlagPermission {
35     ReadOnly,
36     ReadWrite,
37 }
38 
39 impl std::fmt::Display for FlagPermission {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result40     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41         write!(
42             f,
43             "{}",
44             match &self {
45                 Self::ReadOnly => "read-only",
46                 Self::ReadWrite => "read-write",
47             }
48         )
49     }
50 }
51 
52 #[derive(Clone, Debug)]
53 enum ValuePickedFrom {
54     Default,
55     Server,
56     Local,
57 }
58 
59 impl std::fmt::Display for ValuePickedFrom {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result60     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61         write!(
62             f,
63             "{}",
64             match &self {
65                 Self::Default => "default",
66                 Self::Server => "server",
67                 Self::Local => "local",
68             }
69         )
70     }
71 }
72 
73 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
74 enum FlagValue {
75     Enabled,
76     Disabled,
77 }
78 
79 impl TryFrom<&str> for FlagValue {
80     type Error = anyhow::Error;
81 
try_from(value: &str) -> std::result::Result<Self, Self::Error>82     fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
83         match value {
84             "true" | "enabled" => Ok(Self::Enabled),
85             "false" | "disabled" => Ok(Self::Disabled),
86             _ => Err(anyhow!("cannot convert string '{}' to FlagValue", value)),
87         }
88     }
89 }
90 
91 impl std::fmt::Display for FlagValue {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result92     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93         write!(
94             f,
95             "{}",
96             match &self {
97                 Self::Enabled => "enabled",
98                 Self::Disabled => "disabled",
99             }
100         )
101     }
102 }
103 
104 #[derive(Clone, Debug)]
105 struct Flag {
106     namespace: String,
107     name: String,
108     package: String,
109     container: String,
110     value: FlagValue,
111     staged_value: Option<FlagValue>,
112     permission: FlagPermission,
113     value_picked_from: ValuePickedFrom,
114 }
115 
116 impl Flag {
qualified_name(&self) -> String117     fn qualified_name(&self) -> String {
118         format!("{}.{}", self.package, self.name)
119     }
120 
display_staged_value(&self) -> String121     fn display_staged_value(&self) -> String {
122         match (&self.permission, self.staged_value) {
123             (FlagPermission::ReadOnly, _) => "-".to_string(),
124             (FlagPermission::ReadWrite, None) => "-".to_string(),
125             (FlagPermission::ReadWrite, Some(v)) => format!("(->{})", v),
126         }
127     }
128 }
129 
130 trait FlagSource {
list_flags() -> Result<Vec<Flag>>131     fn list_flags() -> Result<Vec<Flag>>;
override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>132     fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
133 }
134 
135 enum FlagSourceType {
136     DeviceConfig,
137     AconfigStorage,
138 }
139 
140 const ABOUT_TEXT: &str = "Tool for reading and writing flags.
141 
142 Rows in the table from the `list` command follow this format:
143 
144   package flag_name value provenance permission container
145 
146   * `package`: package set for this flag in its .aconfig definition.
147   * `flag_name`: flag name, also set in definition.
148   * `value`: the value read from the flag.
149   * `staged_value`: the value on next boot:
150     + `-`: same as current value
151     + `(->enabled) flipped to enabled on boot.
152     + `(->disabled) flipped to disabled on boot.
153   * `provenance`: one of:
154     + `default`: the flag value comes from its build-time default.
155     + `server`: the flag value comes from a server override.
156   * `permission`: read-write or read-only.
157   * `container`: the container for the flag, configured in its definition.
158 ";
159 
160 #[derive(Parser, Debug)]
161 #[clap(long_about=ABOUT_TEXT)]
162 struct Cli {
163     #[clap(subcommand)]
164     command: Command,
165 }
166 
167 #[derive(Parser, Debug)]
168 enum Command {
169     /// List all aconfig flags on this device.
170     List {
171         /// Optionally filter by container name.
172         #[clap(short = 'c', long = "container")]
173         container: Option<String>,
174     },
175 
176     /// Enable an aconfig flag on this device, on the next boot.
177     Enable {
178         /// <package>.<flag_name>
179         qualified_name: String,
180     },
181 
182     /// Disable an aconfig flag on this device, on the next boot.
183     Disable {
184         /// <package>.<flag_name>
185         qualified_name: String,
186     },
187 
188     /// Display which flag storage backs aconfig flags.
189     WhichBacking,
190 }
191 
192 struct PaddingInfo {
193     longest_flag_col: usize,
194     longest_val_col: usize,
195     longest_staged_val_col: usize,
196     longest_value_picked_from_col: usize,
197     longest_permission_col: usize,
198 }
199 
200 struct Filter {
201     container: Option<String>,
202 }
203 
204 impl Filter {
apply(&self, flags: &[Flag]) -> Vec<Flag>205     fn apply(&self, flags: &[Flag]) -> Vec<Flag> {
206         flags
207             .iter()
208             .filter(|flag| match &self.container {
209                 Some(c) => flag.container == *c,
210                 None => true,
211             })
212             .cloned()
213             .collect()
214     }
215 }
216 
format_flag_row(flag: &Flag, info: &PaddingInfo) -> String217 fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
218     let full_name = flag.qualified_name();
219     let p0 = info.longest_flag_col + 1;
220 
221     let val = flag.value.to_string();
222     let p1 = info.longest_val_col + 1;
223 
224     let staged_val = flag.display_staged_value();
225     let p2 = info.longest_staged_val_col + 1;
226 
227     let value_picked_from = flag.value_picked_from.to_string();
228     let p3 = info.longest_value_picked_from_col + 1;
229 
230     let perm = flag.permission.to_string();
231     let p4 = info.longest_permission_col + 1;
232 
233     let container = &flag.container;
234 
235     format!(
236         "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n"
237     )
238 }
239 
set_flag(qualified_name: &str, value: &str) -> Result<()>240 fn set_flag(qualified_name: &str, value: &str) -> Result<()> {
241     let flags_binding = DeviceConfigSource::list_flags()?;
242     let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or(
243         anyhow!("no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?"),
244     )?;
245 
246     ensure!(flag.permission == FlagPermission::ReadWrite,
247             format!("could not write flag '{qualified_name}', it is read-only for the current release configuration."));
248 
249     DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;
250 
251     Ok(())
252 }
253 
list(source_type: FlagSourceType, container: Option<String>) -> Result<String>254 fn list(source_type: FlagSourceType, container: Option<String>) -> Result<String> {
255     let flags_unfiltered = match source_type {
256         FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,
257         FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,
258     };
259 
260     if let Some(ref c) = container {
261         ensure!(
262             load_protos::list_containers()?.contains(c),
263             format!("container '{}' not found", &c)
264         );
265     }
266 
267     let flags = (Filter { container }).apply(&flags_unfiltered);
268     let padding_info = PaddingInfo {
269         longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),
270         longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
271         longest_staged_val_col: flags
272             .iter()
273             .map(|f| f.display_staged_value().len())
274             .max()
275             .unwrap_or(0),
276         longest_value_picked_from_col: flags
277             .iter()
278             .map(|f| f.value_picked_from.to_string().len())
279             .max()
280             .unwrap_or(0),
281         longest_permission_col: flags
282             .iter()
283             .map(|f| f.permission.to_string().len())
284             .max()
285             .unwrap_or(0),
286     };
287 
288     let mut result = String::from("");
289     for flag in flags {
290         let row = format_flag_row(&flag, &padding_info);
291         result.push_str(&row);
292     }
293     Ok(result)
294 }
295 
display_which_backing() -> String296 fn display_which_backing() -> String {
297     if aconfig_flags::auto_generated::enable_only_new_storage() {
298         "aconfig_storage".to_string()
299     } else {
300         "device_config".to_string()
301     }
302 }
303 
invoke_updatable_aflags()304 fn invoke_updatable_aflags() {
305     let updatable_command = "/apex/com.android.configinfrastructure/bin/aflags_updatable";
306 
307     let args: Vec<String> = env::args().collect();
308     let command_args = if args.len() >= 2 { &args[1..] } else { &["--help".to_string()] };
309 
310     let mut child = OsCommand::new(updatable_command);
311     for arg in command_args {
312         child.arg(arg);
313     }
314 
315     let output = child
316         .stdin(Stdio::piped())
317         .stdout(Stdio::piped())
318         .spawn()
319         .expect("failed to execute child")
320         .wait_with_output()
321         .expect("failed to execute command");
322 
323     let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
324     if !output_str.is_empty() {
325         println!("{}", output_str);
326     }
327 }
328 
main() -> Result<()>329 fn main() -> Result<()> {
330     if aconfig_flags::auto_generated::invoke_updatable_aflags() {
331         invoke_updatable_aflags();
332         return Ok(());
333     }
334 
335     ensure!(nix::unistd::Uid::current().is_root(), "must be root");
336 
337     let cli = Cli::parse();
338     let output = match cli.command {
339         Command::List { container } => {
340             if aconfig_flags::auto_generated::enable_only_new_storage() {
341                 list(FlagSourceType::AconfigStorage, container)
342                     .map_err(|err| anyhow!("could not list flags: {err}"))
343                     .map(Some)
344             } else {
345                 list(FlagSourceType::DeviceConfig, container).map(Some)
346             }
347         }
348         Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
349         Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
350         Command::WhichBacking => Ok(Some(display_which_backing())),
351     };
352     match output {
353         Ok(Some(text)) => println!("{text}"),
354         Ok(None) => (),
355         Err(message) => println!("Error: {message}"),
356     }
357 
358     Ok(())
359 }
360 
361 #[cfg(test)]
362 mod tests {
363     use super::*;
364 
365     #[test]
test_filter_container()366     fn test_filter_container() {
367         let flags = vec![
368             Flag {
369                 namespace: "namespace".to_string(),
370                 name: "test1".to_string(),
371                 package: "package".to_string(),
372                 value: FlagValue::Disabled,
373                 staged_value: None,
374                 permission: FlagPermission::ReadWrite,
375                 value_picked_from: ValuePickedFrom::Default,
376                 container: "system".to_string(),
377             },
378             Flag {
379                 namespace: "namespace".to_string(),
380                 name: "test2".to_string(),
381                 package: "package".to_string(),
382                 value: FlagValue::Disabled,
383                 staged_value: None,
384                 permission: FlagPermission::ReadWrite,
385                 value_picked_from: ValuePickedFrom::Default,
386                 container: "not_system".to_string(),
387             },
388             Flag {
389                 namespace: "namespace".to_string(),
390                 name: "test3".to_string(),
391                 package: "package".to_string(),
392                 value: FlagValue::Disabled,
393                 staged_value: None,
394                 permission: FlagPermission::ReadWrite,
395                 value_picked_from: ValuePickedFrom::Default,
396                 container: "system".to_string(),
397             },
398         ];
399 
400         assert_eq!((Filter { container: Some("system".to_string()) }).apply(&flags).len(), 2);
401     }
402 
403     #[test]
test_filter_no_container()404     fn test_filter_no_container() {
405         let flags = vec![
406             Flag {
407                 namespace: "namespace".to_string(),
408                 name: "test1".to_string(),
409                 package: "package".to_string(),
410                 value: FlagValue::Disabled,
411                 staged_value: None,
412                 permission: FlagPermission::ReadWrite,
413                 value_picked_from: ValuePickedFrom::Default,
414                 container: "system".to_string(),
415             },
416             Flag {
417                 namespace: "namespace".to_string(),
418                 name: "test2".to_string(),
419                 package: "package".to_string(),
420                 value: FlagValue::Disabled,
421                 staged_value: None,
422                 permission: FlagPermission::ReadWrite,
423                 value_picked_from: ValuePickedFrom::Default,
424                 container: "not_system".to_string(),
425             },
426             Flag {
427                 namespace: "namespace".to_string(),
428                 name: "test3".to_string(),
429                 package: "package".to_string(),
430                 value: FlagValue::Disabled,
431                 staged_value: None,
432                 permission: FlagPermission::ReadWrite,
433                 value_picked_from: ValuePickedFrom::Default,
434                 container: "system".to_string(),
435             },
436         ];
437 
438         assert_eq!((Filter { container: None }).apply(&flags).len(), 3);
439     }
440 }
441