Add a tool to make calls to SurfaceFlinger
The tool should make the calls to SurfaceFlinger more user friendly and
also document them.
Bug: 269188249
Test: Manually build and call the functions.
Ignore-AOSP-First: sfdo is developed on main as it depends on surfaceflinger which is developed on that branch
Change-Id: I7136c9d7e98175c320dab33369858861831289e6
diff --git a/cmds/sfdo/Android.bp b/cmds/sfdo/Android.bp
new file mode 100644
index 0000000..c19c9da
--- /dev/null
+++ b/cmds/sfdo/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+ name: "sfdo",
+
+ srcs: ["sfdo.cpp"],
+
+ shared_libs: [
+ "libutils",
+ "libgui",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/cmds/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp
new file mode 100644
index 0000000..55326ea
--- /dev/null
+++ b/cmds/sfdo/sfdo.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 <unordered_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::unordered_map<std::string, std::any> g_functions;
+
+const std::unordered_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!"},
+};
+
+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";
+ }
+}
+
+int FrameRateIndicator([[maybe_unused]] int argc, [[maybe_unused]] 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([[maybe_unused]] int argc, [[maybe_unused]] 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 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;
+
+ 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
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index e1726b7..c2f47fc 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -480,6 +480,30 @@
void setOverrideFrameRate(int uid, float frameRate);
/**
+ * Enables or disables the frame rate overlay in the top left corner.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void enableRefreshRateOverlay(boolean active);
+
+ /**
+ * Enables or disables the debug flash.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void setDebugFlash(int delay);
+
+ /**
+ * Force composite ahead of next VSYNC.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void scheduleComposite();
+
+ /**
+ * Force commit ahead of next VSYNC.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void scheduleCommit();
+
+ /**
* Gets priority of the RenderEngine in SurfaceFlinger.
*/
int getGpuContextPriority();
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index a381687..2643fa7 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -150,6 +150,10 @@
MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
(const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
+ MOCK_METHOD(binder::Status, enableRefreshRateOverlay, (bool), (override));
+ MOCK_METHOD(binder::Status, setDebugFlash, (int), (override));
+ MOCK_METHOD(binder::Status, scheduleComposite, (), (override));
+ MOCK_METHOD(binder::Status, scheduleCommit, (), (override));
MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
MOCK_METHOD(binder::Status, addWindowInfosListener,
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e89998f..ffb8622 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -989,6 +989,16 @@
return binder::Status::ok();
}
+ binder::Status enableRefreshRateOverlay(bool /*active*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status setDebugFlash(int /*delay*/) override { return binder::Status::ok(); }
+
+ binder::Status scheduleComposite() override { return binder::Status::ok(); }
+
+ binder::Status scheduleCommit() override { return binder::Status::ok(); }
+
binder::Status getGpuContextPriority(int32_t* /*outPriority*/) override {
return binder::Status::ok();
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1ef381c..25124c2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -6541,21 +6541,14 @@
case 1001:
return NAME_NOT_FOUND;
case 1002: // Toggle flashing on surface damage.
- if (const int delay = data.readInt32(); delay > 0) {
- mDebugFlashDelay = delay;
- } else {
- mDebugFlashDelay = mDebugFlashDelay ? 0 : 1;
- }
- scheduleRepaint();
+ sfdo_setDebugFlash(data.readInt32());
return NO_ERROR;
case 1004: // Force composite ahead of next VSYNC.
case 1006:
- scheduleComposite(FrameHint::kActive);
+ sfdo_scheduleComposite();
return NO_ERROR;
case 1005: { // Force commit ahead of next VSYNC.
- Mutex::Autolock lock(mStateLock);
- setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded |
- eTraversalNeeded);
+ sfdo_scheduleCommit();
return NO_ERROR;
}
case 1007: // Unused.
@@ -6800,19 +6793,13 @@
return NO_ERROR;
}
case 1034: {
- auto future = mScheduler->schedule(
- [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
- switch (n = data.readInt32()) {
- case 0:
- case 1:
- enableRefreshRateOverlay(static_cast<bool>(n));
- break;
- default:
- reply->writeBool(isRefreshRateOverlayEnabled());
- }
- });
-
- future.wait();
+ n = data.readInt32();
+ if (n == 0 || n == 1) {
+ sfdo_enableRefreshRateOverlay(static_cast<bool>(n));
+ } else {
+ Mutex::Autolock lock(mStateLock);
+ reply->writeBool(isRefreshRateOverlayEnabled());
+ }
return NO_ERROR;
}
case 1035: {
@@ -8740,6 +8727,33 @@
std::move(hwcDump), &displays);
}
+// sfdo functions
+
+void SurfaceFlinger::sfdo_enableRefreshRateOverlay(bool active) {
+ auto future = mScheduler->schedule(
+ [&]() FTL_FAKE_GUARD(mStateLock)
+ FTL_FAKE_GUARD(kMainThreadContext) { enableRefreshRateOverlay(active); });
+ future.wait();
+}
+
+void SurfaceFlinger::sfdo_setDebugFlash(int delay) {
+ if (delay > 0) {
+ mDebugFlashDelay = delay;
+ } else {
+ mDebugFlashDelay = mDebugFlashDelay ? 0 : 1;
+ }
+ scheduleRepaint();
+}
+
+void SurfaceFlinger::sfdo_scheduleComposite() {
+ scheduleComposite(SurfaceFlinger::FrameHint::kActive);
+}
+
+void SurfaceFlinger::sfdo_scheduleCommit() {
+ Mutex::Autolock lock(mStateLock);
+ setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded | eTraversalNeeded);
+}
+
// gui::ISurfaceComposer
binder::Status SurfaceComposerAIDL::bootFinished() {
@@ -9433,6 +9447,26 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::enableRefreshRateOverlay(bool active) {
+ mFlinger->sfdo_enableRefreshRateOverlay(active);
+ return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::setDebugFlash(int delay) {
+ mFlinger->sfdo_setDebugFlash(delay);
+ return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::scheduleComposite() {
+ mFlinger->sfdo_scheduleComposite();
+ return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::scheduleCommit() {
+ mFlinger->sfdo_scheduleCommit();
+ return binder::Status::ok();
+}
+
binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) {
*outPriority = mFlinger->getGpuContextPriority();
return binder::Status::ok();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4d17fa7..3ac2335 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1442,6 +1442,11 @@
// Mirroring
// Map of displayid to mirrorRoot
ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug;
+
+ void sfdo_enableRefreshRateOverlay(bool active);
+ void sfdo_setDebugFlash(int delay);
+ void sfdo_scheduleComposite();
+ void sfdo_scheduleCommit();
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1548,6 +1553,10 @@
const sp<IBinder>& displayToken,
std::optional<gui::DisplayDecorationSupport>* outSupport) override;
binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
+ binder::Status enableRefreshRateOverlay(bool active) override;
+ binder::Status setDebugFlash(int delay) override;
+ binder::Status scheduleComposite() override;
+ binder::Status scheduleCommit() override;
binder::Status getGpuContextPriority(int32_t* outPriority) override;
binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
binder::Status addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener,