blob: 863df6b7a0b4344a04f6311a66cf928312b7bf9c [file] [log] [blame]
Carlos Martinez Romero682a45b2024-02-26 10:58:58 -08001// 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
16use android_gui::{aidl::android::gui::ISurfaceComposer::ISurfaceComposer, binder};
17use clap::{Parser, Subcommand};
18use std::fmt::Debug;
19
20const SERVICE_IDENTIFIER: &str = "SurfaceFlingerAIDL";
21
22fn print_result<T, E>(function_name: &str, res: Result<T, E>)
23where
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
32fn 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)]
50struct Cli {
51 #[command(subcommand)]
52 command: Option<Commands>,
53}
54
55#[derive(Subcommand, Debug)]
56enum 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.
84fn 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}