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