blob: e4af2a78bd8fa1dc0e0cd828fc1481987ea4a720 [file] [log] [blame]
Ted Bauer4dbf58a2024-02-08 18:46:52 +00001/*
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
Ted Bauer3ad48d92025-01-06 22:01:07 +000019use std::env;
20use std::process::{Command as OsCommand, Stdio};
21
Ted Bauera98448f2024-03-06 14:01:19 -050022use anyhow::{anyhow, ensure, Result};
Ted Bauer4dbf58a2024-02-08 18:46:52 +000023use clap::Parser;
24
25mod device_config_source;
26use device_config_source::DeviceConfigSource;
27
Ted Bauer6d4db662024-03-06 18:08:32 -050028mod aconfig_storage_source;
29use aconfig_storage_source::AconfigStorageSource;
30
Ted Bauer206d44a2024-03-15 17:04:06 +000031mod load_protos;
32
Ted Bauer46d758b2024-03-12 19:28:58 +000033#[derive(Clone, PartialEq, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000034enum FlagPermission {
35 ReadOnly,
36 ReadWrite,
37}
38
Chris Wailese6bb2e92024-05-09 15:14:22 -070039impl std::fmt::Display for FlagPermission {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ted Baueraeb96092024-05-15 09:23:43 -040041 write!(
42 f,
43 "{}",
44 match &self {
45 Self::ReadOnly => "read-only",
46 Self::ReadWrite => "read-write",
47 }
48 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +000049 }
50}
51
Ted Bauer46d758b2024-03-12 19:28:58 +000052#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000053enum ValuePickedFrom {
54 Default,
55 Server,
Ted Bauer05fea282024-07-19 13:11:42 +000056 Local,
Ted Bauer4dbf58a2024-02-08 18:46:52 +000057}
58
Chris Wailese6bb2e92024-05-09 15:14:22 -070059impl std::fmt::Display for ValuePickedFrom {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ted Baueraeb96092024-05-15 09:23:43 -040061 write!(
62 f,
63 "{}",
64 match &self {
65 Self::Default => "default",
66 Self::Server => "server",
Ted Bauer05fea282024-07-19 13:11:42 +000067 Self::Local => "local",
Ted Baueraeb96092024-05-15 09:23:43 -040068 }
69 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +000070 }
71}
72
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010073#[derive(Clone, Copy, PartialEq, Eq, Debug)]
74enum FlagValue {
75 Enabled,
76 Disabled,
77}
78
79impl TryFrom<&str> for FlagValue {
80 type Error = anyhow::Error;
81
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
Chris Wailese6bb2e92024-05-09 15:14:22 -070091impl std::fmt::Display for FlagValue {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ted Baueraeb96092024-05-15 09:23:43 -040093 write!(
94 f,
95 "{}",
96 match &self {
97 Self::Enabled => "enabled",
98 Self::Disabled => "disabled",
99 }
100 )
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100101 }
102}
103
Ted Bauer46d758b2024-03-12 19:28:58 +0000104#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000105struct Flag {
106 namespace: String,
107 name: String,
108 package: String,
109 container: String,
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100110 value: FlagValue,
Ted Bauer46d758b2024-03-12 19:28:58 +0000111 staged_value: Option<FlagValue>,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000112 permission: FlagPermission,
113 value_picked_from: ValuePickedFrom,
114}
115
Ted Bauer84883bd2024-03-04 22:45:29 +0000116impl Flag {
117 fn qualified_name(&self) -> String {
118 format!("{}.{}", self.package, self.name)
119 }
Ted Bauer46d758b2024-03-12 19:28:58 +0000120
121 fn display_staged_value(&self) -> String {
Ted Bauer431f44a2024-09-13 20:59:34 +0000122 match (&self.permission, self.staged_value) {
123 (FlagPermission::ReadOnly, _) => "-".to_string(),
124 (FlagPermission::ReadWrite, None) => "-".to_string(),
125 (FlagPermission::ReadWrite, Some(v)) => format!("(->{})", v),
Ted Bauer46d758b2024-03-12 19:28:58 +0000126 }
127 }
Ted Bauer84883bd2024-03-04 22:45:29 +0000128}
129
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000130trait FlagSource {
131 fn list_flags() -> Result<Vec<Flag>>;
Ted Bauer84883bd2024-03-04 22:45:29 +0000132 fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000133}
134
Ted Bauer6d4db662024-03-06 18:08:32 -0500135enum FlagSourceType {
136 DeviceConfig,
137 AconfigStorage,
138}
139
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000140const ABOUT_TEXT: &str = "Tool for reading and writing flags.
141
142Rows 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.
Ted Bauer46d758b2024-03-12 19:28:58 +0000149 * `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.
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000153 * `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)]
162struct Cli {
163 #[clap(subcommand)]
164 command: Command,
165}
166
167#[derive(Parser, Debug)]
168enum Command {
169 /// List all aconfig flags on this device.
Ted Bauer6d4db662024-03-06 18:08:32 -0500170 List {
Ted Baueraeb96092024-05-15 09:23:43 -0400171 /// Optionally filter by container name.
172 #[clap(short = 'c', long = "container")]
173 container: Option<String>,
Ted Bauer6d4db662024-03-06 18:08:32 -0500174 },
Ted Bauer84883bd2024-03-04 22:45:29 +0000175
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 },
Ted Bauerb1edaae2024-09-11 19:07:44 +0000187
188 /// Display which flag storage backs aconfig flags.
189 WhichBacking,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000190}
191
192struct PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100193 longest_flag_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000194 longest_val_col: usize,
Ted Bauer46d758b2024-03-12 19:28:58 +0000195 longest_staged_val_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000196 longest_value_picked_from_col: usize,
197 longest_permission_col: usize,
198}
199
Ted Baueraeb96092024-05-15 09:23:43 -0400200struct Filter {
201 container: Option<String>,
202}
203
204impl Filter {
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
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000217fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100218 let full_name = flag.qualified_name();
219 let p0 = info.longest_flag_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000220
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100221 let val = flag.value.to_string();
Mårten Kongstadd408e962024-03-07 13:56:30 +0100222 let p1 = info.longest_val_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000223
Ted Bauer46d758b2024-03-12 19:28:58 +0000224 let staged_val = flag.display_staged_value();
225 let p2 = info.longest_staged_val_col + 1;
226
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000227 let value_picked_from = flag.value_picked_from.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000228 let p3 = info.longest_value_picked_from_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000229
230 let perm = flag.permission.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000231 let p4 = info.longest_permission_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000232
233 let container = &flag.container;
234
Ted Bauer46d758b2024-03-12 19:28:58 +0000235 format!(
236 "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n"
237 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000238}
239
Ted Bauer84883bd2024-03-04 22:45:29 +0000240fn 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
Ted Bauera98448f2024-03-06 14:01:19 -0500246 ensure!(flag.permission == FlagPermission::ReadWrite,
247 format!("could not write flag '{qualified_name}', it is read-only for the current release configuration."));
Ted Bauer84883bd2024-03-04 22:45:29 +0000248
249 DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;
250
251 Ok(())
252}
253
Ted Baueraeb96092024-05-15 09:23:43 -0400254fn list(source_type: FlagSourceType, container: Option<String>) -> Result<String> {
255 let flags_unfiltered = match source_type {
Ted Bauer6d4db662024-03-06 18:08:32 -0500256 FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,
257 FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,
258 };
Ted Bauercd826592024-10-25 20:29:39 +0000259
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
Ted Baueraeb96092024-05-15 09:23:43 -0400267 let flags = (Filter { container }).apply(&flags_unfiltered);
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000268 let padding_info = PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100269 longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100270 longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
Ted Bauer46d758b2024-03-12 19:28:58 +0000271 longest_staged_val_col: flags
272 .iter()
273 .map(|f| f.display_staged_value().len())
274 .max()
275 .unwrap_or(0),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000276 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
Ted Bauerb1edaae2024-09-11 19:07:44 +0000296fn 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
Ted Bauer3ad48d92025-01-06 22:01:07 +0000304fn 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 println!("{}", String::from_utf8_lossy(&output.stdout).trim());
324}
325
Ted Bauer5223a702024-06-17 14:48:55 +0000326fn main() -> Result<()> {
Ted Bauer3ad48d92025-01-06 22:01:07 +0000327 if aconfig_flags::auto_generated::invoke_updatable_aflags() {
328 invoke_updatable_aflags();
329 return Ok(());
330 }
331
Ted Bauer5223a702024-06-17 14:48:55 +0000332 ensure!(nix::unistd::Uid::current().is_root(), "must be root");
333
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000334 let cli = Cli::parse();
335 let output = match cli.command {
Ted Bauerb1edaae2024-09-11 19:07:44 +0000336 Command::List { container } => {
337 if aconfig_flags::auto_generated::enable_only_new_storage() {
338 list(FlagSourceType::AconfigStorage, container)
Ted Bauercd826592024-10-25 20:29:39 +0000339 .map_err(|err| anyhow!("could not list flags: {err}"))
Ted Bauerb1edaae2024-09-11 19:07:44 +0000340 .map(Some)
341 } else {
342 list(FlagSourceType::DeviceConfig, container).map(Some)
343 }
Ted Baueraeb96092024-05-15 09:23:43 -0400344 }
Ted Bauer84883bd2024-03-04 22:45:29 +0000345 Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
346 Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
Ted Bauerb1edaae2024-09-11 19:07:44 +0000347 Command::WhichBacking => Ok(Some(display_which_backing())),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000348 };
349 match output {
Ted Bauer84883bd2024-03-04 22:45:29 +0000350 Ok(Some(text)) => println!("{text}"),
351 Ok(None) => (),
352 Err(message) => println!("Error: {message}"),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000353 }
Ted Bauer5223a702024-06-17 14:48:55 +0000354
355 Ok(())
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000356}
Ted Baueraeb96092024-05-15 09:23:43 -0400357
358#[cfg(test)]
359mod tests {
360 use super::*;
361
362 #[test]
363 fn test_filter_container() {
364 let flags = vec![
365 Flag {
366 namespace: "namespace".to_string(),
367 name: "test1".to_string(),
368 package: "package".to_string(),
369 value: FlagValue::Disabled,
370 staged_value: None,
371 permission: FlagPermission::ReadWrite,
372 value_picked_from: ValuePickedFrom::Default,
373 container: "system".to_string(),
374 },
375 Flag {
376 namespace: "namespace".to_string(),
377 name: "test2".to_string(),
378 package: "package".to_string(),
379 value: FlagValue::Disabled,
380 staged_value: None,
381 permission: FlagPermission::ReadWrite,
382 value_picked_from: ValuePickedFrom::Default,
383 container: "not_system".to_string(),
384 },
385 Flag {
386 namespace: "namespace".to_string(),
387 name: "test3".to_string(),
388 package: "package".to_string(),
389 value: FlagValue::Disabled,
390 staged_value: None,
391 permission: FlagPermission::ReadWrite,
392 value_picked_from: ValuePickedFrom::Default,
393 container: "system".to_string(),
394 },
395 ];
396
397 assert_eq!((Filter { container: Some("system".to_string()) }).apply(&flags).len(), 2);
398 }
399
400 #[test]
401 fn test_filter_no_container() {
402 let flags = vec![
403 Flag {
404 namespace: "namespace".to_string(),
405 name: "test1".to_string(),
406 package: "package".to_string(),
407 value: FlagValue::Disabled,
408 staged_value: None,
409 permission: FlagPermission::ReadWrite,
410 value_picked_from: ValuePickedFrom::Default,
411 container: "system".to_string(),
412 },
413 Flag {
414 namespace: "namespace".to_string(),
415 name: "test2".to_string(),
416 package: "package".to_string(),
417 value: FlagValue::Disabled,
418 staged_value: None,
419 permission: FlagPermission::ReadWrite,
420 value_picked_from: ValuePickedFrom::Default,
421 container: "not_system".to_string(),
422 },
423 Flag {
424 namespace: "namespace".to_string(),
425 name: "test3".to_string(),
426 package: "package".to_string(),
427 value: FlagValue::Disabled,
428 staged_value: None,
429 permission: FlagPermission::ReadWrite,
430 value_picked_from: ValuePickedFrom::Default,
431 container: "system".to_string(),
432 },
433 ];
434
435 assert_eq!((Filter { container: None }).apply(&flags).len(), 3);
436 }
437}