Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 682a45b6 authored by Carlos Martinez Romero's avatar Carlos Martinez Romero
Browse files

[sfdo] Migrate sfdo over to Rust.

Test: build and run on device.
Bug: 329450914
Change-Id: I6a1cbba7eacc74d960f9eb854928bac1ad66d20a
parent cf0a70f6
Loading
Loading
Loading
Loading
+6 −13
Original line number Diff line number Diff line
cc_binary {
rust_binary {
    name: "sfdo",
    srcs: ["sfdo.rs"],

    srcs: ["sfdo.cpp"],

    shared_libs: [
        "libutils",
        "libgui",
    ],

    cflags: [
        "-Wall",
        "-Werror",
        "-Wunused",
        "-Wunreachable-code",
    rustlibs: [
        "android.gui-rust",
        "libclap",
    ],
    edition: "2021",
}

cmds/sfdo/sfdo.cpp

deleted100644 → 0
+0 −166
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <inttypes.h>
#include <stdint.h>
#include <any>
#include <map>

#include <cutils/properties.h>
#include <sys/resource.h>
#include <utils/Log.h>

#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
#include <private/gui/ComposerServiceAIDL.h>

using namespace android;

std::map<std::string, std::any> g_functions;

enum class ParseToggleResult {
    kError,
    kFalse,
    kTrue,
};

const std::map<std::string, std::string> g_function_details = {
        {"debugFlash", "[optional(delay)] Perform a debug flash."},
        {"frameRateIndicator", "[hide | show] displays the framerate in the top left corner."},
        {"scheduleComposite", "Force composite ahead of next VSYNC."},
        {"scheduleCommit", "Force commit ahead of next VSYNC."},
        {"scheduleComposite", "PENDING - if you have a good understanding let me know!"},
        {"forceClientComposition",
         "[enabled | disabled] When enabled, it disables "
         "Hardware Overlays, and routes all window composition to the GPU. This can "
         "help check if there is a bug in HW Composer."},
};

static void ShowUsage() {
    std::cout << "usage: sfdo [help, frameRateIndicator show, debugFlash enabled, ...]\n\n";
    for (const auto& sf : g_functions) {
        const std::string fn = sf.first;
        std::string fdetails = "TODO";
        if (g_function_details.find(fn) != g_function_details.end())
            fdetails = g_function_details.find(fn)->second;
        std::cout << "    " << fn << ": " << fdetails << "\n";
    }
}

// Returns 1 for positive keywords and 0 for negative keywords.
// If the string does not match any it will return -1.
ParseToggleResult parseToggle(const char* str) {
    const std::unordered_set<std::string> positive{"1",  "true",    "y",   "yes",
                                                   "on", "enabled", "show"};
    const std::unordered_set<std::string> negative{"0",   "false",    "n",   "no",
                                                   "off", "disabled", "hide"};

    const std::string word(str);
    if (positive.count(word)) {
        return ParseToggleResult::kTrue;
    }
    if (negative.count(word)) {
        return ParseToggleResult::kFalse;
    }

    return ParseToggleResult::kError;
}

int frameRateIndicator(int argc, char** argv) {
    bool hide = false, show = false;
    if (argc == 3) {
        show = strcmp(argv[2], "show") == 0;
        hide = strcmp(argv[2], "hide") == 0;
    }

    if (show || hide) {
        ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show);
    } else {
        std::cerr << "Incorrect usage of frameRateIndicator. Missing [hide | show].\n";
        return -1;
    }
    return 0;
}

int debugFlash(int argc, char** argv) {
    int delay = 0;
    if (argc == 3) {
        delay = atoi(argv[2]) == 0;
    }

    ComposerServiceAIDL::getComposerService()->setDebugFlash(delay);
    return 0;
}

int scheduleComposite([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
    ComposerServiceAIDL::getComposerService()->scheduleComposite();
    return 0;
}

int scheduleCommit([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
    ComposerServiceAIDL::getComposerService()->scheduleCommit();
    return 0;
}

int forceClientComposition(int argc, char** argv) {
    bool enabled = true;
    // A valid command looks like this:
    // adb shell sfdo forceClientComposition enabled
    if (argc >= 3) {
        const ParseToggleResult toggle = parseToggle(argv[2]);
        if (toggle == ParseToggleResult::kError) {
            std::cerr << "Incorrect usage of forceClientComposition. "
                         "Missing [enabled | disabled].\n";
            return -1;
        }
        if (argc > 3) {
            std::cerr << "Too many arguments after [enabled | disabled]. "
                         "Ignoring extra arguments.\n";
        }
        enabled = (toggle == ParseToggleResult::kTrue);
    } else {
        std::cerr << "Incorrect usage of forceClientComposition. Missing [enabled | disabled].\n";
        return -1;
    }

    ComposerServiceAIDL::getComposerService()->forceClientComposition(enabled);
    return 0;
}

int main(int argc, char** argv) {
    std::cout << "Execute SurfaceFlinger internal commands.\n";
    std::cout << "sfdo requires to be run with root permissions..\n";

    g_functions["frameRateIndicator"] = frameRateIndicator;
    g_functions["debugFlash"] = debugFlash;
    g_functions["scheduleComposite"] = scheduleComposite;
    g_functions["scheduleCommit"] = scheduleCommit;
    g_functions["forceClientComposition"] = forceClientComposition;

    if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) {
        std::cout << "Running: " << argv[1] << "\n";
        const std::string key(argv[1]);
        const auto fn = g_functions[key];
        int result = std::any_cast<int (*)(int, char**)>(fn)(argc, argv);
        if (result == 0) {
            std::cout << "Success.\n";
        }
        return result;
    } else {
        ShowUsage();
    }
    return 0;
}
 No newline at end of file

cmds/sfdo/sfdo.rs

0 → 100644
+155 −0
Original line number Diff line number Diff line
// Copyright (C) 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! sfdo: Make surface flinger do things
use android_gui::{aidl::android::gui::ISurfaceComposer::ISurfaceComposer, binder};
use clap::{Parser, Subcommand};
use std::fmt::Debug;

const SERVICE_IDENTIFIER: &str = "SurfaceFlingerAIDL";

fn print_result<T, E>(function_name: &str, res: Result<T, E>)
where
    E: Debug,
{
    match res {
        Ok(_) => println!("{}: Operation successful!", function_name),
        Err(err) => println!("{}: Operation failed: {:?}", function_name, err),
    }
}

fn parse_toggle(toggle_value: &str) -> Option<bool> {
    let positive = ["1", "true", "y", "yes", "on", "enabled", "show"];
    let negative = ["0", "false", "n", "no", "off", "disabled", "hide"];

    let word = toggle_value.to_lowercase(); // Case-insensitive comparison

    if positive.contains(&word.as_str()) {
        Some(true)
    } else if negative.contains(&word.as_str()) {
        Some(false)
    } else {
        None
    }
}

#[derive(Parser)]
#[command(version = "0.1", about = "Execute SurfaceFlinger internal commands.")]
#[command(propagate_version = true)]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommand, Debug)]
enum Commands {
    #[command(about = "[optional(--delay)] Perform a debug flash.")]
    DebugFlash {
        #[arg(short, long, default_value_t = 0)]
        delay: i32,
    },

    #[command(
        about = "state = [enabled | disabled] When enabled, it disables Hardware Overlays, \
                      and routes all window composition to the GPU. This can help check if \
                      there is a bug in HW Composer."
    )]
    ForceClientComposition { state: Option<String> },

    #[command(about = "state = [hide | show], displays the framerate in the top left corner.")]
    FrameRateIndicator { state: Option<String> },

    #[command(about = "Force composite ahead of next VSYNC.")]
    ScheduleComposite,

    #[command(about = "Force commit ahead of next VSYNC.")]
    ScheduleCommit,
}

/// sfdo command line tool
///
/// sfdo allows you to call different functions from the SurfaceComposer using
/// the adb shell.
fn main() {
    binder::ProcessState::start_thread_pool();
    let composer_service = match binder::get_interface::<dyn ISurfaceComposer>(SERVICE_IDENTIFIER) {
        Ok(service) => service,
        Err(err) => {
            eprintln!("Unable to connect to ISurfaceComposer: {}", err);
            return;
        }
    };

    let cli = Cli::parse();

    match &cli.command {
        Some(Commands::FrameRateIndicator { state }) => {
            if let Some(op_state) = state {
                let toggle = parse_toggle(op_state);
                match toggle {
                    Some(true) => {
                        let res = composer_service.enableRefreshRateOverlay(true);
                        print_result("enableRefreshRateOverlay", res);
                    }
                    Some(false) => {
                        let res = composer_service.enableRefreshRateOverlay(false);
                        print_result("enableRefreshRateOverlay", res);
                    }
                    None => {
                        eprintln!("Invalid state: {}, choices are [hide | show]", op_state);
                    }
                }
            } else {
                eprintln!("No state, choices are [hide | show]");
            }
        }
        Some(Commands::DebugFlash { delay }) => {
            let res = composer_service.setDebugFlash(*delay);
            print_result("setDebugFlash", res);
        }
        Some(Commands::ScheduleComposite) => {
            let res = composer_service.scheduleComposite();
            print_result("scheduleComposite", res);
        }
        Some(Commands::ScheduleCommit) => {
            let res = composer_service.scheduleCommit();
            print_result("scheduleCommit", res);
        }
        Some(Commands::ForceClientComposition { state }) => {
            if let Some(op_state) = state {
                let toggle = parse_toggle(op_state);
                match toggle {
                    Some(true) => {
                        let res = composer_service.forceClientComposition(true);
                        print_result("forceClientComposition", res);
                    }
                    Some(false) => {
                        let res = composer_service.forceClientComposition(false);
                        print_result("forceClientComposition", res);
                    }
                    None => {
                        eprintln!("Invalid state: {}, choices are [enabled | disabled]", op_state);
                    }
                }
            } else {
                eprintln!("No state, choices are [enabled | disabled]");
            }
        }
        None => {
            println!("Execute SurfaceFlinger internal commands.");
            println!("run `adb shell sfdo help` for more to view the commands.");
            println!("run `adb shell sfdo [COMMAND] --help` for more info on the command.");
        }
    }
}
+27 −4
Original line number Diff line number Diff line
@@ -155,9 +155,9 @@ cc_library_static {
    },
}

aidl_library {
    name: "libgui_aidl_hdrs",
    hdrs: [
filegroup {
    name: "libgui_extra_aidl_files",
    srcs: [
        "android/gui/DisplayInfo.aidl",
        "android/gui/FocusRequest.aidl",
        "android/gui/InputApplicationInfo.aidl",
@@ -170,11 +170,34 @@ aidl_library {
    ],
}

filegroup {
    name: "libgui_extra_unstructured_aidl_files",
    srcs: [
        "android/gui/DisplayInfo.aidl",
        "android/gui/InputApplicationInfo.aidl",
        "android/gui/WindowInfo.aidl",
        "android/gui/WindowInfosUpdate.aidl",
    ],
}

aidl_library {
    name: "libgui_aidl_hdrs",
    hdrs: [":libgui_extra_aidl_files"],
}

aidl_library {
    name: "libgui_extra_unstructured_aidl_hdrs",
    hdrs: [":libgui_extra_unstructured_aidl_files"],
}

aidl_library {
    name: "libgui_aidl",
    srcs: ["aidl/**/*.aidl"],
    strip_import_prefix: "aidl",
    deps: ["libgui_aidl_hdrs"],
    deps: [
        "libgui_aidl_hdrs",
        "libgui_extra_unstructured_aidl_hdrs",
    ],
}

filegroup {
+85 −0
Original line number Diff line number Diff line
// Copyright 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package {
    // See: http://go/android-license-faq
    // A large-scale-change added 'default_applicable_licenses' to import
    // all of the 'license_kinds' from "frameworks_native_license"
    // to get the below license kinds:
    //   SPDX-license-identifier-Apache-2.0
    default_applicable_licenses: ["frameworks_native_license"],
    default_team: "trendy_team_android_core_graphics_stack",
}

filegroup {
    name: "libgui_unstructured_aidl_files",
    srcs: [
        ":libgui_extra_unstructured_aidl_files",

        "android/gui/BitTube.aidl",
        "android/gui/CaptureArgs.aidl",
        "android/gui/DisplayCaptureArgs.aidl",
        "android/gui/LayerCaptureArgs.aidl",
        "android/gui/LayerMetadata.aidl",
        "android/gui/ParcelableVsyncEventData.aidl",
        "android/gui/ScreenCaptureResults.aidl",
    ],
}

aidl_library {
    name: "libgui_unstructured_aidl",
    hdrs: [":libgui_unstructured_aidl_files"],
}

filegroup {
    name: "libgui_interface_aidl_files",
    srcs: [
        ":libgui_extra_aidl_files",
        "**/*.aidl",
    ],
    exclude_srcs: [":libgui_unstructured_aidl_files"],
}

aidl_interface {
    name: "android.gui",
    unstable: true,
    srcs: [
        ":libgui_interface_aidl_files",
    ],
    include_dirs: [
        "frameworks/native/libs/gui",
        "frameworks/native/libs/gui/aidl",
    ],
    headers: [
        "libgui_aidl_hdrs",
        "libgui_extra_unstructured_aidl_hdrs",
    ],
    backend: {
        rust: {
            enabled: true,
            additional_rustlibs: [
                "libgui_aidl_types_rs",
            ],
        },
        java: {
            enabled: false,
        },
        cpp: {
            enabled: false,
        },
        ndk: {
            enabled: false,
        },
    },
}
Loading