1 // Copyright (C) 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 //! sfdo: Make surface flinger do things
16 use android_gui::{aidl::android::gui::ISurfaceComposer::ISurfaceComposer, binder};
17 use clap::{Parser, Subcommand};
18 use std::fmt::Debug;
19
20 const SERVICE_IDENTIFIER: &str = "SurfaceFlingerAIDL";
21
print_result<T, E>(function_name: &str, res: Result<T, E>) where E: Debug,22 fn print_result<T, E>(function_name: &str, res: Result<T, E>)
23 where
24 E: Debug,
25 {
26 match res {
27 Ok(_) => println!("{}: Operation successful!", function_name),
28 Err(err) => println!("{}: Operation failed: {:?}", function_name, err),
29 }
30 }
31
parse_toggle(toggle_value: &str) -> Option<bool>32 fn parse_toggle(toggle_value: &str) -> Option<bool> {
33 let positive = ["1", "true", "y", "yes", "on", "enabled", "show"];
34 let negative = ["0", "false", "n", "no", "off", "disabled", "hide"];
35
36 let word = toggle_value.to_lowercase(); // Case-insensitive comparison
37
38 if positive.contains(&word.as_str()) {
39 Some(true)
40 } else if negative.contains(&word.as_str()) {
41 Some(false)
42 } else {
43 None
44 }
45 }
46
47 #[derive(Parser)]
48 #[command(version = "0.1", about = "Execute SurfaceFlinger internal commands.")]
49 #[command(propagate_version = true)]
50 struct Cli {
51 #[command(subcommand)]
52 command: Option<Commands>,
53 }
54
55 #[derive(Subcommand, Debug)]
56 enum Commands {
57 #[command(about = "[optional(--delay)] Perform a debug flash.")]
58 DebugFlash {
59 #[arg(short, long, default_value_t = 0)]
60 delay: i32,
61 },
62
63 #[command(
64 about = "state = [enabled | disabled] When enabled, it disables Hardware Overlays, \
65 and routes all window composition to the GPU. This can help check if \
66 there is a bug in HW Composer."
67 )]
68 ForceClientComposition { state: Option<String> },
69
70 #[command(about = "state = [hide | show], displays the framerate in the top left corner.")]
71 FrameRateIndicator { state: Option<String> },
72
73 #[command(about = "Force composite ahead of next VSYNC.")]
74 ScheduleComposite,
75
76 #[command(about = "Force commit ahead of next VSYNC.")]
77 ScheduleCommit,
78 }
79
80 /// sfdo command line tool
81 ///
82 /// sfdo allows you to call different functions from the SurfaceComposer using
83 /// the adb shell.
main()84 fn main() {
85 binder::ProcessState::start_thread_pool();
86 let composer_service = match binder::get_interface::<dyn ISurfaceComposer>(SERVICE_IDENTIFIER) {
87 Ok(service) => service,
88 Err(err) => {
89 eprintln!("Unable to connect to ISurfaceComposer: {}", err);
90 return;
91 }
92 };
93
94 let cli = Cli::parse();
95
96 match &cli.command {
97 Some(Commands::FrameRateIndicator { state }) => {
98 if let Some(op_state) = state {
99 let toggle = parse_toggle(op_state);
100 match toggle {
101 Some(true) => {
102 let res = composer_service.enableRefreshRateOverlay(true);
103 print_result("enableRefreshRateOverlay", res);
104 }
105 Some(false) => {
106 let res = composer_service.enableRefreshRateOverlay(false);
107 print_result("enableRefreshRateOverlay", res);
108 }
109 None => {
110 eprintln!("Invalid state: {}, choices are [hide | show]", op_state);
111 }
112 }
113 } else {
114 eprintln!("No state, choices are [hide | show]");
115 }
116 }
117 Some(Commands::DebugFlash { delay }) => {
118 let res = composer_service.setDebugFlash(*delay);
119 print_result("setDebugFlash", res);
120 }
121 Some(Commands::ScheduleComposite) => {
122 let res = composer_service.scheduleComposite();
123 print_result("scheduleComposite", res);
124 }
125 Some(Commands::ScheduleCommit) => {
126 let res = composer_service.scheduleCommit();
127 print_result("scheduleCommit", res);
128 }
129 Some(Commands::ForceClientComposition { state }) => {
130 if let Some(op_state) = state {
131 let toggle = parse_toggle(op_state);
132 match toggle {
133 Some(true) => {
134 let res = composer_service.forceClientComposition(true);
135 print_result("forceClientComposition", res);
136 }
137 Some(false) => {
138 let res = composer_service.forceClientComposition(false);
139 print_result("forceClientComposition", res);
140 }
141 None => {
142 eprintln!("Invalid state: {}, choices are [enabled | disabled]", op_state);
143 }
144 }
145 } else {
146 eprintln!("No state, choices are [enabled | disabled]");
147 }
148 }
149 None => {
150 println!("Execute SurfaceFlinger internal commands.");
151 println!("run `adb shell sfdo help` for more to view the commands.");
152 println!("run `adb shell sfdo [COMMAND] --help` for more info on the command.");
153 }
154 }
155 }
156